Skip to content

Parametric dimensions#8083

Open
theoryshaw wants to merge 38 commits into
v0.8.0from
parametric_dimensions
Open

Parametric dimensions#8083
theoryshaw wants to merge 38 commits into
v0.8.0from
parametric_dimensions

Conversation

@theoryshaw

@theoryshaw theoryshaw commented May 19, 2026

Copy link
Copy Markdown
Member

addresses #1320

Still some things to iron out, but putting this out there to test on BonsaiPR.

20260519_0944.mp4

theoryshaw and others added 14 commits May 15, 2026 07:48
…BBIM_Dimension.SuppressZeroInches

Generated with the assistance of an AI coding tool.
Generated with the assistance of an AI coding tool.
…eometry

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
…imensions

Extends parametric dimension support with a modal polyline operator that uses
Bonsai's existing snap infrastructure (same as walls/slabs) for placing anchor
points.  Shift+A in the Annotation tool now routes dimension types through this
operator instead of the generic add_annotation path.

- DrawParametricDimension: PolylineOperator subclass; each confirmed snap point
  is converted to a BBIM_DimensionTarget anchor via _snap_to_anchor, which reads
  face_index from the snap dict for face hits and falls back to closest_point_on_mesh
  for vertex/edge hits
- handle_inserting_polyline override tracks anchor list in sync with polyline
  points (insert on count increase, pop on BACKSPACE)
- hotkey_S_A dispatches to bim.draw_parametric_dimension for DIMENSION/RADIUS/
  DIAMETER/ANGLE/PLAN_LEVEL/SECTION_LEVEL types; all other types keep existing path
- depsgraph_update_post_handler extended to also watch is_updated_geometry so
  dimensions auto-regenerate when a referenced mesh is edited in Edit Mode; the
  affected element's tessellation is evicted from _dim_shape_cache so resolve_anchor
  re-tessellates from the updated IFC representation on the next pass

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>
…cularToFace toggle updates selection

workspace.py:
- Move "Set Dimension Anchor" and "Regenerate" buttons from the properties
  panel (ui.py) into draw_edit_object_interface in the annotation tool,
  visible whenever a selected object is a dimension-type IfcAnnotation
- Change force_perpendicular_to_face from a push-button (toggle=True) to
  a standard checkbox for clearer on/off state

ui.py:
- Remove the "Parametric Dimension" section (now lives in the tool header)

prop.py:
- Add _update_force_perpendicular update callback: when the checkbox is
  toggled, iterates all selected dimension annotations, writes the new
  ForcePerpendicularToFace value to each BBIM_Dimension pset, and calls
  regenerate_dimension so the constraint is applied immediately

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pendicularToFace

- 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>
…tric dimensions

- GizmoAnchorHandle + DimensionAnchorWidget: colored dot gizmos at each
  dimension curve vertex (green=anchored, orange=free); color changes to
  blue while SetDimensionAnchor is in PICK_FACE mode for that vertex
- ClickNearestDimensionAnchor (LMB keymap): Python proximity operator that
  fires SetDimensionAnchor pre-targeted at the nearest anchor dot within
  120px, returning PASS_THROUGH for misses so normal viewport clicks are
  unaffected
- SetDimensionAnchor: added anchor_index prop to enter PICK_FACE directly;
  set_active_anchor called at all phase transitions (invoke, vertex-pick,
  face-pick, alt-click free, ESC/RMB) so gizmo color tracks state correctly
- handler._sync_dimension_anchors_to_curve: proximity-based anchor sync
  when curve vertex count changes in Edit Mode (subdivide / delete)
- depsgraph_update_post_handler: regenerates dimensions when referenced
  elements move; also handles annotation curve edits directly
- Remove standalone Set Anchor button from annotation tool UI (replaced by
  clicking a gizmo dot)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… layer anchors

- 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>
…for DrawParametricDimension

- DrawParametricDimension: TAB cycles snap mode FACE→LAYER→EDGE→VERTEX during
  placement; consumes both PRESS and RELEASE when not in input mode to avoid
  conflict with polyline Cycle Input
