Scripting: add terminalPosition() and conductorPath()

A script laying out a drawing could read where a symbol is
(elementGeometry) but not where its terminals are, so it could not place
one symbol with a terminal exactly in line with another's -- the one
thing that makes the wire between them straight. And it could read a
wire's drawn path only through conductorSegments(), which names the wire
by a terminal and so refuses any terminal carrying two wires: 282 of the
3120 wires in the shipped examples.

terminalPosition(folio, element, terminal) returns where a wire docks on
the terminal, in folio coordinates, and which way it leaves (n/e/s/w,
the element's rotation included). conductorPath(folio, uuid) returns any
wire's drawn path as points, by its uuid.

tst_scriptlayoutreads checks the two against each other on every wire of
a fixture: each path starts and ends where its terminals' positions say,
and leaves each the way it faces; where conductorSegments() can name a
wire, both give the same points; and a quarter turn turns the facing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015FPuYPS4T7QuEwjNu22rXD
This commit is contained in:
ispyisail
2026-10-03 13:26:34 +13:00
parent 18568dfd53
commit cc23cf820f
4 changed files with 260 additions and 1 deletions
+70
View File
@@ -4132,6 +4132,76 @@ QVariantMap QetScriptApi::elementGeometry(int folioIndex, const QString &element
return g;
}
/**
@brief QetScriptApi::terminalPosition
Where a wire docks on terminal @p terminalIndex of the element, in folio
coordinates (Terminal::dockConductor(), the point conductorSegments()
and conductorPath() start or end at), and which way the terminal sends
its wire on the folio, the element's rotation included: "n", "e", "s"
or "w". Two terminals facing each other are joined by a straight wire
exactly when their x (n/s) or y (e/w) are equal, which is what a
script needs to place a symbol in line with another before wiring it.
Empty if the element or terminal is not found.
*/
QVariantMap QetScriptApi::terminalPosition(int folioIndex, const QString &elementUuid,
int terminalIndex) const
{
// const_cast: findTerminal logs, and log() writes to stderr, which is
// not a const operation on this object. The lookup itself changes
// nothing.
auto *self = const_cast<QetScriptApi *>(this);
Terminal *terminal = self->findTerminal(folioIndex, elementUuid, terminalIndex,
QStringLiteral("terminalPosition"));
if (!terminal) return {};
const QPointF p = terminal->dockConductor();
static const char *const facing[] = {"n", "e", "s", "w"};
const int o = static_cast<int>(terminal->orientation());
QVariantMap m;
m.insert(QStringLiteral("x"), p.x());
m.insert(QStringLiteral("y"), p.y());
m.insert(QStringLiteral("facing"),
QString::fromLatin1(o >= 0 && o < 4 ? facing[o] : "?"));
return m;
}
/**
@brief QetScriptApi::conductorPath
The drawn path of the conductor carrying @p conductorUuid on the folio,
as a list of {x, y} points in folio coordinates: the first is where it
docks on its first terminal (conductorEnds()[0]), the last where it
docks on its second, and every point between is a corner or a segment
end. The same points conductorSegments() lists, but for any conductor
-- conductorSegments() names one by a terminal and so refuses a
terminal that carries two. Empty if there is no such conductor.
*/
QVariantList QetScriptApi::conductorPath(int folioIndex, const QString &conductorUuid) const
{
if (!m_project) return {};
const QList<Diagram *> diagrams = m_project->diagrams();
if (folioIndex < 0 || folioIndex >= diagrams.count()) return {};
const QUuid wanted(conductorUuid);
if (wanted.isNull()) return {};
DiagramContent content(diagrams.at(folioIndex), false);
for (Conductor *c : content.conductors(DiagramContent::AnyConductor)) {
if (c->uuid() != wanted) continue;
QVariantList points;
auto add = [&points](const QPointF &p) {
QVariantMap m;
m.insert(QStringLiteral("x"), p.x());
m.insert(QStringLiteral("y"), p.y());
points << m;
};
const QList<ConductorSegment *> segs = c->segmentsList();
for (int i = 0; i < segs.count(); ++i) {
if (i == 0) add(c->mapToScene(segs.at(i)->firstPoint()));
add(c->mapToScene(segs.at(i)->secondPoint()));
}
return points;
}
return {};
}
/**
@brief QetScriptApi::insertFolio
Add a folio at a position (0 is first, folioCount() is last) through