mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-08-13 10:04:13 +02:00
Auto-break conductor
This commit is contained in:
@@ -20,11 +20,16 @@
|
||||
#include "../conductorautonumerotation.h"
|
||||
#include "../diagram.h"
|
||||
#include "../undocommand/addgraphicsobjectcommand.h"
|
||||
#include "../undocommand/deleteqgraphicsitemcommand.h"
|
||||
#include "../factory/elementfactory.h"
|
||||
#include "../qetapp.h"
|
||||
#include "../qetdiagrameditor.h"
|
||||
#include "../qetgraphicsitem/element.h"
|
||||
#include "../qetgraphicsitem/conductor.h"
|
||||
#include "../qetgraphicsitem/terminal.h"
|
||||
#include "../qet.h"
|
||||
#include <QPainterPath>
|
||||
#include <limits>
|
||||
|
||||
/**
|
||||
@brief DiagramEventAddElement::DiagramEventAddElement
|
||||
@@ -248,16 +253,171 @@ void DiagramEventAddElement::addElement()
|
||||
QUndoCommand *undo_object = new QUndoCommand(tr("Ajouter %1").arg(element->name()));
|
||||
new AddGraphicsObjectCommand(element, m_diagram, m_element -> pos(), undo_object);
|
||||
|
||||
//When we search for free aligned terminal we temporally remove m_element to
|
||||
//avoid any interaction with the function Element::AlignedFreeTerminals
|
||||
//This is useful when an element has two (or more) terminals on opposite sides,
|
||||
//because m_element is exactly at the same pos of the new element
|
||||
//added to the scene so new conductor are created between terminal of the new element
|
||||
//and the opposite terminal of m_element.
|
||||
//When we search for free aligned terminal we temporally remove m_element to
|
||||
//avoid any interaction with the function Element::AlignedFreeTerminals
|
||||
//This is useful when an element has two (or more) terminals on opposite sides,
|
||||
//because m_element is exactly at the same pos of the new element
|
||||
//added to the scene so new conductor are created between terminal of the new element
|
||||
//and the opposite terminal of m_element.
|
||||
m_diagram->removeItem(m_element);
|
||||
while (!element -> AlignedFreeTerminals().isEmpty() && m_diagram -> project() -> autoConductor())
|
||||
|
||||
//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())
|
||||
{
|
||||
QPair <Terminal *, Terminal *> pair = element -> AlignedFreeTerminals().takeFirst();
|
||||
//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())
|
||||
{
|
||||
QPointF t_dock = t->dockConductor();
|
||||
|
||||
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.
|
||||
//Convert dock_point to the conductor's local coordinate system,
|
||||
//because c->path() is in local coordinates (generated via mapFromScene).
|
||||
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));
|
||||
QPointF projection;
|
||||
if (QET::orthogonalProjection(local_dock, segment, &projection))
|
||||
{
|
||||
qreal dist = QLineF(local_dock, projection).length();
|
||||
if (dist < 5.0)
|
||||
{
|
||||
point_on_conductor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!point_on_conductor)
|
||||
continue;
|
||||
|
||||
//The terminal lies on this conductor.
|
||||
//Break it: delete old conductor, create one new conductor from the
|
||||
//aligned endpoint to the new terminal. The other endpoint is left
|
||||
//for auto-connect to handle (e.g. connecting to the opposite terminal
|
||||
//of the new element).
|
||||
Terminal *c1 = c->terminal1;
|
||||
Terminal *c2 = c->terminal2;
|
||||
|
||||
conductors_handled.append(c);
|
||||
|
||||
//Get scene positions for endpoint selection
|
||||
QPointF c1_dock = c1->dockConductor();
|
||||
QPointF c2_dock = c2->dockConductor();
|
||||
|
||||
//Determine which endpoint to connect based on terminal orientation.
|
||||
//Terminal::orientation() already accounts for element rotation,
|
||||
//so a North terminal rotated 90° returns East, etc.
|
||||
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;
|
||||
}
|
||||
|
||||
//Fallback: use nearest endpoint
|
||||
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; }
|
||||
}
|
||||
|
||||
//Delete the old conductor
|
||||
DiagramContent content;
|
||||
content.m_other_conductors.append(c);
|
||||
new DeleteQGraphicsItemCommand(m_diagram, content, undo_object);
|
||||
|
||||
//Create new conductor from the aligned endpoint to the new terminal
|
||||
Conductor *new_c = new Conductor(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(connect_to);
|
||||
used_terminals.insert(t);
|
||||
|
||||
//Also connect the 'other' endpoint to preserve bent conductor segments.
|
||||
//For L-shaped conductors (e.g. horizontal + vertical), the 'other' endpoint
|
||||
//may be far from the new element. Find the nearest free terminal of the new
|
||||
//element and create a conductor to it.
|
||||
QPointF other_dock = 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;
|
||||
|
||||
qreal dist = QLineF(ot->dockConductor(), other_dock).length();
|
||||
if (dist < best_dist) {
|
||||
best_dist = dist;
|
||||
other_terminal = ot;
|
||||
}
|
||||
}
|
||||
|
||||
if (other_terminal) {
|
||||
Conductor *new_c2 = new Conductor(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(other);
|
||||
used_terminals.insert(other_terminal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Auto-connect: collect all aligned pairs first, then filter and process.
|
||||
QList<QPair<Terminal *, Terminal *>> aligned_pairs;
|
||||
if (m_diagram->project()->autoConductor())
|
||||
aligned_pairs = element->AlignedFreeTerminals();
|
||||
|
||||
for (const QPair<Terminal *, Terminal *> &pair : aligned_pairs)
|
||||
{
|
||||
//Skip if the other terminal was an endpoint of a broken conductor
|
||||
if (broken_endpoints.contains(pair.second))
|
||||
continue;
|
||||
|
||||
Conductor *conductor = new Conductor(pair.first, pair.second);
|
||||
new AddGraphicsObjectCommand(conductor, m_diagram, QPointF(), undo_object);
|
||||
|
||||
@@ -369,6 +369,21 @@ void QETDiagramEditor::setUpActions()
|
||||
pv->project()->setAutoConductor(ac);
|
||||
});
|
||||
|
||||
//AutoBreakConductor
|
||||
m_auto_break_conductor = new QAction (QET::Icons::Conductor, tr("Coupure automatique de conducteur(s)","Tool tip of auto break conductor"), this);
|
||||
m_auto_break_conductor->setStatusTip (tr("Couper automatiquement les conducteurs existants lors du placement d'un élément", "Status tip of auto break conductor"));
|
||||
m_auto_break_conductor->setCheckable (true);
|
||||
{
|
||||
QSettings settings;
|
||||
m_auto_break_conductor->setChecked(settings.value("diagrameditor/auto_break_conductor", false).toBool());
|
||||
}
|
||||
connect(m_auto_break_conductor, &QAction::triggered, [this](bool abc) {
|
||||
QSettings settings;
|
||||
settings.setValue("diagrameditor/auto_break_conductor", abc);
|
||||
if (ProjectView *pv = currentProjectView())
|
||||
pv->project()->setAutoBreakConductor(abc);
|
||||
});
|
||||
|
||||
//Switch background color
|
||||
m_grey_background = new QAction (QET::Icons::DiagramBg, tr("Couleur de fond blanc/gris","Tool tip of white/grey background button"), this);
|
||||
m_grey_background -> setStatusTip (tr("Affiche la couleur de fond du folio en blanc ou en gris", "Status tip of white/grey background button"));
|
||||
@@ -808,6 +823,7 @@ void QETDiagramEditor::setUpToolBar()
|
||||
diagram_tool_bar -> addAction (m_edit_diagram_properties);
|
||||
diagram_tool_bar -> addAction (m_conductor_reset);
|
||||
diagram_tool_bar -> addAction (m_auto_conductor);
|
||||
diagram_tool_bar -> addAction (m_auto_break_conductor);
|
||||
|
||||
m_add_item_tool_bar = new QToolBar(tr("Ajouter"), this);
|
||||
m_add_item_tool_bar->setObjectName("adding");
|
||||
@@ -1892,9 +1908,14 @@ void QETDiagramEditor::slot_updateModeActions()
|
||||
{
|
||||
m_auto_conductor -> setEnabled (true);
|
||||
m_auto_conductor -> setChecked (pv -> project() -> autoConductor());
|
||||
m_auto_break_conductor -> setEnabled (true);
|
||||
m_auto_break_conductor -> setChecked (pv -> project() -> autoBreakConductor());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_auto_conductor -> setDisabled(true);
|
||||
m_auto_break_conductor -> setDisabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -191,6 +191,7 @@ class QETDiagramEditor : public QETMainWindow
|
||||
*redo, ///< Redo the latest cancelled operation
|
||||
*m_paste, ///< Paste clipboard content on the current diagram
|
||||
*m_auto_conductor, ///< Enable/Disable the use of auto conductor
|
||||
*m_auto_break_conductor, ///< Enable/Disable the use of auto break conductor
|
||||
*conductor_default, ///< Show a dialog to edit default conductor properties
|
||||
*m_grey_background, ///< Switch the background color in white or grey
|
||||
*m_draw_grid, ///< Switch the background grid display or not
|
||||
|
||||
@@ -69,6 +69,10 @@ m_project_properties_handler{this}
|
||||
init();
|
||||
|
||||
QSettings settings;
|
||||
|
||||
//Read auto break conductor default from global settings
|
||||
m_auto_break_conductor = settings.value(QStringLiteral("diagrameditor/auto_break_conductor"), false).toBool();
|
||||
|
||||
int size = settings.beginReadArray(QStringLiteral("diagrameditor/defaultguides"));
|
||||
for (int i = 0; i < size; ++i) {
|
||||
settings.setArrayIndex(i);
|
||||
@@ -924,6 +928,28 @@ void QETProject::setAutoConductor(bool ac)
|
||||
m_auto_conductor = ac;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::autoBreakConductor
|
||||
@return true if use of auto break conductor is authorized.
|
||||
See also Q_PROPERTY autoBreakConductor
|
||||
*/
|
||||
bool QETProject::autoBreakConductor() const
|
||||
{
|
||||
return m_auto_break_conductor;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::setAutoBreakConductor
|
||||
@param abc
|
||||
Enable the use of auto break conductor if true
|
||||
See also Q_PROPERTY autoBreakConductor
|
||||
*/
|
||||
void QETProject::setAutoBreakConductor(bool abc)
|
||||
{
|
||||
if (abc != m_auto_break_conductor)
|
||||
m_auto_break_conductor = abc;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief QETProject::autoFolioNumberingNewFolios
|
||||
emit Signal to add new Diagram with autonum
|
||||
@@ -1732,6 +1758,7 @@ void QETProject::readDefaultPropertiesXml(QDomDocument &xml_project)
|
||||
{
|
||||
m_current_conductor_autonum = conds_autonums.attribute(QStringLiteral("current_autonum"));
|
||||
m_freeze_new_conductors = conds_autonums.attribute(QStringLiteral("freeze_new_conductors")) == QLatin1String("true");
|
||||
m_auto_break_conductor = conds_autonums.attribute(QStringLiteral("auto_break_conductors")) == QLatin1String("true");
|
||||
for (auto elmt : QET::findInDomElement(conds_autonums, QStringLiteral("conductor_autonum")))
|
||||
{
|
||||
NumerotationContext nc;
|
||||
@@ -1858,6 +1885,7 @@ void QETProject::writeDefaultPropertiesXml(QDomElement &xml_element)
|
||||
QDomElement conductor_autonums = xml_document.createElement("conductors_autonums");
|
||||
conductor_autonums.setAttribute("current_autonum", m_current_conductor_autonum);
|
||||
conductor_autonums.setAttribute("freeze_new_conductors", m_freeze_new_conductors ? "true" : "false");
|
||||
conductor_autonums.setAttribute("auto_break_conductors", m_auto_break_conductor ? "true" : "false");
|
||||
foreach (QString key, conductorAutoNum().keys()) {
|
||||
QDomElement conductor_autonum = conductorAutoNum(key).toXml(xml_document, "conductor_autonum");
|
||||
if (key != "" && conductorAutoNumFormula(key) != "") {
|
||||
|
||||
@@ -90,6 +90,7 @@ class QETProject : public QObject
|
||||
};
|
||||
|
||||
Q_PROPERTY(bool autoConductor READ autoConductor WRITE setAutoConductor)
|
||||
Q_PROPERTY(bool autoBreakConductor READ autoBreakConductor WRITE setAutoBreakConductor)
|
||||
|
||||
// constructors, destructor
|
||||
public:
|
||||
@@ -181,9 +182,11 @@ class QETProject : public QObject
|
||||
void setFreezeNewConductors(bool);
|
||||
|
||||
bool autoConductor () const;
|
||||
bool autoBreakConductor () const;
|
||||
bool autoElement () const;
|
||||
bool autoFolio () const;
|
||||
void setAutoConductor (bool ac);
|
||||
void setAutoBreakConductor (bool abc);
|
||||
void setAutoElement (bool ae);
|
||||
void autoFolioNumberingNewFolios ();
|
||||
void autoFolioNumberingSelectedFolios(int, int, const QString&);
|
||||
@@ -312,6 +315,7 @@ class QETProject : public QObject
|
||||
QHash <QString, NumerotationContext> m_element_autonum; //Title and NumContext hash
|
||||
QString m_current_element_autonum;
|
||||
bool m_auto_conductor = true;
|
||||
bool m_auto_break_conductor = false;
|
||||
XmlElementCollection *m_elements_collection = nullptr;
|
||||
bool m_freeze_new_elements = false;
|
||||
bool m_freeze_new_conductors = false;
|
||||
|
||||
Reference in New Issue
Block a user