- DrawParametricDimension: IFC-native snap candidate overrides polyline cursor
  position in LAYER/EDGE/VERTEX modes; FACE mode shows polygon outline; reuses
  _snap_draw_data / _draw_snap_indicator_global infrastructure from SetDimensionAnchor
- DrawParametricDimension: LAYER_BOUNDARY support in _update_perp_constraint,
  deriving normal from LayerSetDirection (AXIS1/2/3)
- ClickNearestDimensionAnchor: scan all visible annotations instead of only
  selected ones — view3d.select deselects the dimension before this operator
  runs, so pre-selection check caused dots to never activate
- AnnotationTool keymap: bim.click_nearest_dimension_anchor placed before
  view3d.select so it fires first when the annotation tool is active

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove clear_snap_objs() from PolylineOperator.invoke — BVH cache now
  persists across invocations; per-entry staleness is checked in
  create_snap_obj via matrix_world equality + vertex count, eliminating
  the ~11 s full rebuild on every Shift+A press.
- Add _init_snapping_points() hook to PolylineOperator; DrawParametricDimension
  overrides it with a cheap plane-intersection placeholder, deferring full
  BVH detection to the first MOUSEMOVE.
- Cache matrix_world in SnapObj and replace O(N_vertices) validation loop
  with O(1) matrix equality + single sample vertex check, cutting per-call
  create_snap_obj cost from 22-600 ms to <0.2 ms on cache hits.
- Use scene-level BVH pierce-through in SetDimensionAnchor._compute_candidates
  instead of per-object ray_cast loop (O(log N) vs O(N_objects)).
- Guard PolylineDecorator snap_mouse_point access against empty collection
  to prevent IndexError before first MOUSEMOVE populates the property.
- Wrap closest_point_on_mesh in try/except RuntimeError in
  _update_snap_draw_data for annotation objects with no internal mesh data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nsion

Vertical faces perpendicular to the camera cannot be hit by raycast, so
the dimension snap tool missed them entirely. Fix by checking nearby
objects whose 3D bbox contains the floor hit point and running
mode-appropriate candidate lookup on each:

- FACE mode: _snap_on_coplanar_faces finds vertical mesh faces with a
  bottom edge at the hovered Z, projects the cursor onto the face plane,
  and returns a FACE candidate (blue outline + face snap point).
- LAYER mode: get_layer_snap_candidates now runs on nearby bbox objects
  the same way VERTEX/EDGE mode already did, using the shared
  _snap_cand_multi_cache (cleared on TAB mode switch).
theoryshaw added a commit that referenced this pull request May 21, 2026
Both PR #7798 (ManualDrawingReference) and PR #8083 (parametric_dimensions)
independently added properties after entry #33 in Psets_BBIM_Annotation.ifc
and new BoolProperty declarations after `type_name` in prop.py and workspace.py
UI rows at the same locations.

Resolution: keep #8083's IDs (#34-#40) unchanged, renumber #7798's
IsManualDrawingReference and IsDocumentReference entries to #41 and #42,
update EPset_Annotation reference list accordingly, keep both UI rows,
and combine hotkey_S_A to use #8083's parametric-dimension routing
with #7798's "INVOKE_DEFAULT" argument for add_annotation.
theoryshaw added a commit that referenced this pull request May 22, 2026
Both PR #7965 (inset_section_endpoints) and PR #8083 (parametric_dimensions)
independently added properties after entry #33 in Psets_BBIM_Annotation.ifc
and new BoolProperty declarations after `type_name` in prop.py and workspace.py
UI rows at the same locations.

Resolution: keep #8083's IDs (#34-#40) unchanged, renumber #7965's
IsManualDrawingReference and IsDocumentReference entries to #41 and #42,
update EPset_Annotation reference list accordingly, keep both UI rows,
and combine hotkey_S_A to use #8083's parametric-dimension routing
with #7965's "INVOKE_DEFAULT" argument for add_annotation.
theoryshaw and others added 13 commits June 7, 2026 17:52
…BBIM_Dimension.SuppressZeroInches

Generated with the assistance of an AI coding tool.
Generated with the assistance of an AI coding tool.
…eometry

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
…imensions

