Skip to content

Closes #6321: Auto-snap SECTION endpoints to drawing border#7965

Open
theoryshaw wants to merge 9 commits intov0.8.0from
inset_section_endpoints
Open

Closes #6321: Auto-snap SECTION endpoints to drawing border#7965
theoryshaw wants to merge 9 commits intov0.8.0from
inset_section_endpoints

Conversation

@theoryshaw
Copy link
Copy Markdown
Member

Closes #6321

Summary

Prior to this change, SECTION annotation endpoints had no relationship to the drawing camera border. Their positions were entirely manual — a user had to place them by hand each time, and they would not update if the drawing scale changed.

This PR introduces automatic placement of SECTION annotation endpoints: when a section annotation is created, when a drawing is activated, or when the diagram scale is changed, the two mesh vertices that form the section line are automatically positioned at a configurable inset distance (BorderOffset) from the drawing camera's orthographic border. The offset is defined in paper-space millimetres and is converted to model-space units using the drawing scale, so the same 8 mm inset looks correct at 1:50, 1:100, or any other scale.

If a user manually moves one endpoint in Edit Mode, that vertex is fingerprinted and its custom position is preserved. Only endpoints that still sit at their last auto-computed position are updated on the next activation or scale change. A dedicated operator lets the user force-reset both endpoints back to the border offset at any time.


What Changed

tool/drawing.py — new geometry helpers and core update logic

  • get_camera_dimensions(camera) — derives the camera's model-space width and height from ortho_scale and the render resolution aspect ratio.
  • _section_ray_rect_intersections(origin, direction, half_w, half_h) — finds the two t values where an infinite ray through the section line's midpoint intersects the camera border rectangle.
  • get_section_border_positions(camera, v0_world, v1_world, border_offset_mm) — uses the above two helpers to compute the two world-space positions that sit border_offset_mm (paper mm, converted by scale) inside the camera border along the section line direction.
  • update_section_endpoints(obj, camera) — the main entry point. Reads BorderOffset, AutoStartPosition, and AutoEndPosition from the BBIM_Section pset. Compares each current world-space vertex position against its stored auto-position; if they match (or no auto-position is stored yet) the vertex is auto-updated. Writes the new auto-positions and BorderOffset back to the pset, then calls bpy.ops.bim.update_representation to persist the moved vertices to the IFC geometry.
  • _parse_vector3 / _format_vector3 — small static helpers to serialise and deserialise a Vector to/from a comma-separated string for IFC pset storage.

data.py — pset defaults

get_section_markers_display_data() now adds BorderOffset: 8.0, AutoStartPosition: "", and AutoEndPosition: "" to the BBIM_Section pset defaults so the values are always present and visible in the Property Sets panel even before the user changes anything.

operator.py — new operator + activation hook

  • UpdateSectionEndpoints — a new bim.update_section_endpoints operator (label: "Reset Section to Border"). It clears AutoStartPosition and AutoEndPosition in the pset so that update_section_endpoints treats both vertices as unmodified, then calls update_section_endpoints to recompute. Only enabled when the active object is a SECTION annotation and a scene camera is set.
  • ActivateDrawingBase._execute — after sync_references reloads the IFC geometry, iterates over all IfcAnnotation members of the drawing group with ObjectType == "SECTION" and calls update_section_endpoints for each one.

prop.py — scale-change hook

update_diagram_scale() (the callback for the diagram scale dropdown) now iterates the same drawing group and calls update_section_endpoints for every SECTION annotation in it, so endpoints are repositioned immediately when the scale is changed.

core/drawing.py — creation hook

add_annotation() now calls update_section_endpoints immediately after the new annotation is assigned to the drawing group, so a freshly created section annotation starts at the correct inset positions.

__init__.py

UpdateSectionEndpoints is added to the classes tuple so it is registered with Blender.


Notes

  • Debug print("[SECTION] …") statements are present throughout and should be removed before final merge.
  • AutoStartPosition / AutoEndPosition are stored in world space in this version. An object-level translate (moving the annotation object without editing vertices) will change world-space vertex positions while leaving local-space v.co unchanged, which can cause false "manually moved" detection. This is a known limitation of the current approach.
  • BorderOffset is intentionally exposed in the Property Sets panel rather than a custom UI panel, so users can inspect and override it without any additional UI work.

Generated with the assistance of an AI coding tool.

Introduces a boolean pset property that marks an ELEVATION or
SECTION annotation as manually placed, exempting it from
automatic deletion or regeneration during drawing sync.

