mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-05 23:41:44 +00:00
Fix SetDimensionAnchor: use LOCAL_POINT anchor for tessellation VERTEX/EDGE snaps
When SetDimensionAnchor's tessellation fallback ran (element has no IfcExtrudedAreaSolid, so get_profile_snap_candidates returns empty), VERTEX and EDGE snaps created a static world anchor (free end) instead of a parametric one. The anchor had a position but no guid, so it never moved with the element. _compute_snap_geom now includes local_m (element-local Blender coordinates, metres) in the tessellation fallback return dict for both VERTEX and EDGE modes. _handle_face_pick uses build_anchor_from_local_point when local_m is present, storing a LOCAL_POINT anchor that resolves back to world space via the element placement — so the endpoint follows the element through moves and rotations.
This commit is contained in:
@@ -6884,12 +6884,15 @@ class SetDimensionAnchor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
elif snap_type == "EDGE" and snap.get("profile_x_m") is not None:
|
||||
anchor = drawing_api.build_anchor_from_profile_edge(file, element, snap)
|
||||
elif snap_type in ("VERTEX", "EDGE") and snap.get("snap_world") is not None:
|
||||
# Tessellation fallback — no IFC profile data available.
|
||||
# Use the snap position as a static WORLD anchor rather than a
|
||||
# FACE fingerprint, so the endpoint stays at the correct vertex/
|
||||
# edge position instead of drifting to the face centre.
|
||||
# Tessellation fallback — element has no IfcExtrudedAreaSolid profile.
|
||||
# Use element-local coordinates so the anchor follows the element
|
||||
# through moves and rotations (LOCAL_POINT method).
|
||||
sw = snap["snap_world"]
|
||||
anchor = drawing_api.make_world_anchor([float(sw[0]), float(sw[1]), float(sw[2])])
|
||||
local_m = snap.get("local_m")
|
||||
if local_m is not None:
|
||||
anchor = drawing_api.build_anchor_from_local_point(element, snap_type, sw, local_m)
|
||||
else:
|
||||
anchor = drawing_api.make_world_anchor([float(sw[0]), float(sw[1]), float(sw[2])])
|
||||
else:
|
||||
hit_m = (float(location.x), float(location.y), float(location.z))
|
||||
normal_m = (float(normal.x), float(normal.y), float(normal.z))
|
||||
@@ -7362,7 +7365,12 @@ class SetDimensionAnchor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
d2 = dx * dx + dy * dy
|
||||
if d2 < best_d2:
|
||||
best_d2, best_i = d2, i
|
||||
return {"type": "VERTEX", "snap_world": face_verts_world[best_i]}
|
||||
local_co = hit_obj.data.vertices[face.vertices[best_i]].co
|
||||
return {
|
||||
"type": "VERTEX",
|
||||
"snap_world": face_verts_world[best_i],
|
||||
"local_m": (local_co.x, local_co.y, local_co.z),
|
||||
}
|
||||
|
||||
if self._snap_mode == "EDGE":
|
||||
best_e, best_d2 = 0, float("inf")
|
||||
@@ -7379,7 +7387,15 @@ class SetDimensionAnchor(bpy.types.Operator, tool.Ifc.Operator):
|
||||
i0, i1 = best_e, (best_e + 1) % n
|
||||
v0_w, v1_w = face_verts_world[i0], face_verts_world[i1]
|
||||
mid_w = ((v0_w[0] + v1_w[0]) * 0.5, (v0_w[1] + v1_w[1]) * 0.5, (v0_w[2] + v1_w[2]) * 0.5)
|
||||
return {"type": "EDGE", "v0": v0_w, "v1": v1_w, "snap_world": mid_w}
|
||||
v0_l = hit_obj.data.vertices[face.vertices[i0]].co
|
||||
v1_l = hit_obj.data.vertices[face.vertices[i1]].co
|
||||
mid_l = ((v0_l.x + v1_l.x) * 0.5, (v0_l.y + v1_l.y) * 0.5, (v0_l.z + v1_l.z) * 0.5)
|
||||
return {
|
||||
"type": "EDGE",
|
||||
"v0": v0_w, "v1": v1_w,
|
||||
"snap_world": mid_w,
|
||||
"local_m": mid_l,
|
||||
}
|
||||
|
||||
if self._snap_mode == "LAYER":
|
||||
element = tool.Ifc.get_entity(hit_obj)
|
||||
|
||||
Reference in New Issue
Block a user