Extends parametric dimension support with a modal polyline operator that uses
Bonsai's existing snap infrastructure (same as walls/slabs) for placing anchor
points.  Shift+A in the Annotation tool now routes dimension types through this
operator instead of the generic add_annotation path.

- DrawParametricDimension: PolylineOperator subclass; each confirmed snap point
  is converted to a BBIM_DimensionTarget anchor via _snap_to_anchor, which reads
  face_index from the snap dict for face hits and falls back to closest_point_on_mesh
  for vertex/edge hits
- handle_inserting_polyline override tracks anchor list in sync with polyline
  points (insert on count increase, pop on BACKSPACE)
- hotkey_S_A dispatches to bim.draw_parametric_dimension for DIMENSION/RADIUS/
  DIAMETER/ANGLE/PLAN_LEVEL/SECTION_LEVEL types; all other types keep existing path
- depsgraph_update_post_handler extended to also watch is_updated_geometry so
  dimensions auto-regenerate when a referenced mesh is edited in Edit Mode; the
  affected element's tessellation is evicted from _dim_shape_cache so resolve_anchor
  re-tessellates from the updated IFC representation on the next pass

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>
…cularToFace toggle updates selection

workspace.py:
- Move "Set Dimension Anchor" and "Regenerate" buttons from the properties
  panel (ui.py) into draw_edit_object_interface in the annotation tool,
  visible whenever a selected object is a dimension-type IfcAnnotation
- Change force_perpendicular_to_face from a push-button (toggle=True) to
  a standard checkbox for clearer on/off state

ui.py:
- Remove the "Parametric Dimension" section (now lives in the tool header)

prop.py:
- Add _update_force_perpendicular update callback: when the checkbox is
  toggled, iterates all selected dimension annotations, writes the new
  ForcePerpendicularToFace value to each BBIM_Dimension pset, and calls
  regenerate_dimension so the constraint is applied immediately

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pendicularToFace

- 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>
…tric dimensions

- GizmoAnchorHandle + DimensionAnchorWidget: colored dot gizmos at each
  dimension curve vertex (green=anchored, orange=free); color changes to
  blue while SetDimensionAnchor is in PICK_FACE mode for that vertex
- ClickNearestDimensionAnchor (LMB keymap): Python proximity operator that
  fires SetDimensionAnchor pre-targeted at the nearest anchor dot within
  120px, returning PASS_THROUGH for misses so normal viewport clicks are
  unaffected
- SetDimensionAnchor: added anchor_index prop to enter PICK_FACE directly;
  set_active_anchor called at all phase transitions (invoke, vertex-pick,
  face-pick, alt-click free, ESC/RMB) so gizmo color tracks state correctly
- handler._sync_dimension_anchors_to_curve: proximity-based anchor sync
  when curve vertex count changes in Edit Mode (subdivide / delete)
- depsgraph_update_post_handler: regenerates dimensions when referenced
  elements move; also handles annotation curve edits directly
- Remove standalone Set Anchor button from annotation tool UI (replaced by
  clicking a gizmo dot)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… layer anchors

- 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>
…for DrawParametricDimension

- DrawParametricDimension: TAB cycles snap mode FACE→LAYER→EDGE→VERTEX during
  placement; consumes both PRESS and RELEASE when not in input mode to avoid
  conflict with polyline Cycle Input
- DrawParametricDimension: IFC-native snap candidate overrides polyline cursor
  position in LAYER/EDGE/VERTEX modes; FACE mode shows polygon outline; reuses
  _snap_draw_data / _draw_snap_indicator_global infrastructure from SetDimensionAnchor
- DrawParametricDimension: LAYER_BOUNDARY support in _update_perp_constraint,
  deriving normal from LayerSetDirection (AXIS1/2/3)
- ClickNearestDimensionAnchor: scan all visible annotations instead of only
  selected ones — view3d.select deselects the dimension before this operator
  runs, so pre-selection check caused dots to never activate
- AnnotationTool keymap: bim.click_nearest_dimension_anchor placed before
  view3d.select so it fires first when the annotation tool is active

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove clear_snap_objs() from PolylineOperator.invoke — BVH cache now
  persists across invocations; per-entry staleness is checked in
  create_snap_obj via matrix_world equality + vertex count, eliminating
  the ~11 s full rebuild on every Shift+A press.
