Compare commits
3 Commits
fix/menu-i
...
fix/viewpo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef16ecbaa2 | ||
|
|
2bf969c62a | ||
|
|
04f9df75cb |
@@ -27,13 +27,13 @@
|
||||
<FCUInt Name="colorError" Value="4086016255"/>
|
||||
</FCParamGroup>
|
||||
<FCParamGroup Name="View">
|
||||
<FCUInt Name="BackgroundColor" Value="505294591"/>
|
||||
<FCUInt Name="BackgroundColor2" Value="286333951"/>
|
||||
<FCUInt Name="BackgroundColor3" Value="404235775"/>
|
||||
<FCUInt Name="BackgroundColor4" Value="825378047"/>
|
||||
<FCUInt Name="BackgroundColor" Value="404235775"/>
|
||||
<FCUInt Name="BackgroundColor2" Value="1819313919"/>
|
||||
<FCUInt Name="BackgroundColor3" Value="1819313919"/>
|
||||
<FCUInt Name="BackgroundColor4" Value="404235775"/>
|
||||
<FCBool Name="Simple" Value="0"/>
|
||||
<FCBool Name="Gradient" Value="1"/>
|
||||
<FCBool Name="UseBackgroundColorMid" Value="0"/>
|
||||
<FCBool Name="UseBackgroundColorMid" Value="1"/>
|
||||
<FCUInt Name="HighlightColor" Value="3416717311"/>
|
||||
<FCUInt Name="SelectionColor" Value="3032415999"/>
|
||||
<FCUInt Name="PreselectColor" Value="2497893887"/>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Custom ViewProvider for ZTools datum objects
|
||||
|
||||
import json
|
||||
import math
|
||||
|
||||
import FreeCAD as App
|
||||
import FreeCADGui as Gui
|
||||
@@ -141,6 +142,23 @@ class ZToolsDatumViewProvider:
|
||||
return None
|
||||
|
||||
|
||||
def _resolve_source_refs(datum_obj):
|
||||
"""Parse ZTools_SourceRefs and resolve to (object, subname, shape) tuples."""
|
||||
refs_json = getattr(datum_obj, "ZTools_SourceRefs", "[]")
|
||||
try:
|
||||
refs = json.loads(refs_json)
|
||||
except json.JSONDecodeError:
|
||||
return []
|
||||
doc = datum_obj.Document
|
||||
resolved = []
|
||||
for ref in refs:
|
||||
obj = doc.getObject(ref.get("object", ""))
|
||||
sub = ref.get("subname", "")
|
||||
shape = obj.getSubObject(sub) if obj and sub else None
|
||||
resolved.append((obj, sub, shape))
|
||||
return resolved
|
||||
|
||||
|
||||
class DatumEditTaskPanel:
|
||||
"""
|
||||
Task panel for editing existing ZTools datum objects.
|
||||
@@ -364,8 +382,57 @@ class DatumEditTaskPanel:
|
||||
self.datum_obj.AttachmentOffset = new_offset
|
||||
self._update_params({"distance": distance})
|
||||
|
||||
elif ztools_type in ("angled", "tangent_cylinder"):
|
||||
self._update_params({"angle": self.angle_spin.value()})
|
||||
elif ztools_type == "angled":
|
||||
angle = self.angle_spin.value()
|
||||
if self._has_attachment():
|
||||
refs = _resolve_source_refs(self.datum_obj)
|
||||
if len(refs) >= 2 and refs[0][2] and refs[1][2]:
|
||||
face_normal = refs[0][2].normalAt(0, 0)
|
||||
edge_shape = refs[1][2]
|
||||
edge_dir = (
|
||||
edge_shape.Vertexes[-1].Point - edge_shape.Vertexes[0].Point
|
||||
).normalize()
|
||||
face_rot = App.Rotation(App.Vector(0, 0, 1), face_normal)
|
||||
local_edge_dir = face_rot.inverted().multVec(edge_dir)
|
||||
angle_rot = App.Rotation(local_edge_dir, angle)
|
||||
self.datum_obj.AttachmentOffset = App.Placement(
|
||||
App.Vector(0, 0, 0), angle_rot
|
||||
)
|
||||
self._update_params({"angle": angle})
|
||||
|
||||
elif ztools_type == "tangent_cylinder":
|
||||
angle = self.angle_spin.value()
|
||||
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"):
|
||||
parameter = self.param_spin.value()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user