Joining two crossing edges is ambiguous - the intersection divides each
edge into two pieces and either could be the one kept. Join always kept
the longest piece, leaving no way to ask for the shorter one.
The piece you clicked is now the piece that survives. Clicks are captured
as they happen by bim.cad_select / bim.cad_select_box, which wrap the CAD
tool's selection keymap and record how far along the edge the pointer
landed. Reading the mouse when the hotkey is pressed does not work: it has
moved on by then, and a remembered screen position goes stale because the
geometry itself shifts during the join.
vert_idx_to_move splits the edge at the intersection rather than at its
midpoint. The two differ, and splitting at the midpoint picks the wrong
side for any click between them. Where the intersection falls beyond the
end of an edge, the edge has to be extended to reach the joint and there
is only one sensible outcome, so the click is ignored there.
Join also now removes the chain of edges between the two being joined,
which were previously left dragged along by the moved vertices as a spike
hanging off the new corner. On a closed loop both routes between two edges
can be the same length, so the chain is found by searching between the two
ends the join discards rather than by shortest path alone.
Undo clears the selection as well as the remembered clicks. Undo restores
the selection with the geometry, which would otherwise leave both edges
looking ready to join while the clicks describing them were gone, and the
join would quietly fall back to the longest side. This is scoped to when
the CAD tool is active.
Two selection fixes fall out of wrapping view3d.select. Every mode property
is now passed explicitly - omitting any let it keep whatever value it last
held, silently turning a plain click into an extending one and leaving the
previous selection in place. And view3d.select_box is wrapped too, since a
click that drags even slightly is routed there and would otherwise arrive
with no click recorded.
Which side is easy to hit depends on how large it is on screen, so a short
piece at a low zoom is a small target. Zooming in makes the intended side
easier to click.
Refs IfcOpenShell#9314
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Closes #9314
Important
Stacked on #8153 — this PR targets cross_select_integration, so the diff shows only its own two commits. #8153 needs to merge first. GitHub will retarget this to v0.9.0 automatically when it does.
What this does
Joining two crossing edges is ambiguous: the intersection divides each edge into two pieces and either could be the one kept. Join always kept the longest piece, so the shorter one was unreachable.
Now the piece you clicked is the piece that survives — click near the end you want to keep, and the rest is trimmed back to the intersection. This matches how joining and filleting work in most CAD packages, where the portions you select are the portions retained.
Join also removes the chain of edges between the two being joined. Previously those weren't removed at all, just dragged along by the vertices the join moved, leaving a spike hanging off the new corner.
Notes for review
Clicks are captured as they happen. bim.cad_select / bim.cad_select_box wrap the CAD tool's selection keymap and record how far along the edge the pointer landed. Reading the mouse when the hotkey is pressed does not work — it has moved on by then — and a remembered screen position goes stale, because the geometry itself shifts during the join.
The edge is split at the intersection, not at its midpoint. The two differ, and splitting at the midpoint picks the wrong side for any click between them. Where the intersection falls beyond the end of an edge, the edge has to be extended to reach the joint and there is only one sensible outcome, so the click is ignored there.
The chain is found between the two ends the join discards. On a closed loop both routes between two edges can be the same number of edges, so shortest-path alone picks arbitrarily and can delete the wrong side.
Undo clears the selection as well as the remembered clicks. Undo restores the selection along with the geometry, which would otherwise leave both edges looking ready to join while the clicks describing them were gone — and the join would quietly fall back to the longest side. Scoped to when the CAD tool is active.
Two incidental fixes to selection
Both fall out of wrapping view3d.select, and are worth keeping regardless of this feature:
Interaction with #8153
The second commit exists because Cross Select routes every LEFTMOUSE press through bim.cross_select and drops view3d.select from the tool keymaps entirely. Without it, Cross Select being enabled — the default — means the CAD tool sees no clicks at all and Join silently reverts to keeping the longest side.
bim.cross_select now reports its single clicks to the CAD tools. Only clicks; a drag is a box select and picks no particular end. Rebuilding the keymap for the preference toggle also had to stop handing every tool the generic selection pair, so tools can declare their own via bim_select_operators.
Known characteristic
The side you click is only as easy to hit as it is big on screen, so a short piece at a low zoom is a small target — zooming in makes the intended side easier to click. This is inherent to "what you click is what you keep" rather than a defect, but worth knowing.
Testing
Manually tested in Blender during development, with Cross Select off: joining with the click on either side of the intersection, undoing and re-picking, and joining across a chain of intermediate edges.
Two things have not been exercised since and are worth a look:
Not covered by automated tests.