mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-13 10:04:13 +02:00
Merge pull request #700 from Kellermorph/update-auto-break
Follow up Auto-break conductors
This commit is contained in:
@@ -298,6 +298,9 @@ set(QET_SRC_FILES
|
||||
${QET_DIR}/sources/dataBase/ui/summaryquerywidget.cpp
|
||||
${QET_DIR}/sources/dataBase/ui/summaryquerywidget.h
|
||||
|
||||
${QET_DIR}/sources/autobreakconductor.cpp
|
||||
${QET_DIR}/sources/autobreakconductor.h
|
||||
|
||||
${QET_DIR}/sources/diagramevent/diagrameventaddelement.cpp
|
||||
${QET_DIR}/sources/diagramevent/diagrameventaddelement.h
|
||||
${QET_DIR}/sources/diagramevent/diagrameventaddimage.cpp
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
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 "autobreakconductor.h"
|
||||
|
||||
#include "diagram.h"
|
||||
#include "qetproject.h"
|
||||
#include "conductorautonumerotation.h"
|
||||
#include "qetgraphicsitem/element.h"
|
||||
#include "qetgraphicsitem/terminal.h"
|
||||
#include "qetgraphicsitem/conductor.h"
|
||||
#include "undocommand/addgraphicsobjectcommand.h"
|
||||
#include "undocommand/deleteqgraphicsitemcommand.h"
|
||||
|
||||
#include <QPainterPath>
|
||||
#include <QMap>
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
@brief distanceToSegment
|
||||
@param point : point to measure from
|
||||
@param segment : the finite segment (not the infinite line through it)
|
||||
@return the distance from @a point to the closest point actually on
|
||||
@a segment. Unlike QET::orthogonalProjection(), which reports a hit
|
||||
for any point on the segment's infinite extension, this clamps the
|
||||
projection to the segment itself: a point collinear with a wire but
|
||||
past its actual drawn end is correctly reported as far away, not "on"
|
||||
the wire.
|
||||
*/
|
||||
qreal distanceToSegment(const QPointF &point, const QLineF &segment)
|
||||
{
|
||||
const QPointF a = segment.p1();
|
||||
const QPointF b = segment.p2();
|
||||
const QPointF ab = b - a;
|
||||
const qreal len2 = QPointF::dotProduct(ab, ab);
|
||||
|
||||
if (len2 <= 0.0)
|
||||
return QLineF(point, a).length();
|
||||
|
||||
qreal t = QPointF::dotProduct(point - a, ab) / len2;
|
||||
t = qBound(0.0, t, 1.0);
|
||||
return QLineF(point, a + t * ab).length();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
/**
|
||||
@brief autoBreakConductors
|
||||
For each terminal of @a element, check if its dock point lies on an
|
||||
existing conductor. If so, break the conductor and reconnect through
|
||||
the element's terminal. Broken conductors from the same circuit
|
||||
(sharing the same far-end endpoint) are broken together; independent
|
||||
crossing conductors are left untouched.
|
||||
*/
|
||||
QSet<Terminal *> autoBreakConductors(
|
||||
Diagram *diagram,
|
||||
Element *element,
|
||||
QUndoCommand *parent,
|
||||
QList<Conductor *> &conductors_handled,
|
||||
QSet<Terminal *> &used_terminals)
|
||||
{
|
||||
QSet<Terminal *> broken_endpoints;
|
||||
|
||||
if (!diagram->project()->autoBreakConductor())
|
||||
return broken_endpoints;
|
||||
|
||||
foreach (Terminal *t, element->terminals())
|
||||
{
|
||||
if (used_terminals.contains(t))
|
||||
continue;
|
||||
|
||||
QPointF t_dock = t->dockConductor();
|
||||
|
||||
struct ConductorMatch {
|
||||
Conductor *conductor;
|
||||
Terminal *connect_to;
|
||||
Terminal *other;
|
||||
};
|
||||
QList<ConductorMatch> all_matches;
|
||||
|
||||
foreach (Conductor *c, diagram->conductors())
|
||||
{
|
||||
if (conductors_handled.contains(c) ||
|
||||
c->terminal1->parentElement() == element ||
|
||||
c->terminal2->parentElement() == element)
|
||||
continue;
|
||||
|
||||
QPointF local_dock = c->mapFromScene(t_dock);
|
||||
bool point_on_conductor = false;
|
||||
QPainterPath path = c->path();
|
||||
for (int i = 0; i < path.elementCount() - 1; ++i)
|
||||
{
|
||||
const QPainterPath::Element &e1 = path.elementAt(i);
|
||||
const QPainterPath::Element &e2 = path.elementAt(i + 1);
|
||||
QLineF segment(QPointF(e1.x, e1.y), QPointF(e2.x, e2.y));
|
||||
if (distanceToSegment(local_dock, segment) < 5.0)
|
||||
{
|
||||
point_on_conductor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!point_on_conductor)
|
||||
continue;
|
||||
|
||||
Terminal *c1 = c->terminal1;
|
||||
Terminal *c2 = c->terminal2;
|
||||
QPointF c1_dock = c1->dockConductor();
|
||||
QPointF c2_dock = c2->dockConductor();
|
||||
|
||||
Terminal *connect_to = nullptr;
|
||||
Terminal *other = nullptr;
|
||||
|
||||
switch (t->orientation()) {
|
||||
case Qet::North:
|
||||
if (c1_dock.y() < t_dock.y()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.y() < t_dock.y()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
case Qet::South:
|
||||
if (c1_dock.y() > t_dock.y()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.y() > t_dock.y()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
case Qet::East:
|
||||
if (c1_dock.x() > t_dock.x()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.x() > t_dock.x()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
case Qet::West:
|
||||
if (c1_dock.x() < t_dock.x()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.x() < t_dock.x()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
}
|
||||
|
||||
if (!connect_to) {
|
||||
qreal d1 = QLineF(t_dock, c1_dock).length();
|
||||
qreal d2 = QLineF(t_dock, c2_dock).length();
|
||||
if (d1 <= d2) { connect_to = c1; other = c2; }
|
||||
else { connect_to = c2; other = c1; }
|
||||
}
|
||||
|
||||
all_matches.append({c, connect_to, other});
|
||||
}
|
||||
|
||||
if (all_matches.isEmpty())
|
||||
continue;
|
||||
|
||||
QMap<Terminal *, QList<ConductorMatch>> groups;
|
||||
for (const auto &m : all_matches) {
|
||||
groups[m.other].append(m);
|
||||
}
|
||||
Terminal *best_other = nullptr;
|
||||
int best_count = 0;
|
||||
for (auto it = groups.constBegin(); it != groups.constEnd(); ++it) {
|
||||
if (it.value().size() > best_count) {
|
||||
best_count = it.value().size();
|
||||
best_other = it.key();
|
||||
}
|
||||
}
|
||||
|
||||
const QList<ConductorMatch> &matches = groups[best_other];
|
||||
|
||||
used_terminals.insert(t);
|
||||
|
||||
DiagramContent content;
|
||||
for (const auto &m : matches) {
|
||||
content.m_other_conductors.append(m.conductor);
|
||||
conductors_handled.append(m.conductor);
|
||||
}
|
||||
new DeleteQGraphicsItemCommand(diagram, content, parent);
|
||||
|
||||
for (const auto &m : matches) {
|
||||
Conductor *new_c = new Conductor(m.connect_to, t);
|
||||
new AddGraphicsObjectCommand(new_c, diagram, QPointF(), parent);
|
||||
ConductorAutoNumerotation can(new_c, diagram, parent);
|
||||
can.numerate();
|
||||
if (diagram->freezeNewConductors() || diagram->project()->isFreezeNewConductors())
|
||||
new_c->setFreezeLabel(true);
|
||||
|
||||
broken_endpoints.insert(m.connect_to);
|
||||
}
|
||||
|
||||
QPointF other_dock = best_other->dockConductor();
|
||||
Terminal *other_terminal = nullptr;
|
||||
qreal best_dist = std::numeric_limits<qreal>::max();
|
||||
foreach (Terminal *ot, element->terminals())
|
||||
{
|
||||
if (used_terminals.contains(ot))
|
||||
continue;
|
||||
|
||||
QPointF ot_dock = ot->dockConductor();
|
||||
bool orientation_ok = false;
|
||||
switch (ot->orientation()) {
|
||||
case Qet::North: orientation_ok = other_dock.y() < ot_dock.y(); break;
|
||||
case Qet::South: orientation_ok = other_dock.y() > ot_dock.y(); break;
|
||||
case Qet::East: orientation_ok = other_dock.x() > ot_dock.x(); break;
|
||||
case Qet::West: orientation_ok = other_dock.x() < ot_dock.x(); break;
|
||||
}
|
||||
if (!orientation_ok)
|
||||
continue;
|
||||
|
||||
qreal dist = QLineF(ot_dock, other_dock).length();
|
||||
if (dist < best_dist) {
|
||||
best_dist = dist;
|
||||
other_terminal = ot;
|
||||
}
|
||||
}
|
||||
|
||||
if (other_terminal) {
|
||||
Conductor *new_c2 = new Conductor(best_other, other_terminal);
|
||||
new AddGraphicsObjectCommand(new_c2, diagram, QPointF(), parent);
|
||||
ConductorAutoNumerotation can2(new_c2, diagram, parent);
|
||||
can2.numerate();
|
||||
if (diagram->freezeNewConductors() || diagram->project()->isFreezeNewConductors())
|
||||
new_c2->setFreezeLabel(true);
|
||||
|
||||
broken_endpoints.insert(best_other);
|
||||
used_terminals.insert(other_terminal);
|
||||
}
|
||||
}
|
||||
|
||||
return broken_endpoints;
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#ifndef AUTOBREAKCONDUCTOR_H
|
||||
#define AUTOBREAKCONDUCTOR_H
|
||||
|
||||
#include <QSet>
|
||||
#include <QList>
|
||||
|
||||
class Diagram;
|
||||
class Element;
|
||||
class Terminal;
|
||||
class Conductor;
|
||||
class QUndoCommand;
|
||||
|
||||
/**
|
||||
@brief autoBreakConductors
|
||||
For each terminal of @a element, check if its dock point lies on an
|
||||
existing conductor. If so, break the conductor and reconnect through the
|
||||
element's terminal. Broken conductors from the same circuit (sharing the
|
||||
same far-end endpoint) are broken together; independent crossing
|
||||
conductors are left untouched.
|
||||
@param diagram the diagram containing the conductors
|
||||
@param element the element whose terminals trigger breaks
|
||||
@param parent undo command under which break/reconnect
|
||||
sub-commands are created
|
||||
@param conductors_handled shared list of conductors already claimed by
|
||||
a previous call in the same batch (prevents
|
||||
double-processing when multiple elements are
|
||||
broken in one pass)
|
||||
@param used_terminals shared set of terminals already used as
|
||||
connect_to or other_terminal in the same batch
|
||||
@return set of terminals that were connected to a broken conductor's
|
||||
far-end (useful for preventing duplicate auto-connect)
|
||||
*/
|
||||
QSet<Terminal *> autoBreakConductors(
|
||||
Diagram *diagram,
|
||||
Element *element,
|
||||
QUndoCommand *parent,
|
||||
QList<Conductor *> &conductors_handled,
|
||||
QSet<Terminal *> &used_terminals);
|
||||
|
||||
#endif // AUTOBREAKCONDUCTOR_H
|
||||
@@ -17,7 +17,9 @@
|
||||
*/
|
||||
#include "diagramcommands.h"
|
||||
|
||||
#include "autobreakconductor.h"
|
||||
#include "diagram.h"
|
||||
#include "qetproject.h"
|
||||
#include "qetgraphicsitem/conductortextitem.h"
|
||||
#include "qetgraphicsitem/element.h"
|
||||
#include "qetgraphicsitem/elementtextitemgroup.h"
|
||||
@@ -48,6 +50,7 @@ PasteDiagramCommand::PasteDiagramCommand( Diagram *dia, const DiagramContent &c,
|
||||
PasteDiagramCommand::~PasteDiagramCommand()
|
||||
{
|
||||
diagram -> qgiManager().release(content.items(filter));
|
||||
delete m_break_cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,6 +61,10 @@ void PasteDiagramCommand::undo()
|
||||
{
|
||||
diagram -> showMe();
|
||||
|
||||
//Undo auto-break before removing items, so terminals are still on scene
|
||||
if (m_break_cmd)
|
||||
m_break_cmd->undo();
|
||||
|
||||
foreach(QGraphicsItem *item, content.items(filter))
|
||||
diagram->removeItem(item);
|
||||
}
|
||||
@@ -102,6 +109,27 @@ void PasteDiagramCommand::redo()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Auto-break conductors on first paste. Items are already on the
|
||||
//scene at this point (added before this command was created).
|
||||
if (diagram->project()->autoBreakConductor())
|
||||
{
|
||||
m_break_cmd = new QUndoCommand();
|
||||
QList<Conductor *> conductors_handled;
|
||||
QSet<Terminal *> used_terminals;
|
||||
for (Element *e : content.m_elements) {
|
||||
autoBreakConductors(diagram, e, m_break_cmd,
|
||||
conductors_handled, used_terminals);
|
||||
}
|
||||
if (m_break_cmd->childCount() == 0) {
|
||||
delete m_break_cmd;
|
||||
m_break_cmd = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_break_cmd->redo();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -109,6 +137,10 @@ void PasteDiagramCommand::redo()
|
||||
for (QGraphicsItem *item : qgis_list) {
|
||||
diagram->addItem(item);
|
||||
}
|
||||
|
||||
//Re-execute the stored break commands (items are back on scene)
|
||||
if (m_break_cmd)
|
||||
m_break_cmd->redo();
|
||||
}
|
||||
|
||||
const QList<QGraphicsItem *> qgis_list = content.items();
|
||||
|
||||
@@ -53,6 +53,8 @@ class PasteDiagramCommand : public QUndoCommand {
|
||||
int filter;
|
||||
/// prevent the first call to redo()
|
||||
bool first_redo;
|
||||
/// auto-break conductor sub-commands (created on first redo)
|
||||
QUndoCommand *m_break_cmd = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,37 +28,10 @@
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../qetgraphicsitem/terminal.h"
|
||||
#include "../qet.h"
|
||||
#include "../autobreakconductor.h"
|
||||
#include <QPainterPath>
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
/**
|
||||
@brief distanceToSegment
|
||||
@param point : point to measure from
|
||||
@param segment : the finite segment (not the infinite line through it)
|
||||
@return the distance from @a point to the closest point actually on
|
||||
@a segment. Unlike QET::orthogonalProjection(), which reports a hit
|
||||
for any point on the segment's infinite extension, this clamps the
|
||||
projection to the segment itself: a point collinear with a wire but
|
||||
past its actual drawn end is correctly reported as far away, not "on"
|
||||
the wire.
|
||||
*/
|
||||
qreal distanceToSegment(const QPointF &point, const QLineF &segment)
|
||||
{
|
||||
const QPointF a = segment.p1();
|
||||
const QPointF b = segment.p2();
|
||||
const QPointF ab = b - a;
|
||||
const qreal len2 = QPointF::dotProduct(ab, ab);
|
||||
|
||||
if (len2 <= 0.0)
|
||||
return QLineF(point, a).length();
|
||||
|
||||
qreal t = QPointF::dotProduct(point - a, ab) / len2;
|
||||
t = qBound(0.0, t, 1.0);
|
||||
return QLineF(point, a + t * ab).length();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief DiagramEventAddElement::DiagramEventAddElement
|
||||
Defaut constructor
|
||||
@@ -292,191 +265,10 @@ void DiagramEventAddElement::addElement()
|
||||
//Auto break conductor: if a terminal of the new element lies on an existing
|
||||
//conductor, break the conductor and reconnect through the new element's terminal.
|
||||
//Track the endpoints of broken conductors so auto-connect doesn't create duplicates.
|
||||
QSet<Terminal *> broken_endpoints;
|
||||
|
||||
if (m_diagram->project()->autoBreakConductor())
|
||||
{
|
||||
//Track which conductors we already handled for this element
|
||||
QList<Conductor *> conductors_handled;
|
||||
//Track which terminals of the new element are already used (by break or other-connect)
|
||||
QSet<Terminal *> used_terminals;
|
||||
|
||||
foreach (Terminal *t, element->terminals())
|
||||
{
|
||||
//Skip terminals already used by a previous break+reconnect (other_terminal)
|
||||
if (used_terminals.contains(t))
|
||||
continue;
|
||||
|
||||
QPointF t_dock = t->dockConductor();
|
||||
|
||||
//Collect all conductors that pass through this dock point.
|
||||
struct ConductorMatch {
|
||||
Conductor *conductor;
|
||||
Terminal *connect_to; //endpoint "before" the dock point (based on orientation)
|
||||
Terminal *other; //endpoint "after" the dock point
|
||||
};
|
||||
QList<ConductorMatch> all_matches;
|
||||
|
||||
foreach (Conductor *c, m_diagram->conductors())
|
||||
{
|
||||
//Skip conductors we already handled or connected to the new element
|
||||
if (conductors_handled.contains(c) ||
|
||||
c->terminal1->parentElement() == element ||
|
||||
c->terminal2->parentElement() == element)
|
||||
continue;
|
||||
|
||||
//Check if dock point lies on the conductor path. Distance is
|
||||
//measured to the segment itself (clamped), not the infinite
|
||||
//line through it -- see distanceToSegment().
|
||||
QPointF local_dock = c->mapFromScene(t_dock);
|
||||
bool point_on_conductor = false;
|
||||
QPainterPath path = c->path();
|
||||
for (int i = 0; i < path.elementCount() - 1; ++i)
|
||||
{
|
||||
const QPainterPath::Element &e1 = path.elementAt(i);
|
||||
const QPainterPath::Element &e2 = path.elementAt(i + 1);
|
||||
QLineF segment(QPointF(e1.x, e1.y), QPointF(e2.x, e2.y));
|
||||
if (distanceToSegment(local_dock, segment) < 5.0)
|
||||
{
|
||||
point_on_conductor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!point_on_conductor)
|
||||
continue;
|
||||
|
||||
Terminal *c1 = c->terminal1;
|
||||
Terminal *c2 = c->terminal2;
|
||||
QPointF c1_dock = c1->dockConductor();
|
||||
QPointF c2_dock = c2->dockConductor();
|
||||
|
||||
Terminal *connect_to = nullptr;
|
||||
Terminal *other = nullptr;
|
||||
|
||||
switch (t->orientation()) {
|
||||
case Qet::North:
|
||||
if (c1_dock.y() < t_dock.y()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.y() < t_dock.y()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
case Qet::South:
|
||||
if (c1_dock.y() > t_dock.y()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.y() > t_dock.y()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
case Qet::East:
|
||||
if (c1_dock.x() > t_dock.x()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.x() > t_dock.x()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
case Qet::West:
|
||||
if (c1_dock.x() < t_dock.x()) { connect_to = c1; other = c2; }
|
||||
else if (c2_dock.x() < t_dock.x()) { connect_to = c2; other = c1; }
|
||||
break;
|
||||
}
|
||||
|
||||
if (!connect_to) {
|
||||
qreal d1 = QLineF(t_dock, c1_dock).length();
|
||||
qreal d2 = QLineF(t_dock, c2_dock).length();
|
||||
if (d1 <= d2) { connect_to = c1; other = c2; }
|
||||
else { connect_to = c2; other = c1; }
|
||||
}
|
||||
|
||||
all_matches.append({c, connect_to, other});
|
||||
}
|
||||
|
||||
if (all_matches.isEmpty())
|
||||
continue;
|
||||
|
||||
//Group matches by their "other" endpoint and find the largest group.
|
||||
//This ensures that when multiple independent circuits cross at the
|
||||
//same dock point, the group with the most conductors gets priority,
|
||||
//not whichever happens to have the nearest connect_to endpoint.
|
||||
QMap<Terminal *, QList<ConductorMatch>> groups;
|
||||
for (const auto &m : all_matches) {
|
||||
groups[m.other].append(m);
|
||||
}
|
||||
Terminal *best_other = nullptr;
|
||||
int best_count = 0;
|
||||
for (auto it = groups.constBegin(); it != groups.constEnd(); ++it) {
|
||||
if (it.value().size() > best_count) {
|
||||
best_count = it.value().size();
|
||||
best_other = it.key();
|
||||
}
|
||||
}
|
||||
|
||||
//Only break conductors in the largest group (same "other" endpoint).
|
||||
//This prevents bridging independent nets: if two unrelated conductors
|
||||
//cross at a dock point, only the larger group gets broken.
|
||||
const QList<ConductorMatch> &matches = groups[best_other];
|
||||
|
||||
//Mark terminal as used
|
||||
used_terminals.insert(t);
|
||||
|
||||
//Delete all matched conductors in one batch
|
||||
DiagramContent content;
|
||||
for (const auto &m : matches) {
|
||||
content.m_other_conductors.append(m.conductor);
|
||||
conductors_handled.append(m.conductor);
|
||||
}
|
||||
new DeleteQGraphicsItemCommand(m_diagram, content, undo_object);
|
||||
|
||||
//Create new conductors from each connect_to endpoint to this terminal.
|
||||
//This creates junctions at the terminal when multiple conductors
|
||||
//from the same circuit pass through the dock point (e.g. left and
|
||||
//right conductors going to the same terminal strip terminal).
|
||||
for (const auto &m : matches) {
|
||||
Conductor *new_c = new Conductor(m.connect_to, t);
|
||||
new AddGraphicsObjectCommand(new_c, m_diagram, QPointF(), undo_object);
|
||||
ConductorAutoNumerotation can(new_c, m_diagram, undo_object);
|
||||
can.numerate();
|
||||
if (m_diagram->freezeNewConductors() || m_diagram->project()->isFreezeNewConductors())
|
||||
new_c->setFreezeLabel(true);
|
||||
|
||||
broken_endpoints.insert(m.connect_to);
|
||||
}
|
||||
|
||||
//Connect the shared "other" endpoint to the nearest free terminal
|
||||
//with matching orientation.
|
||||
QPointF other_dock = best_other->dockConductor();
|
||||
Terminal *other_terminal = nullptr;
|
||||
qreal best_dist = std::numeric_limits<qreal>::max();
|
||||
foreach (Terminal *ot, element->terminals())
|
||||
{
|
||||
if (used_terminals.contains(ot))
|
||||
continue;
|
||||
|
||||
//Check that the other_dock approaches from the correct direction
|
||||
//relative to the candidate terminal's orientation.
|
||||
QPointF ot_dock = ot->dockConductor();
|
||||
bool orientation_ok = false;
|
||||
switch (ot->orientation()) {
|
||||
case Qet::North: orientation_ok = other_dock.y() < ot_dock.y(); break;
|
||||
case Qet::South: orientation_ok = other_dock.y() > ot_dock.y(); break;
|
||||
case Qet::East: orientation_ok = other_dock.x() > ot_dock.x(); break;
|
||||
case Qet::West: orientation_ok = other_dock.x() < ot_dock.x(); break;
|
||||
}
|
||||
if (!orientation_ok)
|
||||
continue;
|
||||
|
||||
qreal dist = QLineF(ot_dock, other_dock).length();
|
||||
if (dist < best_dist) {
|
||||
best_dist = dist;
|
||||
other_terminal = ot;
|
||||
}
|
||||
}
|
||||
|
||||
if (other_terminal) {
|
||||
Conductor *new_c2 = new Conductor(best_other, other_terminal);
|
||||
new AddGraphicsObjectCommand(new_c2, m_diagram, QPointF(), undo_object);
|
||||
ConductorAutoNumerotation can2(new_c2, m_diagram, undo_object);
|
||||
can2.numerate();
|
||||
if (m_diagram->freezeNewConductors() || m_diagram->project()->isFreezeNewConductors())
|
||||
new_c2->setFreezeLabel(true);
|
||||
|
||||
broken_endpoints.insert(best_other);
|
||||
used_terminals.insert(other_terminal);
|
||||
}
|
||||
}
|
||||
}
|
||||
QList<Conductor *> conductors_handled;
|
||||
QSet<Terminal *> used_terminals;
|
||||
QSet<Terminal *> broken_endpoints = autoBreakConductors(m_diagram, element, undo_object,
|
||||
conductors_handled, used_terminals);
|
||||
|
||||
//Auto-connect: collect all aligned pairs first, then filter and process.
|
||||
QList<QPair<Terminal *, Terminal *>> aligned_pairs;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
#include "elementsmover.h"
|
||||
#include "qetproject.h"
|
||||
#include "autobreakconductor.h"
|
||||
#include "conductorautonumerotation.h"
|
||||
#include "diagram.h"
|
||||
#include "qetgraphicsitem/conductor.h"
|
||||
@@ -178,6 +179,19 @@ void ElementsMover::endMovement()
|
||||
undo_object->setText(quc->text());
|
||||
}
|
||||
|
||||
//Auto-break conductors: for each moved element, break any conductor
|
||||
//whose path passes through a terminal dock point, and reconnect through
|
||||
//the element. The element is already at its final position on screen.
|
||||
if (m_diagram->project()->autoBreakConductor())
|
||||
{
|
||||
QList<Conductor *> conductors_handled;
|
||||
QSet<Terminal *> used_terminals;
|
||||
for (Element *e : m_moved_content.m_elements) {
|
||||
autoBreakConductors(m_diagram, e, undo_object,
|
||||
conductors_handled, used_terminals);
|
||||
}
|
||||
}
|
||||
|
||||
//There is only one element moved, and project authorize auto conductor,
|
||||
//we try auto connection of conductor;
|
||||
typedef DiagramContent dc;
|
||||
|
||||
Reference in New Issue
Block a user