- Add _init_snapping_points() hook to PolylineOperator; DrawParametricDimension
  overrides it with a cheap plane-intersection placeholder, deferring full
  BVH detection to the first MOUSEMOVE.
- Cache matrix_world in SnapObj and replace O(N_vertices) validation loop
  with O(1) matrix equality + single sample vertex check, cutting per-call
  create_snap_obj cost from 22-600 ms to <0.2 ms on cache hits.
- Use scene-level BVH pierce-through in SetDimensionAnchor._compute_candidates
  instead of per-object ray_cast loop (O(log N) vs O(N_objects)).
- Guard PolylineDecorator snap_mouse_point access against empty collection
  to prevent IndexError before first MOUSEMOVE populates the property.
- Wrap closest_point_on_mesh in try/except RuntimeError in
  _update_snap_draw_data for annotation objects with no internal mesh data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
theoryshaw and others added 3 commits June 7, 2026 17:53
…nsion

Vertical faces perpendicular to the camera cannot be hit by raycast, so
the dimension snap tool missed them entirely. Fix by checking nearby
objects whose 3D bbox contains the floor hit point and running
mode-appropriate candidate lookup on each:

- FACE mode: _snap_on_coplanar_faces finds vertical mesh faces with a
  bottom edge at the hovered Z, projects the cursor onto the face plane,
  and returns a FACE candidate (blue outline + face snap point).
- LAYER mode: get_layer_snap_candidates now runs on nearby bbox objects
  the same way VERTEX/EDGE mode already did, using the shared
  _snap_cand_multi_cache (cleared on TAB mode switch).
…rcePerpendicularToFace

- Switch FACE/LAYER mode nearby-object filtering from 3D bbox to 2D
  screen-space bbox (30px tolerance), fixing walls whose local Y extent
  doesn't contain the floor hit point (e.g. wall at Z=0 with mesh not
  quite reaching the floor level).

- Also run _snap_on_coplanar_faces on hit_obj itself so the blue
  outline and IFC snap fire even when the cursor lands exactly on
  the wall/floor boundary (hit_obj IS the wall, previously skipped).

- Store face_normal_world in coplanar face candidates and call
  build_anchor_from_hit in _build_ifc_anchor for snap=="FACE", so
  the anchor gets a proper FACE type with normal_local in addr.
  This enables ForcePerpendicularToFace and LinePosition to work
  for coplanar edge-on face snaps the same as directly-hit faces.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tline

- Add dedicated POST_PIXEL GPU callback (_draw_anchor_hover_global) using
  pre-converted 2D screen coords, replacing the shared POST_VIEW callback
  that caused GPU state issues and Blender freezes
- Add LAYER snap mode hover indicator showing full seam-corner outline
- Remove select_set calls from hover highlight to prevent green object outline
- Add _is_hidden() using hide_get/hide_viewport/visible_get so only scene-
  visible objects are snap candidates
- Add _face_perp_ok() filter (camera-based) to prefer wall faces over
  floor/ceiling faces in FACE mode; non-perp hits tracked in ray_hit_objs
  so directly-hit elements always rank above proximity-found neighbours
- Add _get_current_anchor_guid() to promote the currently-bound element to
  the front of the candidate list when re-picking an anchor vertex
- Add _coplanar_face_outline() to merge tessellated triangles (including
  walls with window/door voids) into the correct outer face boundary;
  walks all disconnected loops and returns the largest (outer perimeter),
  skips meshes > 500 polygons to avoid freezing on terrain objects
- Skip _prefer_perp_face_index in FACE mode so the exact hit face is used
  rather than the face most perpendicular to the camera
- Sort proximity candidates so ray-hit objects rank before bbox-only matches

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@theoryshaw theoryshaw force-pushed the parametric_dimensions branch from 0eee689 to 76e390f Compare June 7, 2026 22:55
@theoryshaw

Copy link
Copy Markdown
Member Author

The build (BonsaiPR v0.8.6-alpha260608-c65433c) already contains old
parametric_dimensions commits (pre-rebase, different hashes) followed by
PR #7798 (ManualDrawingReference) and other PRs merged on top.

