mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-11 14:44:15 +02:00
Reapply "Add cable management: drawing, cores, numbering, cross-references and cable list"
This reverts commit 2bb3343d2d.
This commit is contained in:
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#include "cablemanager.h"
|
||||
|
||||
#include "cable.h"
|
||||
#include "../autoNum/assignvariables.h"
|
||||
#include "../autoNum/numerotationcontextcommands.h"
|
||||
#include "../diagram.h"
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../qetgraphicsitem/terminal.h"
|
||||
#include "../qetproject.h"
|
||||
|
||||
#include <QHash>
|
||||
#include <QPainterPath>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
@brief Where two segments cross, if they do at all.
|
||||
|
||||
Done by hand rather than with QLineF::intersects() because the
|
||||
collinear case has to be a miss: a cable drawn exactly on top of a
|
||||
conductor follows it, it does not cross it, and counting it would
|
||||
claim a core the cable never reaches.
|
||||
@param a1 start of the first segment
|
||||
@param a2 end of the first segment
|
||||
@param b1 start of the second segment
|
||||
@param b2 end of the second segment
|
||||
@param result receives the crossing point when there is one
|
||||
@return true when the two segments cross within their own length
|
||||
*/
|
||||
bool segmentIntersection(const QPointF &a1, const QPointF &a2,
|
||||
const QPointF &b1, const QPointF &b2,
|
||||
QPointF *result)
|
||||
{
|
||||
const QPointF r = a2 - a1;
|
||||
const QPointF s = b2 - b1;
|
||||
const QPointF u = b1 - a1;
|
||||
|
||||
const qreal denominator = r.x() * s.y() - r.y() * s.x();
|
||||
if (qFuzzyIsNull(denominator)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const qreal t = (u.x() * s.y() - u.y() * s.x()) / denominator;
|
||||
const qreal v = (u.x() * r.y() - u.y() * r.x()) / denominator;
|
||||
if (t < 0.0 || t > 1.0 || v < 0.0 || v > 1.0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
*result = a1 + t * r;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief How far a point stands from a segment, in scene units
|
||||
*/
|
||||
qreal distanceToSegment(const QPointF &point, const QPointF &a, const QPointF &b)
|
||||
{
|
||||
const QPointF ab = b - a;
|
||||
const qreal length_squared = ab.x() * ab.x() + ab.y() * ab.y();
|
||||
if (qFuzzyIsNull(length_squared)) {
|
||||
return QLineF(point, a).length();
|
||||
}
|
||||
|
||||
const QPointF ap = point - a;
|
||||
qreal t = (ap.x() * ab.x() + ap.y() * ab.y()) / length_squared;
|
||||
t = qBound(0.0, t, 1.0);
|
||||
return QLineF(point, a + t * ab).length();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Whether some cable of the project already stands under this
|
||||
number.
|
||||
|
||||
A number which is taken must not be handed out twice. That happens
|
||||
while a whole run of cables is numbered one after the other -- none
|
||||
of them has been added to the project by anything but its own
|
||||
creation yet -- and it happens when a rule has simply been given a
|
||||
value which some cable carries already.
|
||||
@param project
|
||||
@param designation the number to look for
|
||||
@return true when a cable of that project stands under it
|
||||
*/
|
||||
bool designationIsTaken(QETProject *project, const QString &designation)
|
||||
{
|
||||
if (!project || designation.isEmpty()) return false;
|
||||
|
||||
for (Cable *cable : project->cables())
|
||||
{
|
||||
if (cable && cable->designation() == designation) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
@brief CableManager::crossedConductors
|
||||
Tell which conductors a line about to become a cable runs across, in
|
||||
the order they are met along the line. That order is what gives each
|
||||
core its place, and therefore its colour, so it has to be the order
|
||||
of the drawing and not the order the conductors happen to be stored
|
||||
in.
|
||||
@param diagram the folio the line is drawn on
|
||||
@param line the line, in scene coordinates
|
||||
@return the crossings, ordered from the start of the line
|
||||
*/
|
||||
QList<CableCrossing> CableManager::crossedConductors(Diagram *diagram, const QLineF &line)
|
||||
{
|
||||
QList<CableCrossing> crossings;
|
||||
if (!diagram) {
|
||||
return crossings;
|
||||
}
|
||||
|
||||
const QPointF start = line.p1();
|
||||
if (qFuzzyCompare(start, line.p2())) {
|
||||
return crossings;
|
||||
}
|
||||
|
||||
const QPointF direction = line.p2() - start;
|
||||
for (Conductor *conductor : diagram->conductors())
|
||||
{
|
||||
QPointF where;
|
||||
if (!crossingOf(diagram, line, conductor, &where)) continue;
|
||||
|
||||
CableCrossing crossing;
|
||||
crossing.conductor = conductor;
|
||||
crossing.position = where;
|
||||
crossings.append(crossing);
|
||||
}
|
||||
|
||||
std::sort(crossings.begin(), crossings.end(),
|
||||
[&start, &direction](const CableCrossing &a_, const CableCrossing &b_) {
|
||||
const QPointF da = a_.position - start;
|
||||
const QPointF db = b_.position - start;
|
||||
const qreal ta = da.x() * direction.x() + da.y() * direction.y();
|
||||
const qreal tb = db.x() * direction.x() + db.y() * direction.y();
|
||||
return ta < tb;
|
||||
});
|
||||
|
||||
return crossings;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CableManager::crossingOf
|
||||
Where one line runs across one conductor, if it does at all.
|
||||
|
||||
Done by hand rather than with QLineF::intersects() because the
|
||||
collinear case has to be a miss: a cable drawn exactly on top of a
|
||||
conductor follows it, it does not cross it, and counting it would
|
||||
claim a core the cable never reaches.
|
||||
@param diagram the folio the line is drawn on
|
||||
@param line the line, in scene coordinates
|
||||
@param conductor the wire being asked about
|
||||
@param result receives the crossing point, in scene coordinates
|
||||
@return true when the line really runs across that wire
|
||||
*/
|
||||
bool CableManager::crossingOf(Diagram *diagram,
|
||||
const QLineF &line,
|
||||
Conductor *conductor,
|
||||
QPointF *result)
|
||||
{
|
||||
if (!conductor || !diagram) return false;
|
||||
|
||||
const QPointF start = line.p1();
|
||||
const QPointF end = line.p2();
|
||||
if (qFuzzyCompare(start, end)) return false;
|
||||
|
||||
//Work in the conductor's own coordinates: its path is stored
|
||||
//there, and a conductor is never where it was when it was
|
||||
//created once the whole drawing has been moved about.
|
||||
const QPointF a = conductor->mapFromScene(start);
|
||||
const QPointF b = conductor->mapFromScene(end);
|
||||
|
||||
const QPainterPath path = conductor->path();
|
||||
for (int i = 0; i + 1 < path.elementCount(); ++i)
|
||||
{
|
||||
const QPainterPath::Element &e1 = path.elementAt(i);
|
||||
const QPainterPath::Element &e2 = path.elementAt(i + 1);
|
||||
//A MoveTo ends the segment before it: the jump to the new
|
||||
//subpath is not drawn, so nothing can cross it.
|
||||
if (e2.type == QPainterPath::MoveToElement) {
|
||||
continue;
|
||||
}
|
||||
QPointF where;
|
||||
if (segmentIntersection(a, b,
|
||||
QPointF(e1.x, e1.y),
|
||||
QPointF(e2.x, e2.y),
|
||||
&where))
|
||||
{
|
||||
if (result) {
|
||||
*result = conductor->mapToScene(where);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CableManager::distanceTo
|
||||
How far a point of the sheet stands from a wire, in scene units.
|
||||
@param conductor
|
||||
@param scene_pos
|
||||
@return that distance, which is where the wire passes when the point
|
||||
is beside it rather than in front of one of its ends
|
||||
*/
|
||||
qreal CableManager::distanceTo(Conductor *conductor, const QPointF &scene_pos)
|
||||
{
|
||||
if (!conductor) {
|
||||
return std::numeric_limits<qreal>::max();
|
||||
}
|
||||
|
||||
qreal best = std::numeric_limits<qreal>::max();
|
||||
const QPainterPath path = conductor->path();
|
||||
for (int i = 0; i + 1 < path.elementCount(); ++i)
|
||||
{
|
||||
const QPainterPath::Element &e1 = path.elementAt(i);
|
||||
const QPainterPath::Element &e2 = path.elementAt(i + 1);
|
||||
if (e2.type == QPainterPath::MoveToElement) continue;
|
||||
|
||||
const qreal d = distanceToSegment(
|
||||
scene_pos,
|
||||
conductor->mapToScene(QPointF(e1.x, e1.y)),
|
||||
conductor->mapToScene(QPointF(e2.x, e2.y)));
|
||||
if (d < best) {
|
||||
best = d;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
@brief The end of @a conductor which does not touch @a element.
|
||||
|
||||
Which end that is tells where the wire goes on its own sheet: a wire
|
||||
ending on a report arrow points away from it, and that is the end
|
||||
which says something about where it comes from or where it runs to.
|
||||
@param conductor the wire to look at
|
||||
@param element the report arrow it ends on (may be nullptr)
|
||||
@return the scene position of the other end, (0, 0) when there is none
|
||||
*/
|
||||
QPointF farEndPosition(const Conductor *conductor, Element *element)
|
||||
{
|
||||
const Terminal *far = nullptr;
|
||||
if (conductor->terminal1
|
||||
&& conductor->terminal1->parentElement() != element) {
|
||||
far = conductor->terminal1;
|
||||
} else {
|
||||
far = conductor->terminal2 ? conductor->terminal2 : conductor->terminal1;
|
||||
}
|
||||
return far ? far->scenePos() : QPointF();
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Whether wire @a a runs before wire @a b on its own sheet.
|
||||
|
||||
Both are looked at from @a element: what tells them apart is where
|
||||
their other end goes. Putting the wires which come in to a report
|
||||
and the wires which go out of its partner in this order is what
|
||||
pairs a continuation up with the core it belongs to when several
|
||||
cores run through one and the same report.
|
||||
@param a @param b the two wires to compare
|
||||
@param element the report arrow both end on (may be nullptr)
|
||||
@return true when @a a comes first
|
||||
*/
|
||||
bool farBefore(const Conductor *a, const Conductor *b, Element *element)
|
||||
{
|
||||
const QPointF pa = farEndPosition(a, element);
|
||||
const QPointF pb = farEndPosition(b, element);
|
||||
if (pa.y() != pb.y()) return pa.y() < pb.y();
|
||||
if (pa.x() != pb.x()) return pa.x() < pb.x();
|
||||
return a->uuid() < b->uuid();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
@brief CableManager::refreshLabels
|
||||
Rewrite the cable field of every conductor of the project from the
|
||||
cables themselves.
|
||||
|
||||
The rule is one-directional and therefore cannot drift: a conductor
|
||||
which some cable claims gets the label that cable generates, a
|
||||
conductor which no cable claims any more -- because the cable was
|
||||
delete, the core was dragged away or the type lost that core -- has
|
||||
the generated label taken away. A conductor which never belonged to
|
||||
a cable keeps whatever it had, since only one with a reference is
|
||||
touched.
|
||||
|
||||
A core which ends on a folio report goes on over there: the wire
|
||||
coming out of the linked arrow shows the same piece of copper, so
|
||||
it belongs to the same core and carries the same cable field. What
|
||||
the links say is worked out from scratch on every pass, and a wire
|
||||
which some cable already claims is never taken away from it, so the
|
||||
reference appears wherever a report carries a core and disappears
|
||||
again by itself as soon as the cable, the core or the link itself
|
||||
is gone. Chains of reports are followed as far as they go, so a
|
||||
cable may run over any number of sheets.
|
||||
@param project
|
||||
*/
|
||||
void CableManager::refreshLabels(QETProject *project)
|
||||
{
|
||||
if (!project) return;
|
||||
|
||||
struct Binding {
|
||||
QUuid cable;
|
||||
int slot = -1;
|
||||
QString label;
|
||||
};
|
||||
|
||||
QHash<QUuid, Binding> bindings;
|
||||
for (Cable *cable : project->cables())
|
||||
{
|
||||
if (!cable) continue;
|
||||
for (const CableCore &core : cable->usedCores())
|
||||
{
|
||||
if (core.conductor.isNull()) continue;
|
||||
Binding binding;
|
||||
binding.cable = cable->uuid();
|
||||
binding.slot = core.core;
|
||||
binding.label = cable->labelOfCore(core.core);
|
||||
bindings.insert(core.conductor, binding);
|
||||
}
|
||||
}
|
||||
|
||||
//A core which ends on a folio report continues on the wire which
|
||||
//leaves the linked arrow on the other sheet. That wire shows the
|
||||
//same piece of copper, so it gets the same reference as the core
|
||||
//it continues. The walk repeats while it finds new wires, so a
|
||||
//chain of reports is followed across any number of sheets, and
|
||||
//it only ever fills a wire no cable has claimed yet: two cores
|
||||
//can never fight over the same wire, and a reference worked out
|
||||
//here is taken away again by the same pass as soon as the cable,
|
||||
//the core or the link does.
|
||||
QHash<QUuid, Conductor *> conductors_by_uuid;
|
||||
QHash<Element *, QList<Conductor *>> endings_on_report;
|
||||
for (Diagram *diagram : project->diagrams())
|
||||
{
|
||||
if (!diagram) continue;
|
||||
for (Conductor *conductor : diagram->conductors())
|
||||
{
|
||||
if (conductor) conductors_by_uuid.insert(conductor->uuid(), conductor);
|
||||
}
|
||||
}
|
||||
|
||||
//Where a wire goes on its own sheet (farBefore, above): putting the
|
||||
//wires which come in and the wires which go out in the same order
|
||||
//is what pairs a continuation up with its core when several cores
|
||||
//run through one and the same report.
|
||||
|
||||
bool grew = true;
|
||||
while (grew)
|
||||
{
|
||||
grew = false;
|
||||
endings_on_report.clear();
|
||||
for (const QUuid &source_uuid : bindings.keys())
|
||||
{
|
||||
Conductor *source = conductors_by_uuid.value(source_uuid);
|
||||
if (!source) continue;
|
||||
|
||||
Terminal *ends[2] = {source->terminal1, source->terminal2};
|
||||
for (Terminal *end : ends)
|
||||
{
|
||||
if (!end) continue;
|
||||
Element *element = end->parentElement();
|
||||
//An arrow taken off the sheet carries nothing over
|
||||
//any more, whatever still links it
|
||||
if (!element || !element->scene()) continue;
|
||||
if (!(element->linkType() & Element::AllReport)) continue;
|
||||
|
||||
QList<Conductor *> &in = endings_on_report[element];
|
||||
if (!in.contains(source)) in.append(source);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = endings_on_report.constBegin();
|
||||
it != endings_on_report.constEnd(); ++it)
|
||||
{
|
||||
Element *element = it.key();
|
||||
QList<Conductor *> in = it.value();
|
||||
std::sort(in.begin(), in.end(),
|
||||
[&](const Conductor *a, const Conductor *b) {
|
||||
return farBefore(a, b, element);
|
||||
});
|
||||
|
||||
for (Element *partner : element->linkedElements())
|
||||
{
|
||||
if (!partner || partner == element || !partner->scene()) continue;
|
||||
|
||||
QList<Conductor *> out = partner->conductors();
|
||||
std::sort(out.begin(), out.end(),
|
||||
[&](const Conductor *a, const Conductor *b) {
|
||||
return farBefore(a, b, partner);
|
||||
});
|
||||
|
||||
//One core coming in may branch out into several wires
|
||||
//over there -- they all show the same piece of copper.
|
||||
//Several cores coming in take one wire each, in the
|
||||
//order they run on their own sheet.
|
||||
const int count = (in.size() == 1)
|
||||
? out.size()
|
||||
: qMin(in.size(), out.size());
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
Conductor *continued = out.at(i);
|
||||
if (!continued || bindings.contains(continued->uuid())) continue;
|
||||
const int from = qMin(i, in.size() - 1);
|
||||
bindings.insert(continued->uuid(),
|
||||
bindings.value(in.at(from)->uuid()));
|
||||
grew = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Diagram *diagram : project->diagrams())
|
||||
{
|
||||
if (!diagram) continue;
|
||||
for (Conductor *conductor : diagram->conductors())
|
||||
{
|
||||
if (!conductor) continue;
|
||||
|
||||
const auto it = bindings.constFind(conductor->uuid());
|
||||
if (it != bindings.constEnd())
|
||||
{
|
||||
const ConductorProperties &properties = conductor->properties();
|
||||
conductor->setCableReference(it->label, it->cable, it->slot);
|
||||
}
|
||||
else if (!conductor->properties().m_cable_uuid.isNull())
|
||||
{
|
||||
conductor->setCableReference(QString(), QUuid(), -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CableManager::claimConductor
|
||||
Make a conductor a core of @a new_owner, taking it away from whichever
|
||||
cable held it before. A conductor is a single piece of wire: two
|
||||
cables cannot both claim it, and the one that loses it must not go
|
||||
on drawing a label for it.
|
||||
@param project
|
||||
@param conductor
|
||||
@param new_owner the cable which is about to use the conductor
|
||||
*/
|
||||
void CableManager::claimConductor(QETProject *project, Conductor *conductor, Cable *new_owner)
|
||||
{
|
||||
if (!project || !conductor) return;
|
||||
|
||||
const QUuid owner_uuid = conductor->properties().m_cable_uuid;
|
||||
if (owner_uuid.isNull() || (new_owner && owner_uuid == new_owner->uuid())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Cable *previous = cableByUuid(project, owner_uuid)) {
|
||||
previous->removeCore(conductor->properties().m_cable_slot);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CableManager::cableByUuid
|
||||
@param project
|
||||
@param uuid
|
||||
@return the cable with that identity, or nullptr
|
||||
*/
|
||||
Cable *CableManager::cableByUuid(QETProject *project, const QUuid &uuid)
|
||||
{
|
||||
if (!project || uuid.isNull()) return nullptr;
|
||||
for (Cable *cable : project->cables()) {
|
||||
if (cable && cable->uuid() == uuid) return cable;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CableManager::conductorByUuid
|
||||
@param project
|
||||
@param uuid
|
||||
@return the conductor with that identity, or nullptr. Needed because
|
||||
a cable remembers its cores by identity: only the drawing knows where
|
||||
they ended up.
|
||||
*/
|
||||
Conductor *CableManager::conductorByUuid(QETProject *project, const QUuid &uuid)
|
||||
{
|
||||
if (!project || uuid.isNull()) return nullptr;
|
||||
for (Diagram *diagram : project->diagrams()) {
|
||||
if (!diagram) continue;
|
||||
for (Conductor *conductor : diagram->conductors()) {
|
||||
if (conductor && conductor->uuid() == uuid) return conductor;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CableManager::nextDesignation
|
||||
Work out the number of the next cable drawn on a folio.
|
||||
|
||||
When the project has a numbering rule for cables, that rule decides:
|
||||
whatever it holds at this moment is the number, read by the very
|
||||
same machinery the element numbering reads its own. When it has
|
||||
none, the plain letter W is handed out: a project which numbers its
|
||||
cables by no rule gets no numbers invented for it, and W -- the one
|
||||
part of the old numbers which was never a number -- is all such a
|
||||
cable is called until a rule is defined.
|
||||
|
||||
A rule whose counter belongs to one folio is read from the beginning
|
||||
of the folio the cable is drawn on, so that the number this cable
|
||||
gets is the first free one of its own folio rather than whatever the
|
||||
project's rule happens to hold after numbering other folios.
|
||||
|
||||
Nothing here moves the project's own rule on: that belongs to the
|
||||
moment the cable really comes into being.
|
||||
@param project
|
||||
@param diagram the folio the cable is being drawn on
|
||||
@return the number
|
||||
*/
|
||||
QString CableManager::nextDesignation(QETProject *project, Diagram *diagram)
|
||||
{
|
||||
NumerotationContext context;
|
||||
if (project) {
|
||||
context = project->cableAutoNum(project->cableCurrentAutoNum());
|
||||
}
|
||||
//The folio-bound parts are put back to the start of that folio;
|
||||
//the stepping of the overload below then walks past every
|
||||
//number of this folio which is already taken
|
||||
context = autonum::resetFolioCounters(context);
|
||||
return nextDesignation(project, diagram, context);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief CableManager::nextDesignation
|
||||
Work the number of a cable out of a rule which the caller keeps in
|
||||
@p context -- the project's own rule as it stands, or a copy of it
|
||||
which the caller is numbering a whole run of cables with.
|
||||
|
||||
Whatever the rule holds is the number, unless some cable already
|
||||
stands under it: then @p context is stepped on and the rule is read
|
||||
once more, so that a paste which brings in several cables at once
|
||||
numbers each of them differently although the project's own rule
|
||||
does not move until those cables really exist. A rule which cannot
|
||||
step -- one with nothing in it which counts -- hands out what it
|
||||
says and says no more, for what it says is all it knows.
|
||||
|
||||
A project which numbers its cables by no rule is not read at all
|
||||
and is handed the plain letter W instead: no numbers are invented
|
||||
where none have been defined.
|
||||
@param project
|
||||
@param diagram the folio the cable is being drawn on, which every
|
||||
folio-bound part of a rule is read from
|
||||
@param context the rule to read; stepped on when what it says is
|
||||
already taken
|
||||
@return the number
|
||||
*/
|
||||
QString CableManager::nextDesignation(QETProject *project,
|
||||
Diagram *diagram,
|
||||
NumerotationContext &context)
|
||||
{
|
||||
if (!project || !diagram) return QString();
|
||||
|
||||
const QString key = project->cableCurrentAutoNum();
|
||||
//Whether there was a rule to read at all, taken down before the
|
||||
//loop: stepping the rule on empties it, and that must not look
|
||||
//like a project which never had one.
|
||||
const bool ruled = !key.isEmpty() && !context.isEmpty();
|
||||
QString previous;
|
||||
|
||||
for (int attempt = 0; ruled && attempt < 1000 && !context.isEmpty(); ++attempt)
|
||||
{
|
||||
//The rule is read on a copy: reading it fills in what its
|
||||
//folio-bound parts are worth here and now, and that must
|
||||
//not creep into the rule itself.
|
||||
NumerotationContext evaluated = context;
|
||||
const QString formula = autonum::numerotationContextToFormula(evaluated);
|
||||
autonum::sequentialNumbers seq;
|
||||
autonum::setSequential(formula, seq, evaluated, diagram, key);
|
||||
const QString candidate =
|
||||
autonum::AssignVariables::formulaToLabel(formula, seq, diagram);
|
||||
|
||||
if (candidate.isEmpty()) break;
|
||||
if (!designationIsTaken(project, candidate)) return candidate;
|
||||
|
||||
//Taken already: step the rule on and read it once more.
|
||||
if (candidate == previous) return candidate;
|
||||
previous = candidate;
|
||||
|
||||
NumerotationContextCommands ncc(context, diagram);
|
||||
const NumerotationContext following = ncc.next();
|
||||
if (following.size() != context.size()) break;
|
||||
context = following;
|
||||
}
|
||||
|
||||
//No rule at all: the plain letter W, and nothing more. The old
|
||||
//numbers read folio + W + index, and W is the only part of them
|
||||
//which was never a number -- so it is what a project which has
|
||||
//defined no numbering rule calls its cables, until it defines
|
||||
//one.
|
||||
if (!ruled) {
|
||||
return QStringLiteral("W");
|
||||
}
|
||||
|
||||
//A rule which says nothing usable here: no number at all. The
|
||||
//number is what the rule says, and a rule which says nothing
|
||||
//leaves the field to the user rather than having it filled in
|
||||
//with something it never held.
|
||||
return QString();
|
||||
}
|
||||
Reference in New Issue
Block a user