Files
qelectrotech-source-mirror/sources/conductorrouter.h
T
ispyisail 84cda1f0b0 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>
2026-10-03 08:43:27 +13:00

84 lines
2.9 KiB
C++

/*
Copyright 2006-2026 The QElectroTech Team
This file is part of QElectroTech.
QElectroTech is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
QElectroTech is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-2.0-or-later
#ifndef CONDUCTORROUTER_H
#define CONDUCTORROUTER_H
#include <QList>
#include <QPointF>
#include <QRectF>
#include <QString>
#include <QVector>
/**
@brief The ConductorRouter namespace
Finds a path for a conductor that goes around obstacles -- the bounding
rectangles of the symbols on the folio -- instead of the default two or
three straight segments, which run through whatever lies between the two
terminals. Kept free of any QGraphicsItem so the geometry can be tested
on its own.
The path is made of horizontal and vertical segments only, like every
conductor. It leaves each terminal in the terminal's own direction,
runs on the folio grid, and is the cheapest one found by a search that
charges for length, for every bend, and for running along or crossing
the wires already drawn.
*/
namespace ConductorRouter
{
///The direction a terminal points, outward from its symbol.
///Same order as Qet::Orientation.
enum class Direction { North, East, South, West };
struct Request
{
QPointF start; ///< the first terminal's docking point
Direction start_direction = Direction::North;
QPointF end; ///< the second terminal's docking point
Direction end_direction = Direction::North;
///Areas no segment may cross. A margin is added to each.
QList<QRectF> obstacles;
///Each terminal's own symbol, when known, as it appears in
///obstacles. A route steps out of it first and never walks
///through another symbol to get out; an obstacle drawn around
///it (a cabinet made as one element) is left out.
QRectF start_symbol;
QRectF end_symbol;
///The other wires on the folio, each as its list of points.
///Running along one or crossing one costs extra.
QList<QVector<QPointF>> wires;
///The route stays inside this, when it is valid (the folio).
QRectF bounds;
qreal grid = 10.0;
///Added around each obstacle, so a route keeps clear of it
qreal margin = 5.0;
};
struct Result
{
///From start to end, both included; empty if none was found
QList<QPointF> points;
///Why there is no route, when points is empty
QString error;
};
Result route(const Request &request);
}
#endif // CONDUCTORROUTER_H