2 Commits

Author SHA1 Message Date
forbes
ef16ecbaa2 fix: viewport background gradient — Overlay0/Mantle 3-color gradient (#188)
Change viewport background from 2-color Base→Crust gradient to
3-color Overlay0→Mantle→Overlay0 gradient for better visual depth.
2026-02-14 13:03:37 -06:00
forbes
2bf969c62a fix: use TangentPlane MapMode for tangent-to-cylinder datums (#58)
plane_tangent_to_cylinder() now derives a vertex from the cylinder
face's edges and uses TangentPlane MapMode with AttachmentOffset to
encode the angular position. This makes tangent datums parametric —
they auto-update when cylinder geometry changes.

Add _find_cylinder_vertex() and _vertex_angle_on_cylinder() helpers.
Store vertex_angle in ZTools_Params for the edit panel to compute
AttachmentOffset updates. Falls back to manual placement when no
vertex can be resolved (non-Body datums).
2026-02-08 18:53:13 -06:00
3 changed files with 104 additions and 32 deletions

View File

@@ -27,13 +27,13 @@
<FCUInt Name="colorError" Value="4086016255"/> <FCUInt Name="colorError" Value="4086016255"/>
</FCParamGroup> </FCParamGroup>
<FCParamGroup Name="View"> <FCParamGroup Name="View">
<FCUInt Name="BackgroundColor" Value="505294591"/> <FCUInt Name="BackgroundColor" Value="404235775"/>
<FCUInt Name="BackgroundColor2" Value="286333951"/> <FCUInt Name="BackgroundColor2" Value="1819313919"/>
<FCUInt Name="BackgroundColor3" Value="404235775"/> <FCUInt Name="BackgroundColor3" Value="1819313919"/>
<FCUInt Name="BackgroundColor4" Value="825378047"/> <FCUInt Name="BackgroundColor4" Value="404235775"/>
<FCBool Name="Simple" Value="0"/> <FCBool Name="Simple" Value="0"/>
<FCBool Name="Gradient" Value="1"/> <FCBool Name="Gradient" Value="1"/>
<FCBool Name="UseBackgroundColorMid" Value="0"/> <FCBool Name="UseBackgroundColorMid" Value="1"/>
<FCUInt Name="HighlightColor" Value="3416717311"/> <FCUInt Name="HighlightColor" Value="3416717311"/>
<FCUInt Name="SelectionColor" Value="3032415999"/> <FCUInt Name="SelectionColor" Value="3032415999"/>
<FCUInt Name="PreselectColor" Value="2497893887"/> <FCUInt Name="PreselectColor" Value="2497893887"/>

View File

@@ -402,24 +402,36 @@ class DatumEditTaskPanel:
elif ztools_type == "tangent_cylinder": elif ztools_type == "tangent_cylinder":
angle = self.angle_spin.value() angle = self.angle_spin.value()
refs = _resolve_source_refs(self.datum_obj) if self._has_attachment():
if refs and refs[0][2]: params_json = getattr(self.datum_obj, "ZTools_Params", "{}")
face = refs[0][2] try:
if isinstance(face.Surface, Part.Cylinder): params = json.loads(params_json)
cyl = face.Surface except json.JSONDecodeError:
axis = cyl.Axis params = {}
center = cyl.Center vertex_angle = params.get("vertex_angle", 0.0)
radius = cyl.Radius offset_rot = App.Rotation(App.Vector(0, 0, 1), angle - vertex_angle)
rad = math.radians(angle) self.datum_obj.AttachmentOffset = App.Placement(
if abs(axis.dot(App.Vector(1, 0, 0))) < 0.99: App.Vector(0, 0, 0), offset_rot
local_x = axis.cross(App.Vector(1, 0, 0)).normalize() )
else: else:
local_x = axis.cross(App.Vector(0, 1, 0)).normalize() refs = _resolve_source_refs(self.datum_obj)
local_y = axis.cross(local_x) if refs and refs[0][2]:
radial = local_x * math.cos(rad) + local_y * math.sin(rad) face = refs[0][2]
tangent_point = center + radial * radius if isinstance(face.Surface, Part.Cylinder):
rot = App.Rotation(App.Vector(0, 0, 1), radial) cyl = face.Surface
self.datum_obj.Placement = App.Placement(tangent_point, rot) 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}) self._update_params({"angle": angle})
elif ztools_type in ("normal_to_edge", "on_edge"): elif ztools_type in ("normal_to_edge", "on_edge"):

View File

@@ -903,6 +903,41 @@ def plane_angled(
return plane 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( def plane_tangent_to_cylinder(
face: Part.Face, face: Part.Face,
angle: float = 0, angle: float = 0,
@@ -966,16 +1001,41 @@ def plane_tangent_to_cylinder(
if body: if body:
plane = body.newObject("PartDesign::Plane", name) plane = body.newObject("PartDesign::Plane", name)
# TangentPlane mode needs (face, vertex). Without a vertex reference, # TangentPlane MapMode needs (face, vertex). Derive a vertex from
# fall back to manual placement. # the cylinder face's edges and encode the angular offset.
_setup_ztools_datum( vertex_sub = (
plane, _find_cylinder_vertex(source_object, source_subname)
placement, if source_object and source_subname
"tangent_cylinder", else None
{"angle": angle, "radius": radius},
source_refs,
is_plane=True,
) )
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: else:
plane = doc.addObject("Part::Plane", name) plane = doc.addObject("Part::Plane", name)
plane.Length = 50 plane.Length = 50