Routing: no loops between close symbols, and route inside frames

Reported on #1178: with "route": "avoid", two contacts one above the
other, wired bottom terminal to top terminal 30 px apart or closer, got
a five-segment loop (at 20 px: v 80, h -20, v -140, h 20, v 80) that ran
down through the lower contact and back up past the upper one.

The cause was exitPoint(): it walked out of a terminal until clear of
every obstacle, so with another symbol in front it walked through that
symbol, and the search then had to come back. The same walk made a wire
between two symbols inside a frame (a cabinet drawn as one element)
leave through the frame's side, go round, and cross back in.

The router now knows each terminal's own symbol (Request::start_symbol,
end_symbol; applyRoute() fills them):
- two terminals facing each other on one line with nothing between
  them are joined straight, however close;
- the exit walks through the margin around other symbols but never
  through one; a terminal pointing straight into another symbol gets
  "no-route" instead of a route through it;
- an obstacle drawn around either end's own symbol is left out.
Without the symbols (the old Request), routes are as before.

Rerouting every wire of four shipped examples (perceuse, affuteuse_250h,
Polonez, industrial; 1331 wires): master routes 121 of them through
another symbol, this none (10 pass through a second symbol lying
exactly on an end symbol's rectangle, which no route can avoid). 99
wires that master routed through a symbol now get "no-route" and keep
their path. The 1232 wires both route are 6 % shorter in total
(436,144 -> 410,608 units) with 11 % fewer bends (2256 -> 2010).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-10-03 08:43:03 +13:00
parent 25084a44da
commit 84cda1f0b0
5 changed files with 218 additions and 17 deletions
+7 -2
View File
@@ -1226,8 +1226,9 @@ ConductorRouter::Direction routerDirection(Qet::Orientation o)
as one undo step through Conductor::setPathPoints() -- the same
ChangeConductorCommand a handle drag pushes, so the path is saved and
survives a reload. Obstacles are every element's own rectangle, its
texts left out; the other conductors are not obstacles but cost extra
to run along or cross.
texts left out, except one drawn around either end's own symbol (a
frame); the other conductors are not obstacles but cost extra to run
along or cross.
@return "routed", or "no-route" with the reason logged when there is
no such path -- the conductor then keeps the path it had. Not a
failure: the wire exists and joins the right terminals either way.
@@ -1246,6 +1247,10 @@ QString QetScriptApi::applyRoute(Conductor *conductor, const QString &caller)
request.bounds = diagram->border_and_titleblock.insideBorderRect();
for (Element *e : diagram->elements())
request.obstacles << e->mapRectToScene(e->boundingRect());
if (Element *e = conductor->terminal1->parentElement())
request.start_symbol = e->mapRectToScene(e->boundingRect());
if (Element *e = conductor->terminal2->parentElement())
request.end_symbol = e->mapRectToScene(e->boundingRect());
for (Conductor *other : diagram->conductors()) {
if (other == conductor) continue;
QVector<QPointF> wire;