mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-05 23:41:44 +00:00
Make LinePosition camera-relative in section/elevation views
_get_line_offset_direction now uses cross(camera_dir, dim_dir) instead of cross(world_Z, dim_dir) when the camera is mostly horizontal (section or elevation view). This keeps the offset axis in the view plane so the DimensionLinePositionWidget gizmo drags the line visually up/down rather than in/out of the screen. Plan view behaviour (cross(world_Z, dim_dir)) is preserved unchanged so existing stored LinePosition values continue to work. camera_dir is threaded through regenerate_dimension(), _get_line_offset_direction(), and all callers: gizmos.py (_set_pos / _offset_dir), prop.py (_get/_set_line_position), handler.py (regenerate_dims_for_layer and depsgraph handler), and all three operator.py call sites (DrawParametricDimension, _do_write_anchor, RegenerateDimensions). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2818,8 +2818,23 @@ class DimensionLinePositionWidget(types.GizmoGroup):
|
||||
# Helpers
|
||||
|
||||
@staticmethod
|
||||
def _offset_dir(obj: bpy.types.Object) -> "Vector | None":
|
||||
"""World-space unit direction perpendicular to the dimension line and world_Z."""
|
||||
def _cam_dir() -> "Vector | None":
|
||||
"""Scene camera forward direction, or None."""
|
||||
cam = bpy.context.scene.camera
|
||||
if not cam:
|
||||
return None
|
||||
return (cam.matrix_world.to_3x3() @ Vector((0.0, 0.0, -1.0))).normalized()
|
||||
|
||||
@classmethod
|
||||
def _offset_dir(cls, obj: bpy.types.Object) -> "Vector | None":
|
||||
"""World-space direction perpendicular to the dimension line and in the view plane.
|
||||
|
||||
Plan view (camera mostly vertical): cross(world_Z, dim_dir) — preserves
|
||||
existing stored LinePosition values.
|
||||
Section/elevation (camera mostly horizontal): cross(cam_forward, dim_dir) —
|
||||
keeps the offset axis inside the view plane so the gizmo moves the line
|
||||
visually sideways (up/down in section) rather than into/out of the screen.
|
||||
"""
|
||||
if not obj.data or not hasattr(obj.data, "splines") or not obj.data.splines:
|
||||
return None
|
||||
spline = obj.data.splines[0]
|
||||
@@ -2831,8 +2846,10 @@ class DimensionLinePositionWidget(types.GizmoGroup):
|
||||
if dim.length < 1e-10:
|
||||
return None
|
||||
dim.normalize()
|
||||
world_z = Vector((0.0, 0.0, 1.0))
|
||||
od = world_z.cross(dim)
|
||||
cam_view = cls._cam_dir()
|
||||
cam_is_plan = (cam_view is None) or abs(cam_view.z) > 0.7
|
||||
ref = Vector((0.0, 0.0, 1.0)) if cam_is_plan else cam_view
|
||||
od = ref.cross(dim)
|
||||
if od.length < 1e-6:
|
||||
od = Vector((1.0, 0.0, 0.0)).cross(dim)
|
||||
if od.length < 1e-6:
|
||||
@@ -2920,7 +2937,11 @@ class DimensionLinePositionWidget(types.GizmoGroup):
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
resolved_pts = drawing_api.regenerate_dimension(file, element, placement_override=placement_override)
|
||||
cam_view = self._cam_dir()
|
||||
cam_dir_tuple = tuple(cam_view) if cam_view is not None else None
|
||||
resolved_pts = drawing_api.regenerate_dimension(
|
||||
file, element, placement_override=placement_override, camera_dir=cam_dir_tuple
|
||||
)
|
||||
if resolved_pts:
|
||||
_update_blender_curve(element, resolved_pts)
|
||||
tool.Blender.update_viewport()
|
||||
|
||||
@@ -113,6 +113,12 @@ def regenerate_dims_for_layer(file, layer) -> None:
|
||||
geom_settings = ifcopenshell.geom.settings()
|
||||
geom_settings.set("APPLY_DEFAULT_MATERIALS", False)
|
||||
|
||||
cam = bpy.context.scene.camera
|
||||
cam_dir_tuple = None
|
||||
if cam:
|
||||
from mathutils import Vector as _Vec
|
||||
cam_dir_tuple = tuple((cam.matrix_world.to_3x3() @ _Vec((0, 0, -1))).normalized())
|
||||
|
||||
for ann_id in annotation_ids:
|
||||
try:
|
||||
annotation = file.by_id(ann_id)
|
||||
@@ -143,6 +149,7 @@ def regenerate_dims_for_layer(file, layer) -> None:
|
||||
settings=geom_settings,
|
||||
shape_cache=_dim_shape_cache,
|
||||
placement_override=placement_override,
|
||||
camera_dir=cam_dir_tuple,
|
||||
)
|
||||
if resolved_pts:
|
||||
_update_blender_curve(annotation, resolved_pts)
|
||||
|
||||
@@ -6397,10 +6397,15 @@ class DrawParametricDimension(bpy.types.Operator, PolylineOperator, tool.Ifc.Ope
|
||||
placement_override[elem.id()] = np.array(elem_obj.matrix_world)
|
||||
except Exception:
|
||||
pass
|
||||
_cam = bpy.context.scene.camera
|
||||
_cam_dir_tuple = None
|
||||
if _cam:
|
||||
_cam_dir_tuple = tuple((bpy.context.scene.camera.matrix_world.to_3x3() @ Vector((0, 0, -1))).normalized())
|
||||
resolved_pts = drawing_api.regenerate_dimension(
|
||||
file, annotation,
|
||||
shape_cache=getattr(self, "_shape_cache", None),
|
||||
placement_override=placement_override,
|
||||
camera_dir=_cam_dir_tuple,
|
||||
)
|
||||
if resolved_pts:
|
||||
_update_blender_curve(annotation, resolved_pts)
|
||||
@@ -7669,12 +7674,18 @@ def _do_write_anchor(annotation, annotation_obj, new_anchor: dict, vertex_index:
|
||||
if _is_elevation:
|
||||
_update_elevation_marker_z(file, annotation, shape_cache=shape_cache, placement_override=placement_override)
|
||||
else:
|
||||
import bpy as _bpy
|
||||
import ifcopenshell.api.drawing as drawing_api
|
||||
_cam = _bpy.context.scene.camera
|
||||
_cam_dir_tuple = None
|
||||
if _cam:
|
||||
_cam_dir_tuple = tuple((_cam.matrix_world.to_3x3() @ Vector((0, 0, -1))).normalized())
|
||||
resolved_pts = drawing_api.regenerate_dimension(
|
||||
file,
|
||||
annotation,
|
||||
shape_cache=shape_cache,
|
||||
placement_override=placement_override,
|
||||
camera_dir=_cam_dir_tuple,
|
||||
)
|
||||
if resolved_pts:
|
||||
_update_blender_curve(annotation, resolved_pts)
|
||||
@@ -7944,6 +7955,11 @@ class RegenerateDimensions(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
from bonsai.bim.module.drawing.handler import _sync_dimension_anchors_to_curve
|
||||
|
||||
_cam = context.scene.camera
|
||||
_cam_dir_tuple = None
|
||||
if _cam:
|
||||
_cam_dir_tuple = tuple((_cam.matrix_world.to_3x3() @ Vector((0, 0, -1))).normalized())
|
||||
|
||||
updated = 0
|
||||
for annotation in candidates:
|
||||
pset = ifcopenshell.util.element.get_pset(annotation, "BBIM_Dimension")
|
||||
@@ -7998,6 +8014,7 @@ class RegenerateDimensions(bpy.types.Operator, tool.Ifc.Operator):
|
||||
settings=geom_settings,
|
||||
shape_cache=shape_cache,
|
||||
placement_override=placement_override,
|
||||
camera_dir=_cam_dir_tuple,
|
||||
)
|
||||
if not resolved_pts:
|
||||
continue
|
||||
|
||||
@@ -1135,13 +1135,27 @@ def _get_line_position(self) -> float:
|
||||
m = math.sqrt(dx * dx + dy * dy + dz * dz)
|
||||
if m > 1e-10:
|
||||
ddx, ddy, ddz = dx / m, dy / m, dz / m
|
||||
# cross(world_Z=(0,0,1), dim_dir) = (-ddy, ddx, 0)
|
||||
cam = _bpy.context.scene.camera
|
||||
cam_is_plan = True
|
||||
cvx, cvy, cvz = 0.0, 0.0, 1.0
|
||||
if cam:
|
||||
from mathutils import Vector as _Vec
|
||||
cv = (cam.matrix_world.to_3x3() @ _Vec((0, 0, -1))).normalized()
|
||||
cvx, cvy, cvz = cv.x, cv.y, cv.z
|
||||
cam_is_plan = abs(cvz) > 0.7
|
||||
if cam_is_plan:
|
||||
# cross(world_Z, dim_dir)
|
||||
ox, oy, oz = -ddy, ddx, 0.0
|
||||
om = math.sqrt(ox * ox + oy * oy)
|
||||
else:
|
||||
# cross(cam_dir, dim_dir)
|
||||
ox = cvy * ddz - cvz * ddy
|
||||
oy = cvz * ddx - cvx * ddz
|
||||
oz = cvx * ddy - cvy * ddx
|
||||
om = math.sqrt(ox * ox + oy * oy + oz * oz)
|
||||
if om > 1e-6:
|
||||
od = (ox / om, oy / om, 0.0)
|
||||
od = (ox / om, oy / om, oz / om)
|
||||
pt = anchors[0]["pt"]
|
||||
return float(pt[0] * od[0] + pt[1] * od[1])
|
||||
return float(pt[0] * od[0] + pt[1] * od[1] + pt[2] * od[2])
|
||||
except Exception:
|
||||
pass
|
||||
return 0.0
|
||||
@@ -1180,6 +1194,12 @@ def _set_line_position(self, value: float) -> None:
|
||||
|
||||
from bonsai.bim.module.drawing.operator import _update_blender_curve
|
||||
|
||||
cam = _bpy.context.scene.camera
|
||||
cam_dir_tuple = None
|
||||
if cam:
|
||||
from mathutils import Vector as _Vec
|
||||
cam_dir_tuple = tuple((cam.matrix_world.to_3x3() @ _Vec((0, 0, -1))).normalized())
|
||||
|
||||
for obj, element, pset_data in targets:
|
||||
pset_entity = file.by_id(pset_data["id"])
|
||||
ifcopenshell.api.pset.edit_pset(file, pset=pset_entity, properties={"LinePosition": value})
|
||||
@@ -1198,7 +1218,9 @@ def _set_line_position(self, value: float) -> None:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
resolved_pts = drawing_api.regenerate_dimension(file, element, placement_override=placement_override)
|
||||
resolved_pts = drawing_api.regenerate_dimension(
|
||||
file, element, placement_override=placement_override, camera_dir=cam_dir_tuple
|
||||
)
|
||||
if resolved_pts:
|
||||
_update_blender_curve(element, resolved_pts)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user