mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-19 04:24:14 +02:00
Reattach wires to a terminal that moved a little since the project was saved
A project saves, for every placed symbol, where each terminal docked, its orientation and its number. When the symbol's definition is replaced by one whose terminal sits a few units elsewhere, wires were matched by exact position and so were dropped on the next open: legacy wires by Terminal:: fromXml(), uuid wires by the uuid derived from the terminal's new place. Element::fromXml() now pairs each saved terminal that nothing matched exactly with the live terminal of the same orientation whose docking point is within 5 units in x and y, only when each is the other's only candidate. A pair registers the saved number, and the uuid derived from the old place, which Diagram::findTerminals() checks after the derived uuid of the current place. Wire ends reattached this way are counted and logged once per folio. tst_movedterminals covers a move of 3 and 5 units (number and derived uuid), 6 units, two candidates, and an unmoved terminal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
#include "../qetapp.h"
|
||||
#include "../qetproject.h"
|
||||
#include "../dataBase/projectdatabase.h"
|
||||
#include "../ElementsCollection/terminaluuids.h"
|
||||
#include "../PropertiesEditor/propertieseditordialog.h"
|
||||
#include "../autoNum/assignvariables.h"
|
||||
#include "../autoNum/elementautonumschemecommand.h"
|
||||
@@ -972,6 +973,107 @@ bool Element::valideXml(QDomElement &e)
|
||||
return(true);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Element::matchMovedTerminals
|
||||
Pair the saved terminals that no live terminal matched exactly with the
|
||||
live terminals that moved a little: the definition of the symbol was
|
||||
replaced or redrawn since the project was saved. A saved record gives
|
||||
the old docking point and orientation of its terminal, so it is paired
|
||||
with a live terminal of the same orientation whose docking point is
|
||||
within moved_terminal_reach in x and y, when each is the only candidate
|
||||
of the other. Anything less clear stays unmatched, as before.
|
||||
|
||||
A pair registers the record's legacy id, and the uuid TerminalUuids
|
||||
derived for the terminal at its old place, so that a wire naming either
|
||||
reattaches (see Diagram::findTerminals()).
|
||||
@param records : the saved terminals of this symbol
|
||||
@param matched_records : indexes in records already matched
|
||||
@param matched_terminals : the live terminals already matched
|
||||
@param id_adr : receives the legacy id of each pair
|
||||
*/
|
||||
void Element::matchMovedTerminals(const QList<QDomElement> &records,
|
||||
const QSet<int> &matched_records,
|
||||
const QSet<Terminal *> &matched_terminals,
|
||||
QHash<int, Terminal *> &id_adr)
|
||||
{
|
||||
if (matched_records.size() == records.size()) return;
|
||||
|
||||
const auto dock_of = [](const QDomElement &r) {
|
||||
return QPointF(r.attribute(QStringLiteral("x")).toDouble(),
|
||||
r.attribute(QStringLiteral("y")).toDouble());
|
||||
};
|
||||
const auto orientation_of = [](const QDomElement &r) {
|
||||
return r.attribute(QStringLiteral("orientation")).toInt();
|
||||
};
|
||||
|
||||
QHash<int, QList<Terminal *>> candidates; // record -> live terminals in reach
|
||||
QHash<Terminal *, QList<int>> claimants; // live terminal -> records in reach
|
||||
for (int i = 0; i < records.size(); ++i) {
|
||||
if (matched_records.contains(i)) continue;
|
||||
const QPointF dock = dock_of(records.at(i));
|
||||
for (Terminal *t : std::as_const(m_terminals)) {
|
||||
if (matched_terminals.contains(t)) continue;
|
||||
if (static_cast<int>(t->orientation()) != orientation_of(records.at(i))) continue;
|
||||
if (qAbs(t->dock_elmt_.x() - dock.x()) > moved_terminal_reach
|
||||
|| qAbs(t->dock_elmt_.y() - dock.y()) > moved_terminal_reach) continue;
|
||||
candidates[i] << t;
|
||||
claimants[t] << i;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = candidates.constBegin(); it != candidates.constEnd(); ++it) {
|
||||
Terminal *t = it.value().value(0);
|
||||
if (it.value().size() != 1 || claimants.value(t).size() != 1) continue;
|
||||
const QDomElement &record = records.at(it.key());
|
||||
const QPointF dock = dock_of(record);
|
||||
|
||||
//The old place of the terminal: its docking point less the
|
||||
//stub that Terminal::init() added
|
||||
QPointF old_pos = dock;
|
||||
switch (orientation_of(record)) {
|
||||
case Qet::North: old_pos -= QPointF(0, Terminal::terminalSize); break;
|
||||
case Qet::East : old_pos -= QPointF(-Terminal::terminalSize, 0); break;
|
||||
case Qet::West : old_pos -= QPointF(Terminal::terminalSize, 0); break;
|
||||
case Qet::South: old_pos -= QPointF(0, -Terminal::terminalSize); break;
|
||||
}
|
||||
//The rank among the records at the same place, as the project
|
||||
//counted it when it gave the terminals their uuids
|
||||
int rank = 0;
|
||||
for (int j = 0; j < it.key(); ++j) {
|
||||
const QPointF other = dock_of(records.at(j));
|
||||
if (orientation_of(records.at(j)) == orientation_of(record)
|
||||
&& qAbs(other.x() - dock.x()) < 1e-6
|
||||
&& qAbs(other.y() - dock.y()) < 1e-6)
|
||||
++rank;
|
||||
}
|
||||
|
||||
id_adr.insert(record.attribute(QStringLiteral("id")).toInt(), t);
|
||||
m_old_place_terminals.insert(TerminalUuids::derived(old_pos.x(), old_pos.y(),
|
||||
orientation_of(record), rank), t);
|
||||
m_moved_terminals << t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@return the terminal that was paired with the saved terminal whose uuid
|
||||
derived from its old place is @p old_place_uuid, if any
|
||||
@see matchMovedTerminals()
|
||||
*/
|
||||
Terminal *Element::terminalFromOldPlace(const QUuid &old_place_uuid) const
|
||||
{
|
||||
return m_old_place_terminals.value(old_place_uuid, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
@return true if @p terminal was paired with a saved terminal at another
|
||||
place than its own
|
||||
@see matchMovedTerminals()
|
||||
*/
|
||||
bool Element::terminalWasMoved(const Terminal *terminal) const
|
||||
{
|
||||
return m_moved_terminals.contains(const_cast<Terminal *>(terminal));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Element::fromXml
|
||||
Import the parameters of this element from a xml document.
|
||||
@@ -999,22 +1101,28 @@ bool Element::fromXml(QDomElement &e,
|
||||
}
|
||||
|
||||
QHash<int, Terminal *> priv_id_adr;
|
||||
QSet<Terminal *> matched_terminals;
|
||||
QSet<int> matched_records;
|
||||
|
||||
for (auto *qgi : childItems())
|
||||
{
|
||||
if (auto terminal_ = qgraphicsitem_cast<Terminal *>(qgi))
|
||||
{
|
||||
for(auto qde : liste_terminals)
|
||||
for (int i = 0; i < liste_terminals.size(); ++i)
|
||||
{
|
||||
QDomElement qde = liste_terminals.at(i);
|
||||
if (terminal_ -> fromXml(qde))
|
||||
{
|
||||
priv_id_adr.insert(qde.attribute(QStringLiteral("id")).toInt(),
|
||||
terminal_);
|
||||
matched_terminals << terminal_;
|
||||
matched_records << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matchMovedTerminals(liste_terminals, matched_records, matched_terminals, priv_id_adr);
|
||||
|
||||
//Check that associated id/address doesn't conflict with table_id_adr
|
||||
for(auto found_id : priv_id_adr.keys())
|
||||
|
||||
Reference in New Issue
Block a user