Extends the SVG projection line deduplication introduced earlier with
several new cases:
**Containment adjacency (inner element inside outer)**
- The original shared-vertex check failed when an inner element's corners
lie on edges of the outer element rather than at corner vertices.
- Added vertex-to-edge proximity check using point-to-segment distance.
- When one AABB is fully contained within another, skip the dominant-face
normal check: a short/flat inner element's largest polygon is its
top/bottom face, giving a spurious perpendicular normal.
**Anti-parallel normals (mirrored wall faces)**
- Two adjacent wall sections can have opposite face normals (+Y vs -Y)
while still being on the same plane. The original `abs(dot)` check
correctly identifies them as parallel, but projecting onto each
object's own normal produced mirrored plane-position values that never
matched.
- Fixed by projecting both objects' vertices onto the same reference
normal (n_a) when computing face-plane positions.
**Parallel offset walls (false positive rejection)**
- Two walls with identical orientations but on different parallel planes
(e.g., offset walls meeting at a corner) were incorrectly accepted by
the normal-direction check alone.
- Added face-plane position check: after confirming normals are parallel,
verify at least one face-plane position of A (projected onto the shared
normal) matches one of B within tolerance.
**L-shaped wall split segments (collinear overlap)**
- An L-shaped wall's boundary at a shared interface can be split into
multiple sub-segments (one per notch side), while the adjacent simple
wall has a single full-length segment covering the same edge.
- Exact endpoint matching always failed in this case.
- Extended `lines_match` with a collinear-overlap check: axis-aligned
segments on the same line with overlapping extents (beyond a trivial
point touch) are now treated as the same physical edge.
Fix#3742: Improve coplanar boundary removal for complex wall configurations
Extends the SVG projection line deduplication introduced earlier with
several new cases:
**Containment adjacency (inner element inside outer)**
- The original shared-vertex check failed when an inner element's corners
lie on edges of the outer element rather than at corner vertices.
- Added vertex-to-edge proximity check using point-to-segment distance.
- When one AABB is fully contained within another, skip the dominant-face
normal check: a short/flat inner element's largest polygon is its
top/bottom face, giving a spurious perpendicular normal.
**Anti-parallel normals (mirrored wall faces)**
- Two adjacent wall sections can have opposite face normals (+Y vs -Y)
while still being on the same plane. The original `abs(dot)` check
correctly identifies them as parallel, but projecting onto each
object's own normal produced mirrored plane-position values that never
matched.
- Fixed by projecting both objects' vertices onto the same reference
normal (n_a) when computing face-plane positions.
**Parallel offset walls (false positive rejection)**
- Two walls with identical orientations but on different parallel planes
(e.g., offset walls meeting at a corner) were incorrectly accepted by
the normal-direction check alone.
- Added face-plane position check: after confirming normals are parallel,
verify at least one face-plane position of A (projected onto the shared
normal) matches one of B within tolerance.
**L-shaped wall split segments (collinear overlap)**
- An L-shaped wall's boundary at a shared interface can be split into
multiple sub-segments (one per notch side), while the adjacent simple
wall has a single full-length segment covering the same edge.
- Exact endpoint matching always failed in this case.
- Extended `lines_match` with a collinear-overlap check: axis-aligned
segments on the same line with overlapping extents (beyond a trivial
point touch) are now treated as the same physical edge.
Generated with the assistance of an AI coding tool.
Adds `remove_coplanar_boundary_lines()` to operator.py (Bonsai uses this
path, not draw.py's main()). After `merge_linework_and_add_metadata()`
assigns material CSS classes, this post-processes the SVG to delete
projection line segments that appear in two or more adjacent, coplanar
elements with the same material and presentation style.
Key design decisions:
- Material identity: compared via sorted IFC material ID tuples from
`get_materials()`, not CSS class names — avoids false matches between
unrelated `material-null` elements.
- Presentation style identity: compared via IFC IfcPresentationStyle IDs
from `StyledByItem` on geometry representation items — handles elements
with no material but distinct visual styles.
- Physical adjacency: confirmed by a 3D shared-vertex test (tol=0.01 m)
after a quick AABB guard, rejecting elements whose 2D projections
overlap but sit at different depths.
- Coplanarity: determined by the dominant (largest-area) face normal of
each Blender mesh object — area-weighted averages are unreliable for
slabs whose equal top/bottom faces cancel out. Folded walls sharing an
edge but meeting at an angle are correctly rejected (normal dot ≪ 1.0).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Anthropic prompt caching (cache_control on system prompt and
tools) to reduce repeated token costs by ~90%
- Truncate large tool results in conversation history (2000 char cap)
to prevent context bloat from ifc_tree/ifc_select responses
- Add sliding window (40 messages) on conversation history, trimming
at user message boundaries to avoid breaking tool-call sequences
- Default "New IFC" button to IFC4X3 schema instead of IFC4
- Constrain ifc_new schema parameter with enum to prevent invalid
schema strings like "IFC4X3ADD2"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Parse ifcmerge JSON output and display a per-conflict breakdown in the
panel when merge fails. Ctrl+click on the Merge button previews
conflicts without committing. Add SelectConflictEntity operator to
select and frame the conflicting object in the 3D viewport.
Generated with the assistance of an AI coding tool.
A degenerate edge (zero-length segment) caused an early `return`
of a tuple instead of continuing the loop, resulting in a
TypeError when snap.py iterated the result and tried to assign
`point["group"]` on a float.
Generated with the assistance of an AI coding tool.
Add a provider selector (OpenAI / Anthropic) to the ifcchat web UI,
allowing users to use their Anthropic API key with Claude models
instead of only OpenAI.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a remote branch tip is checked out (resulting in detached HEAD),
the new-branch name field is now pre-filled with the local equivalent
of the remote branch name (generating a unique suffix if that name is
already taken), so the commit button is immediately usable.
See #7580
Generated with the assistance of an AI coding tool.
This version has some functional differences:
- Structured JSON error message instead of free text (on STDOUT not STDERR)
- New --prioritise-local flag to control which side wins in merge conflicts (not used by Bonsai yet)
- IfcLocalPlacement conflicts now auto-resolve instead of failing the merge (partial solution to #6885)
- Float values are normalised when comparing entities (workaround for #7696)
Include bonsai_git_branch in get_debug_info(). For dev environments
using the GitPython-based update_commit_data() path, the branch is
read from repo.active_branch.name. For built extensions, a 7777777
placeholder is replaced at build time via the Makefile, matching the
existing pattern for bonsai_commit_hash and bonsai_commit_date.
Generated with the assistance of an AI coding tool.
Move all business logic into bonsai core and tool. Performance fixes to
minimise file IO, various minor bug fixes and tests.
Generated with the assistance of an AI coding tool.
In order to fix error of the type:
| point, _ = cls.intersect_edge_region_border(
| ^^^^^^^^
| TypeError: cannot unpack non-iterable NoneType object
a tuple is expected.
Two bugs introduced in 31b571322:
- SnapObj assumed obj.data is always a Mesh; non-mesh
objects (empties, lights, etc.) have obj.data = None,
causing an AttributeError on obj.data.edges.
- view3d_utils was used but never imported.
Generated with the assistance of an AI coding tool.