Adds a new 'Force ∥ to Face' toggle alongside the existing 'Force ⊥ to
Face'. The constraint direction is cross(face_normal, camera_dir), keeping
dimension vertices running along the face surface rather than into it.
Enabling one constraint automatically disables the other (mutual exclusion
via a re-entrant guard in the prop callbacks).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New modal operator (bim.set_dimension_anchor) anchors dimension vertices
to IFC element faces. Anchors are stored as JSON in a BBIM_DimensionTarget
pset on the IfcAnnotation and resolved via tessellation at regeneration time.
- resolve_anchor.py / regenerate_dimension.py: new ifcopenshell API modules
- bim.set_dimension_anchor: 2-phase Object Mode modal (pick vertex → pick face)
- bim.regenerate_dimensions: recomputes all parametric dimensions
- Auto-regeneration via depsgraph_update_post when referenced elements move
- placement_override reads Blender matrix_world for G-moved elements
- Plan-view annotations flattened to annotation plane (Z=0 in local space)
- IfcIndexedPolyCurve.Segments rebuilt to handle n-point chains correctly
- ClickNearestDimensionAnchor: scan all selected objects instead of active
object so clicking a green dot doesn't lose to the underlying IFC geometry
- GizmoAnchorHandle: remove draw_select entirely (any entry in the select
buffer causes Blender's gizmo system to consume clicks); keep purely visual
- Scale anchor dots to scale_basis = 0.2
- Fix ReferenceError in decoration.py draw loop after undo by catching
ReferenceError and resetting DecoratorData.is_loaded
- SetDimensionAnchor: inherit tool.Ifc.Operator so IFC pset writes are
tracked for undo; finish the modal after each face write so each anchor
gets its own undo step
- Fix ReferenceError in _modal after undo when annotation RNA is freed
- handler.py: add regenerate_dims_for_layer; call it from
EditMaterialSetItem._execute so dimensions update when layer thickness changes
- regenerate_dimension.py: fix ForcePerpendicularToFace for LAYER_BOUNDARY
anchors by deriving the thickness-axis normal from LayerSetDirection
(AXIS2→Y, AXIS1→X, AXIS3→Z) instead of requiring a stored normal_local
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BBIM_Dimension.LinePosition (IfcLengthMeasure): holds the dimension line at
a fixed global coordinate along cross(world_Z, dim_direction), independent of
geometry movement
- regenerate_dimension applies LinePosition only when ForcePerpendicularToFace is
also set (the two are semantically coupled); anchor["pt"] always stores the true
surface hit so the measured length is unaffected
- BIMAnnotationProperties.line_position uses get/set callbacks instead of an update
callback to avoid the 'Writing to ID classes in this context is not allowed' error
that fires when Blender draws the tool header
- DimensionLinePositionWidget (BIM_GGT_dimension_line_position): gizmo group with
two opposing GizmoCone handles at the curve midpoint; poll requires
ForcePerpendicularToFace so the handles only appear when the feature is active
- UI: line_position field and gizmo are hidden when ForcePerpendicularToFace is off
- Psets_BBIM_Annotation.ifc: #40 LinePosition template added to BBIM_Dimension
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SetDimensionAnchor — hover-select-then-confirm:
- Cursor highlights candidate IFC elements (orange Blender selection outline)
before committing; Tab cycles through overlapping/coplanar candidates
- _compute_candidates: ray-cast all IFC mesh objects; falls back to 2D
bounding-box proximity (5 cm tolerance) for plan-view picks where the
ray misses the mesh by sub-mm amounts
- _write_anchor: after anchoring a face, immediately calls
regenerate_dimension with placement_override (Blender matrix_world)
and _update_blender_curve so the curve vertex moves to the resolved point
DrawParametricDimension — ForcePerpendicularToFace live snap constraint:
- Reads force_perpendicular_to_face toggle from annotation props on invoke
- After anchor[0] is placed on a FACE, _update_perp_constraint extracts
the face normal and stores it as the constraint axis
- _apply_perp_constraint runs every modal tick after handle_snap_selection,
projecting the current snap point onto pt[0] + t*normal
- On finalize, _create_dimension_from_polyline writes ForcePerpendicularToFace
to the BBIM_Dimension pset and calls regenerate_dimension to snap the
stored curve to the constraint before the operator exits
regenerate_dimension.py:
- ForcePerpendicularToFace block: after resolving all anchors, projects
vertices 1…n onto the line through pt[0] along anchor[0]'s face normal
- _get_anchor_face_normal_world: reads normal_local from anchor fingerprint,
calls _rotate_local_to_world with placement_override; falls back to stored
world-space normal
resolve_anchor.py:
- _rotate_local_to_world: transforms an element-local direction vector to
world space using the element's placement or placement_override matrix
pset/operator.py:
- EditPset._execute: after editing a BBIM_Dimension pset on an IfcAnnotation,
auto-calls regenerate_dimension + _update_blender_curve so changes to
anchors/ForcePerpendicularToFace are reflected immediately in the viewport
prop.py / workspace.py:
- Added force_perpendicular_to_face BoolProperty to BIMAnnotationProperties
- UI toggle shown in annotation tool header for DIMENSION/RADIUS/DIAMETER/
ANGLE/PLAN_LEVEL/SECTION_LEVEL types
Psets_BBIM_Annotation.ifc:
- Added ForcePerpendicularToFace property template (#39) to BBIM_Dimension
- Extended BBIM_Dimension applicability to ANGLE, PLAN_LEVEL, SECTION_LEVEL
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New modal operator (bim.set_dimension_anchor) anchors dimension vertices
to IFC element faces. Anchors are stored as JSON in a BBIM_DimensionTarget
pset on the IfcAnnotation and resolved via tessellation at regeneration time.
- resolve_anchor.py / regenerate_dimension.py: new ifcopenshell API modules
- bim.set_dimension_anchor: 2-phase Object Mode modal (pick vertex → pick face)
- bim.regenerate_dimensions: recomputes all parametric dimensions
- Auto-regeneration via depsgraph_update_post when referenced elements move
- placement_override reads Blender matrix_world for G-moved elements
- Plan-view annotations flattened to annotation plane (Z=0 in local space)
- IfcIndexedPolyCurve.Segments rebuilt to handle n-point chains correctly