Generated with the assistance of an AI coding tool.
Adds operator to place manual elevation/section drawing reference
tags that survive drawing regeneration. Includes core function,
tool methods, type-selection dialog, default horizontal rotation
for elevation tags, and SVG null guard for unassigned references.

Generated with the assistance of an AI coding tool.
Adds operator to link a manual drawing reference tag to a target
drawing via IfcRelAssignsToProduct, with a pre-populated dialog
and immediate Properties panel refresh on confirm.

Generated with the assistance of an AI coding tool.
Adds MANUAL_DRAWING_REFERENCE to the annotation type dropdown.
Selecting it shows a dialog to choose elevation or section and
optionally assign a target drawing before placement. Tags are
protected from regeneration via EPset_Annotation.IsManualDrawingReference.

Generated with the assistance of an AI coding tool.
The elevation tag's local -Z axis is intentionally parallel to its
drawing camera's view direction, making screen-space projection of that
axis always degenerate (zero XY delta). Fall through to the tag's local
+X axis, which lies in the camera plane and rotates correctly as the
user adjusts the tag's orientation. Also fix a zero-length vector crash
in svgwriter when the same degenerate case occurs during SVG export.
Use get_default_annotation_matrix() for manual SECTION annotations so
the object is placed in the camera's annotation plane with the correct
rotation, matching how auto-generated section annotations are created.
Without this, annotations placed in elevation or section camera views
had identity rotation, causing the IFC representation to be in the wrong
coordinate system and failing to tessellate.

Also guard draw_edit_object_interface against non-IFC active objects to
prevent AssertionError during toolbar redraws, and skip IFC item edit
mode for ELEVATION/SECTION annotation types on placement.
Replace plain enum dropdowns with prop_with_search in the AddAnnotation
and AssignManualDrawingReference operator dialogs, making it easier to
locate a target drawing when many drawings exist in the project.

Generated with the assistance of an AI coding tool.
Extends manual drawing reference annotations (elevation and section) to
also support external SVG references imported via bim.add_reference.

- Add "Is a Reference" checkbox to the annotation tool sidebar, shown
  when Elevation or Section is the active type; checking it and pressing
  Add opens a dialog to optionally link the tag to a Bonsai drawing or
  an external SVG reference
- The MANUAL_DRAWING_REFERENCE dropdown type is retained for backwards
  compatibility; selecting it shows a style picker (Elevation/Section)
  and the same linking dialog
- External-reference annotations are flagged with IsDocumentReference
  in EPset_Annotation and linked to their IfcDocumentInformation via
  IfcRelAssociatesDocument; drawing-reference annotations continue to
  use IfcRelAssignsToProduct as before
- SVG export resolves the correct reference/sheet IDs for both link
  types via get_reference_and_sheet_id_from_annotation
- Add IsDocumentReference to the EPset_Annotation pset template
When a SECTION annotation is created or a drawing is activated,
endpoint vertices are automatically placed at a configurable
BorderOffset (paper-space mm, default 8) inside the camera border,
scaled by the drawing scale. BorderOffset is stored in the
BBIM_Section pset and visible in the Property Sets panel.
An "UpdateSectionEndpoints" operator (bim.update_section_endpoints)
resets endpoints back to the border offset on demand. Endpoints are
also recomputed when the diagram scale is changed.

Generated with the assistance of an AI coding tool.
@theoryshaw theoryshaw force-pushed the inset_section_endpoints branch from 504d0b1 to c1d4494 Compare April 19, 2026 12:31
@theoryshaw
Copy link
Copy Markdown
Member Author

BonsaiPR Conflict Resolution: PR #7798 vs PR #7965

Date: 2026-04-19
Build branch: BonsaiPR v0.8.6-alpha260419-916f06d (falken10vdl/build-0.8.6-alpha2604191301)


Problem

PR #7798 (Manual drawing reference) (ManualDrawingReference branch) was being skipped in the BonsaiPR build with:

⚠️ Skipped - Conflict with other PRs. Merges cleanly with base

The goal was to resolve the conflict without modifying PR #7798, which needs to stay clean for upstream submission.


Step 1: Identifying the Conflicting PR