After the rebase of parametric_dimensions onto v0.8.0, git's LCA between
the build state and this branch reverted to v0.8.0. This caused false
conflicts in DrawParametricDimension (operator.py) where both sides "added"
it from empty, and real conflicts in prop.py/workspace.py/pset from PR #7798
additions that now appeared as build-only changes.

Fix: merge old-pd tip 913c9c4 with -s ours (no content change) to make
it a git ancestor of this branch. This shifts the LCA to 913c9c4, so:
- operator.py: only new pd changed DrawParametricDimension from that LCA -> clean
- prop.py / workspace.py / pset: new pd = LCA; build added #7798 content -> clean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@theoryshaw

Copy link
Copy Markdown
Member Author

theoryshaw and others added 7 commits June 14, 2026 11:20
…ension

Walls viewed edge-on in plan (2-7 px screen bbox) were never hit by
Blender's raycast, so all three snap modes silently returned nothing.

- FACE: remove has_coplanar_edge Z-gate; vertical faces are now
  snappable regardless of what elevation the native snap lands on
  (sub-floor surfaces at Z~-7.5m were blocking all candidates)
- All modes: replace hardcoded 30 px _FACE_THRESH_D2 with a
  per-candidate max_tol that matches the adaptive _SCREEN_TOL used
  for bbox inclusion (~98 px for 2 px-wide walls)
- VERTEX/EDGE/LAYER: remove early `if not hit_obj: return None`;
  all modes now search objs_2d_bbox with adaptive tolerance when the
  primary raycast misses
- Add _get_mesh_snap_candidates fallback for tessellated elements
  (IfcFacetedBrep etc.) where get_profile_snap_candidates returns []
- Add LOCAL_POINT anchor method (build_anchor_from_local_point +
  resolve_anchor handler) so mesh-derived anchors store element-local
  coords and follow the element through moves/rotations rather than
  becoming free-floating WORLD anchors

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…stale active-obj guard

ClickNearestDimensionAnchor was firing SetDimensionAnchor immediately on
LMB PRESS and returning FINISHED, which caused Blender to re-deliver the
RELEASE to view3d.select — deselecting the annotation mid-flight.  Rewrite
as a two-event modal: PRESS starts the modal, RELEASE fires SetDimensionAnchor
and exits.  SetDimensionAnchor also swallows any LMB RELEASE it receives to
prevent view3d.select from stealing the active object after hand-off.

The pre-click active-object guard (skip if dimension not active_object) caused
dots to never turn blue: view3d.select was silently replacing the dimension
with the plane underneath on every line-body click, so the dimension was
never the active object at the time of the dot click.  Removed — the operator
now selects the dimension itself before going modal, making each dot click
self-contained.

RADIUS_PX reduced from 60 to 15 to match the gizmo disc visual size and
prevent false triggers on line-body clicks near endpoints.
regenerate_dimension was only called when ForcePerpendicularToFace was
set, so normal two-anchor dimensions were left at raw polyline cursor
positions after placement.  The depsgraph handler would later correct
them when the user happened to select a referenced IFC element, making
accurate placement appear to require a manual selection step.

Remove the _force_perpendicular guard so the anchor-based regeneration
always runs at the end of _create_dimension_from_polyline.
…cularToFace

DimensionLinePositionWidget was gated behind ForcePerpendicularToFace in
both the gizmo poll and the regenerate_dimension LinePosition application.
The coupling was unnecessary: _get_line_offset_direction already derives
the offset axis from cross(world_Z, dim_direction) as its primary path,
requiring the face normal only as a vertical-dimension fallback.

Remove the ForcePerpendicularToFace guard from both sites so any
anchor-based dimension shows the drag arrows and responds to LinePosition.
…X/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.
…h UI

BakeParametricDimension (bim.bake_parametric_dimension): removes the
BBIM_Dimension pset from the active annotation, leaving the curve
geometry in place as a static dimension that no longer regenerates
when referenced elements move.

MakeDimensionParametric (bim.make_dimension_parametric): adds a
BBIM_Dimension pset to a static dimension annotation, creating one
free world-point anchor per spline vertex at the current positions.
The endpoints can then be re-anchored to IFC faces via SetDimensionAnchor.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant