diff --git a/ztools/ztools/commands/datum_viewprovider.py b/ztools/ztools/commands/datum_viewprovider.py index 39036fa..2959e93 100644 --- a/ztools/ztools/commands/datum_viewprovider.py +++ b/ztools/ztools/commands/datum_viewprovider.py @@ -402,24 +402,36 @@ class DatumEditTaskPanel: elif ztools_type == "tangent_cylinder": angle = self.angle_spin.value() - refs = _resolve_source_refs(self.datum_obj) - if refs and refs[0][2]: - face = refs[0][2] - if isinstance(face.Surface, Part.Cylinder): - cyl = face.Surface - axis = cyl.Axis - center = cyl.Center - radius = cyl.Radius - rad = math.radians(angle) - if abs(axis.dot(App.Vector(1, 0, 0))) < 0.99: - local_x = axis.cross(App.Vector(1, 0, 0)).normalize() - else: - local_x = axis.cross(App.Vector(0, 1, 0)).normalize() - local_y = axis.cross(local_x) - radial = local_x * math.cos(rad) + local_y * math.sin(rad) - tangent_point = center + radial * radius - rot = App.Rotation(App.Vector(0, 0, 1), radial) - self.datum_obj.Placement = App.Placement(tangent_point, rot) + if self._has_attachment(): + params_json = getattr(self.datum_obj, "ZTools_Params", "{}") + try: + params = json.loads(params_json) + except json.JSONDecodeError: + params = {} + vertex_angle = params.get("vertex_angle", 0.0) + offset_rot = App.Rotation(App.Vector(0, 0, 1), angle - vertex_angle) + self.datum_obj.AttachmentOffset = App.Placement( + App.Vector(0, 0, 0), offset_rot + ) + else: + refs = _resolve_source_refs(self.datum_obj) + if refs and refs[0][2]: + face = refs[0][2] + if isinstance(face.Surface, Part.Cylinder): + cyl = face.Surface + axis = cyl.Axis + center = cyl.Center + radius = cyl.Radius + rad = math.radians(angle) + if abs(axis.dot(App.Vector(1, 0, 0))) < 0.99: + local_x = axis.cross(App.Vector(1, 0, 0)).normalize() + else: + local_x = axis.cross(App.Vector(0, 1, 0)).normalize() + local_y = axis.cross(local_x) + radial = local_x * math.cos(rad) + local_y * math.sin(rad) + tangent_point = center + radial * radius + rot = App.Rotation(App.Vector(0, 0, 1), radial) + self.datum_obj.Placement = App.Placement(tangent_point, rot) self._update_params({"angle": angle}) elif ztools_type in ("normal_to_edge", "on_edge"): diff --git a/ztools/ztools/datums/core.py b/ztools/ztools/datums/core.py index 2b754ef..e1b3d1e 100644 --- a/ztools/ztools/datums/core.py +++ b/ztools/ztools/datums/core.py @@ -903,6 +903,41 @@ def plane_angled( return plane +def _find_cylinder_vertex(obj, face_subname): + """Find a vertex subname from a cylindrical face's edges.""" + face = obj.getSubObject(face_subname) + if not face or not face.Edges: + return None + edge = face.Edges[0] + if not edge.Vertexes: + return None + vertex_point = edge.Vertexes[0].Point + for i, v in enumerate(obj.Shape.Vertexes, 1): + if v.Point.isEqual(vertex_point, 1e-6): + return f"Vertex{i}" + return None + + +def _vertex_angle_on_cylinder(obj, vertex_sub, cylinder): + """Compute the angular position of a vertex on a cylinder surface.""" + vertex = obj.getSubObject(vertex_sub) + if not vertex: + return 0.0 + point = vertex.Point + relative = point - cylinder.Center + axis = cylinder.Axis + radial = relative - axis * relative.dot(axis) + if radial.Length < 1e-10: + return 0.0 + radial.normalize() + if abs(axis.dot(App.Vector(1, 0, 0))) < 0.99: + local_x = axis.cross(App.Vector(1, 0, 0)).normalize() + else: + local_x = axis.cross(App.Vector(0, 1, 0)).normalize() + local_y = axis.cross(local_x) + return math.degrees(math.atan2(radial.dot(local_y), radial.dot(local_x))) + + def plane_tangent_to_cylinder( face: Part.Face, angle: float = 0, @@ -966,16 +1001,41 @@ def plane_tangent_to_cylinder( if body: plane = body.newObject("PartDesign::Plane", name) - # TangentPlane mode needs (face, vertex). Without a vertex reference, - # fall back to manual placement. - _setup_ztools_datum( - plane, - placement, - "tangent_cylinder", - {"angle": angle, "radius": radius}, - source_refs, - is_plane=True, + # TangentPlane MapMode needs (face, vertex). Derive a vertex from + # the cylinder face's edges and encode the angular offset. + vertex_sub = ( + _find_cylinder_vertex(source_object, source_subname) + if source_object and source_subname + else None ) + if vertex_sub: + vertex_angle = _vertex_angle_on_cylinder(source_object, vertex_sub, cyl) + offset_angle = angle - vertex_angle + offset_rot = App.Rotation(App.Vector(0, 0, 1), offset_angle) + att_offset = App.Placement(App.Vector(0, 0, 0), offset_rot) + _setup_ztools_datum( + plane, + placement, + "tangent_cylinder", + {"angle": angle, "radius": radius, "vertex_angle": vertex_angle}, + source_refs, + is_plane=True, + map_mode="TangentPlane", + support=[ + (source_object, source_subname), + (source_object, vertex_sub), + ], + offset=att_offset, + ) + else: + _setup_ztools_datum( + plane, + placement, + "tangent_cylinder", + {"angle": angle, "radius": radius}, + source_refs, + is_plane=True, + ) else: plane = doc.addObject("Part::Plane", name) plane.Length = 50