PR #7798 touches the following files:

  • src/bonsai/bonsai/bim/data/pset/Psets_BBIM_Annotation.ifc
  • src/bonsai/bonsai/bim/module/drawing/__init__.py
  • src/bonsai/bonsai/bim/module/drawing/data.py
  • src/bonsai/bonsai/bim/module/drawing/decoration.py
  • src/bonsai/bonsai/bim/module/drawing/operator.py
  • src/bonsai/bonsai/bim/module/drawing/prop.py
  • src/bonsai/bonsai/bim/module/drawing/svgwriter.py
  • src/bonsai/bonsai/bim/module/drawing/ui.py
  • src/bonsai/bonsai/bim/module/drawing/workspace.py
  • src/bonsai/bonsai/core/drawing.py
  • src/bonsai/bonsai/tool/drawing.py

After testing PR #7798 against the alpha2604191301 build branch, the only conflicting file was:

src/bonsai/bonsai/bim/module/drawing/__init__.py

Checking which build PRs touched __init__.py revealed that PR #7965 (Closes #6321: Auto-snap SECTION endpoints to drawing border) (inset_section_endpoints branch) was the cause.

Confirmation: PR #7798 was successfully included in the previous build alpha2604191235, but the new alpha2604191301 build added PR #7965 which then caused the conflict.


Step 2: Testing Other Skipped PRs

The following open PRs were also not in the build branch and were tested against it:

PR Branch Result
#7781 Unhide_with_alt_click ✅ Merges cleanly
#7705 rotated_slab ✅ Merges cleanly
#7886 fix/layer3-composite-profile-outer-curve ❌ Conflicts in wall.py
#7924 fix/ci-merge-projects-contexts ❌ Conflicts in root.py
#7941 bug_extend_walls_to_underside ✅ Merges cleanly

PRs #7886 and #7924 have separate conflicts (not with PR #7798), left for another time per user request.


Step 3: Nature of the Conflict

Both PRs add a new operator class registration to the same location in __init__.py, immediately after operator.AddAnnotationType:

PR #7798 adds:

    operator.AddAnnotationType,
+   operator.AssignManualDrawingReference,   # ← PR #7798
    operator.AddDrawing,

PR #7965 adds:

    operator.AddAnnotationType,
+   operator.UpdateSectionEndpoints,         # ← PR #7965
    operator.AddDrawing,

This is a classic adjacent-line insertion conflict. The two features are independent and additive — neither is a subset of the other. Both operators need to be registered.


Step 4: Rebase Direction

Decision: Rebase PR #7965 onto PR #7798.

PR #7798 (ManualDrawingReference) PR #7965 (inset_section_endpoints)
Created March 15, 2026 April 18, 2026 (yesterday)
Maturity 5 weeks old, polished 1 day old, very new
Status Must stay clean for upstream Newer, can be adjusted

PR #7798 is the more mature, upstream-bound PR and must not be modified. PR #7965 is a brand-new PR that should accommodate the established one.

The build processes PRs in descending PR-number order (newest first). Since 7965 > 7798, PR #7965 is processed before PR #7798. After rebasing PR #7965 onto PR #7798, the build flow becomes:

  1. PR Closes #6321: Auto-snap SECTION endpoints to drawing border #7965 (rebased) → merges cleanly, brings in all of PR Manual drawing reference #7798's content + UpdateSectionEndpoints
  2. PR Manual drawing reference #7798 → "Already up to date" (its commits are already included) → ✅ no conflict

Step 5: Rebase Execution

Used git rebase --onto to replay only PR #7965's single commit onto ManualDrawingReference:

git checkout -b pr-7965-rebased origin/inset_section_endpoints
git rebase --onto origin/ManualDrawingReference origin/v0.8.0

Conflict in __init__.py: Resolved by including BOTH operators in alphabetical order:

    operator.AddAnnotationType,
    operator.AssignManualDrawingReference,   # from PR #7798
    operator.UpdateSectionEndpoints,         # from PR #7965
    operator.AddDrawing,

Rebase completed cleanly. All other files (data.py, operator.py, prop.py, core/drawing.py, tool/drawing.py) auto-merged without conflicts.


Step 6: Push

git push origin pr-7965-rebased:inset_section_endpoints --force-with-lease

Result:

To https://github.com/IfcOpenShell/IfcOpenShell.git
 + 504d0b1da0...c1d4494a64 pr-7965-rebased -> inset_section_endpoints (forced update)

Summary

Action Detail
Conflicting PRs PR #7798PR #7965
Conflict file src/bonsai/bonsai/bim/module/drawing/__init__.py
Resolution Rebase PR #7965 onto PR #7798; include both operator registrations
PR modified PR #7965 (inset_section_endpoints) — force-pushed
PR untouched PR #7798 (ManualDrawingReference) — unchanged
Expected build result Both PRs will be included in next build

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.

Have annotations, when first created, were located further inside the boundary box

1 participant