2007-12-01 10:47:15 +00:00
|
|
|
/*
|
2009-04-03 19:30:25 +00:00
|
|
|
Copyright 2006-2009 Xavier Guerrin
|
2007-12-01 10:47:15 +00:00
|
|
|
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/>.
|
|
|
|
*/
|
2006-10-27 15:47:22 +00:00
|
|
|
#include <QtDebug>
|
2007-10-03 17:02:39 +00:00
|
|
|
#include "conductor.h"
|
|
|
|
#include "conductorsegment.h"
|
|
|
|
#include "conductorsegmentprofile.h"
|
2008-07-30 12:44:57 +00:00
|
|
|
#include "diagramtextitem.h"
|
2006-10-27 15:47:22 +00:00
|
|
|
#include "element.h"
|
2007-09-25 23:24:36 +00:00
|
|
|
#include "diagram.h"
|
2007-09-27 17:42:02 +00:00
|
|
|
#include "diagramcommands.h"
|
2007-04-04 02:13:14 +00:00
|
|
|
#define PR(x) qDebug() << #x " = " << x;
|
2006-10-27 15:47:22 +00:00
|
|
|
|
2007-10-03 17:02:39 +00:00
|
|
|
bool Conductor::pen_and_brush_initialized = false;
|
|
|
|
QPen Conductor::conductor_pen = QPen();
|
|
|
|
QBrush Conductor::conductor_brush = QBrush();
|
2007-10-20 19:27:16 +00:00
|
|
|
QBrush Conductor::square_brush = QBrush(Qt::darkGreen);
|
2006-10-27 15:47:22 +00:00
|
|
|
/**
|
|
|
|
Constructeur
|
2007-12-16 12:19:04 +00:00
|
|
|
@param p1 Premiere Borne a laquelle le conducteur est lie
|
|
|
|
@param p2 Seconde Borne a laquelle le conducteur est lie
|
2006-10-27 15:47:22 +00:00
|
|
|
@param parent Element parent du conducteur (0 par defaut)
|
2007-12-16 12:19:04 +00:00
|
|
|
@param scene QGraphicsScene a laquelle appartient le conducteur
|
2006-10-27 15:47:22 +00:00
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
Conductor::Conductor(Terminal *p1, Terminal* p2, Element *parent, QGraphicsScene *scene) :
|
2008-07-09 21:14:30 +00:00
|
|
|
QObject(),
|
2007-09-20 20:16:08 +00:00
|
|
|
QGraphicsPathItem(parent, scene),
|
|
|
|
terminal1(p1),
|
|
|
|
terminal2(p2),
|
|
|
|
destroyed(false),
|
|
|
|
segments(NULL),
|
2008-03-01 17:39:44 +00:00
|
|
|
moving_point(false),
|
|
|
|
moving_segment(false),
|
2007-09-20 20:16:08 +00:00
|
|
|
previous_z_value(zValue()),
|
|
|
|
modified_path(false),
|
|
|
|
has_to_save_profile(false)
|
|
|
|
{
|
2006-10-27 15:47:22 +00:00
|
|
|
// ajout du conducteur a la liste de conducteurs de chacune des deux bornes
|
2007-10-03 17:02:39 +00:00
|
|
|
bool ajout_p1 = terminal1 -> addConductor(this);
|
|
|
|
bool ajout_p2 = terminal2 -> addConductor(this);
|
2007-09-20 20:16:08 +00:00
|
|
|
|
2006-10-27 15:47:22 +00:00
|
|
|
// en cas d'echec de l'ajout (conducteur deja existant notamment)
|
|
|
|
if (!ajout_p1 || !ajout_p2) return;
|
2007-09-20 20:16:08 +00:00
|
|
|
|
2007-01-05 14:29:08 +00:00
|
|
|
// attributs de dessin par defaut (communs a tous les conducteurs)
|
|
|
|
if (!pen_and_brush_initialized) {
|
2007-10-03 17:02:39 +00:00
|
|
|
conductor_pen.setJoinStyle(Qt::MiterJoin);
|
|
|
|
conductor_pen.setCapStyle(Qt::SquareCap);
|
|
|
|
conductor_pen.setColor(Qt::black);
|
|
|
|
conductor_pen.setStyle(Qt::SolidLine);
|
|
|
|
conductor_pen.setWidthF(1.0);
|
|
|
|
conductor_brush.setColor(Qt::white);
|
|
|
|
conductor_brush.setStyle(Qt::NoBrush);
|
2007-01-05 14:29:08 +00:00
|
|
|
pen_and_brush_initialized = true;
|
|
|
|
}
|
2007-09-20 20:16:08 +00:00
|
|
|
|
2007-10-22 20:27:39 +00:00
|
|
|
// par defaut, les 4 profils sont des profils nuls = il faut utiliser priv_calculeConductor
|
|
|
|
conductor_profiles.insert(Qt::TopLeftCorner, ConductorProfile());
|
|
|
|
conductor_profiles.insert(Qt::TopRightCorner, ConductorProfile());
|
|
|
|
conductor_profiles.insert(Qt::BottomLeftCorner, ConductorProfile());
|
|
|
|
conductor_profiles.insert(Qt::BottomRightCorner, ConductorProfile());
|
|
|
|
|
2006-10-27 15:47:22 +00:00
|
|
|
// calcul du rendu du conducteur
|
2007-10-03 17:02:39 +00:00
|
|
|
priv_calculeConductor(terminal1 -> amarrageConductor(), terminal1 -> orientation(), terminal2 -> amarrageConductor(), terminal2 -> orientation());
|
2006-11-26 21:35:05 +00:00
|
|
|
setFlags(QGraphicsItem::ItemIsSelectable);
|
2007-02-15 20:00:20 +00:00
|
|
|
setAcceptsHoverEvents(true);
|
2007-03-08 20:29:13 +00:00
|
|
|
|
|
|
|
// ajout du champ de texte editable
|
2007-09-26 22:57:53 +00:00
|
|
|
text_item = new DiagramTextItem();
|
2007-10-27 13:18:17 +00:00
|
|
|
text_item -> setFlag(QGraphicsItem::ItemIsMovable, false);
|
|
|
|
text_item -> setTextInteractionFlags(Qt::TextEditorInteraction);
|
2007-10-14 14:44:33 +00:00
|
|
|
text_item -> setPlainText(properties_.text);
|
|
|
|
text_item -> previous_text = properties_.text;
|
2007-03-08 20:29:13 +00:00
|
|
|
calculateTextItemPosition();
|
2007-04-15 19:53:23 +00:00
|
|
|
text_item -> setParentItem(this);
|
2008-07-09 21:14:30 +00:00
|
|
|
connect(
|
|
|
|
text_item,
|
|
|
|
SIGNAL(diagramTextChanged(DiagramTextItem *, const QString &, const QString &)),
|
|
|
|
this,
|
|
|
|
SLOT(displayedTextChanged())
|
|
|
|
);
|
2006-10-27 15:47:22 +00:00
|
|
|
}
|
|
|
|
|
2007-04-12 03:13:13 +00:00
|
|
|
/**
|
|
|
|
Destructeur
|
|
|
|
Detruit le conducteur ainsi que ses segments. Il ne detruit pas les bornes
|
|
|
|
mais s'en detache
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
Conductor::~Conductor() {
|
2007-04-12 03:13:13 +00:00
|
|
|
// se detache des bornes
|
|
|
|
if (!isDestroyed()) destroy();
|
|
|
|
|
|
|
|
// supprime les segments
|
2007-11-04 17:37:10 +00:00
|
|
|
deleteSegments();
|
2007-04-12 03:13:13 +00:00
|
|
|
}
|
|
|
|
|
2006-10-27 15:47:22 +00:00
|
|
|
/**
|
|
|
|
Met a jour la representation graphique du conducteur.
|
|
|
|
@param rect Rectangle a mettre a jour
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::update(const QRectF &rect) {
|
2007-01-05 14:29:08 +00:00
|
|
|
// appelle la bonne fonction pour calculer l'aspect du conducteur
|
2007-10-22 20:27:39 +00:00
|
|
|
if (nbSegments() && !conductor_profiles[currentPathType()].isNull()) {
|
|
|
|
priv_modifieConductor(
|
|
|
|
terminal1 -> amarrageConductor(), terminal1 -> orientation(),
|
|
|
|
terminal2 -> amarrageConductor(), terminal2 -> orientation()
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
priv_calculeConductor(
|
|
|
|
terminal1 -> amarrageConductor(), terminal1 -> orientation(),
|
|
|
|
terminal2 -> amarrageConductor(), terminal2 -> orientation()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2007-03-08 20:29:13 +00:00
|
|
|
calculateTextItemPosition();
|
2006-10-27 15:47:22 +00:00
|
|
|
QGraphicsPathItem::update(rect);
|
|
|
|
}
|
|
|
|
|
2006-11-30 18:05:02 +00:00
|
|
|
/**
|
2007-10-22 20:27:39 +00:00
|
|
|
Met a jour la representation graphique du conducteur en considerant que la
|
|
|
|
borne b a pour position pos. Cette fonction est appelee lorsqu'une seule
|
|
|
|
des bornes du conducteur a change de position.
|
2006-11-30 18:05:02 +00:00
|
|
|
@param rect Rectangle a mettre a jour
|
|
|
|
@param b Borne
|
2007-09-05 20:42:08 +00:00
|
|
|
@param newpos position de la borne b
|
2006-11-30 18:05:02 +00:00
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::updateWithNewPos(const QRectF &rect, const Terminal *b, const QPointF &newpos) {
|
2006-11-30 18:05:02 +00:00
|
|
|
QPointF p1, p2;
|
2007-01-29 20:14:26 +00:00
|
|
|
if (b == terminal1) {
|
2006-11-30 18:05:02 +00:00
|
|
|
p1 = newpos;
|
2007-10-03 17:02:39 +00:00
|
|
|
p2 = terminal2 -> amarrageConductor();
|
2007-01-29 20:14:26 +00:00
|
|
|
} else if (b == terminal2) {
|
2007-10-03 17:02:39 +00:00
|
|
|
p1 = terminal1 -> amarrageConductor();
|
2006-11-30 18:05:02 +00:00
|
|
|
p2 = newpos;
|
|
|
|
} else {
|
2007-10-03 17:02:39 +00:00
|
|
|
p1 = terminal1 -> amarrageConductor();
|
|
|
|
p2 = terminal2 -> amarrageConductor();
|
2006-11-30 18:05:02 +00:00
|
|
|
}
|
2007-10-22 20:27:39 +00:00
|
|
|
if (nbSegments() && !conductor_profiles[currentPathType()].isNull())
|
2007-10-03 17:02:39 +00:00
|
|
|
priv_modifieConductor(p1, terminal1 -> orientation(), p2, terminal2 -> orientation());
|
2007-01-05 14:29:08 +00:00
|
|
|
else
|
2007-10-03 17:02:39 +00:00
|
|
|
priv_calculeConductor(p1, terminal1 -> orientation(), p2, terminal2 -> orientation());
|
2007-03-08 20:29:13 +00:00
|
|
|
calculateTextItemPosition();
|
2007-01-05 14:29:08 +00:00
|
|
|
QGraphicsPathItem::update(rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Genere le QPainterPath a partir de la liste des points
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::segmentsToPath() {
|
2007-02-14 01:08:29 +00:00
|
|
|
// chemin qui sera dessine
|
2007-01-05 14:29:08 +00:00
|
|
|
QPainterPath path;
|
2007-02-14 01:08:29 +00:00
|
|
|
|
|
|
|
// s'il n'y a pa des segments, on arrete la
|
|
|
|
if (segments == NULL) setPath(path);
|
|
|
|
|
|
|
|
// demarre le chemin
|
|
|
|
path.moveTo(segments -> firstPoint());
|
|
|
|
|
|
|
|
// parcourt les segments pour dessiner le chemin
|
2007-10-03 17:02:39 +00:00
|
|
|
ConductorSegment *segment = segments;
|
2007-02-14 01:08:29 +00:00
|
|
|
while(segment -> hasNextSegment()) {
|
|
|
|
path.lineTo(segment -> secondPoint());
|
|
|
|
segment = segment -> nextSegment();
|
2007-01-05 14:29:08 +00:00
|
|
|
}
|
2007-02-14 01:08:29 +00:00
|
|
|
|
|
|
|
// termine le chemin
|
|
|
|
path.lineTo(segment -> secondPoint());
|
|
|
|
|
|
|
|
// affecte le chemin au conducteur
|
2007-01-05 14:29:08 +00:00
|
|
|
setPath(path);
|
2006-11-30 18:05:02 +00:00
|
|
|
}
|
|
|
|
|
2006-12-03 17:44:20 +00:00
|
|
|
/**
|
2009-11-22 16:12:22 +00:00
|
|
|
Gere les updates
|
2006-12-03 17:44:20 +00:00
|
|
|
@param p1 Coordonnees du point d'amarrage de la borne 1
|
2007-01-05 14:29:08 +00:00
|
|
|
@param o1 Orientation de la borne 1
|
2006-12-03 17:44:20 +00:00
|
|
|
@param p2 Coordonnees du point d'amarrage de la borne 2
|
2007-01-05 14:29:08 +00:00
|
|
|
@param o2 Orientation de la borne 2
|
2006-12-03 17:44:20 +00:00
|
|
|
*/
|
2009-11-22 16:12:22 +00:00
|
|
|
void Conductor::priv_modifieConductor(const QPointF &p1, QET::Orientation o1, const QPointF &p2, QET::Orientation o2) {
|
|
|
|
Q_UNUSED(o1);
|
|
|
|
Q_UNUSED(o2);
|
2007-10-22 20:27:39 +00:00
|
|
|
|
|
|
|
ConductorProfile &conductor_profile = conductor_profiles[currentPathType()];
|
|
|
|
|
2007-10-03 17:02:39 +00:00
|
|
|
Q_ASSERT_X(conductor_profile.nbSegments(QET::Both) > 1, "Conductor::priv_modifieConductor", "pas de points a modifier");
|
|
|
|
Q_ASSERT_X(!conductor_profile.isNull(), "Conductor::priv_modifieConductor", "pas de profil utilisable");
|
2006-12-03 17:44:20 +00:00
|
|
|
|
2007-01-05 14:29:08 +00:00
|
|
|
// recupere les coordonnees fournies des bornes
|
|
|
|
QPointF new_p1 = mapFromScene(p1);
|
|
|
|
QPointF new_p2 = mapFromScene(p2);
|
2007-09-20 20:16:08 +00:00
|
|
|
QRectF new_rect = QRectF(new_p1, new_p2);
|
2006-12-03 17:44:20 +00:00
|
|
|
|
2007-09-20 20:16:08 +00:00
|
|
|
// recupere la largeur et la hauteur du profil
|
2007-10-03 17:02:39 +00:00
|
|
|
qreal profile_width = conductor_profile.width();
|
|
|
|
qreal profile_height = conductor_profile.height();
|
2006-12-03 17:44:20 +00:00
|
|
|
|
2007-09-20 20:16:08 +00:00
|
|
|
// calcule les differences verticales et horizontales a appliquer
|
|
|
|
qreal h_diff = (qAbs(new_rect.width()) - qAbs(profile_width) ) * getSign(profile_width);
|
|
|
|
qreal v_diff = (qAbs(new_rect.height()) - qAbs(profile_height)) * getSign(profile_height);
|
2006-12-03 17:44:20 +00:00
|
|
|
|
2007-09-20 20:16:08 +00:00
|
|
|
// applique les differences aux segments
|
2007-10-03 17:02:39 +00:00
|
|
|
QHash<ConductorSegmentProfile *, qreal> segments_lengths;
|
|
|
|
segments_lengths.unite(shareOffsetBetweenSegments(h_diff, conductor_profile.horizontalSegments()));
|
|
|
|
segments_lengths.unite(shareOffsetBetweenSegments(v_diff, conductor_profile.verticalSegments()));
|
2007-02-14 01:08:29 +00:00
|
|
|
|
2007-09-20 20:16:08 +00:00
|
|
|
// en deduit egalement les coefficients d'inversion (-1 pour une inversion, +1 pour conserver le meme sens)
|
|
|
|
int horiz_coeff = getCoeff(new_rect.width(), profile_width);
|
|
|
|
int verti_coeff = getCoeff(new_rect.height(), profile_height);
|
|
|
|
|
|
|
|
// genere les nouveaux points
|
2007-02-14 01:08:29 +00:00
|
|
|
QList<QPointF> points;
|
2007-09-20 20:16:08 +00:00
|
|
|
points << new_p1;
|
2007-10-03 17:02:39 +00:00
|
|
|
int limit = conductor_profile.segments.count() - 1;
|
2007-09-20 20:16:08 +00:00
|
|
|
for (int i = 0 ; i < limit ; ++ i) {
|
|
|
|
// dernier point
|
2007-01-05 14:29:08 +00:00
|
|
|
QPointF previous_point = points.last();
|
2007-09-20 20:16:08 +00:00
|
|
|
|
|
|
|
// profil de segment de conducteur en cours
|
2007-10-03 17:02:39 +00:00
|
|
|
ConductorSegmentProfile *csp = conductor_profile.segments.at(i);
|
2007-09-20 20:16:08 +00:00
|
|
|
|
|
|
|
// coefficient et offset a utiliser pour ce point
|
|
|
|
qreal coeff = csp -> isHorizontal ? horiz_coeff : verti_coeff;
|
|
|
|
qreal offset_applied = segments_lengths[csp];
|
|
|
|
|
|
|
|
// applique l'offset et le coeff au point
|
|
|
|
if (csp -> isHorizontal) {
|
|
|
|
points << QPointF (
|
|
|
|
previous_point.x() + (coeff * offset_applied),
|
|
|
|
previous_point.y()
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
points << QPointF (
|
|
|
|
previous_point.x(),
|
|
|
|
previous_point.y() + (coeff * offset_applied)
|
|
|
|
);
|
|
|
|
}
|
2006-12-03 17:44:20 +00:00
|
|
|
}
|
2007-09-20 20:16:08 +00:00
|
|
|
points << new_p2;
|
2007-02-14 01:08:29 +00:00
|
|
|
pointsToSegments(points);
|
|
|
|
segmentsToPath();
|
2007-01-05 14:29:08 +00:00
|
|
|
}
|
|
|
|
|
2007-09-20 20:16:08 +00:00
|
|
|
/**
|
|
|
|
@param offset Longueur a repartir entre les segments
|
|
|
|
@param segments_list Segments sur lesquels il faut repartir la longueur
|
|
|
|
@param precision seuil en-deca duquel on considere qu'il ne reste rien a repartir
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
QHash<ConductorSegmentProfile *, qreal> Conductor::shareOffsetBetweenSegments(
|
2007-09-20 20:16:08 +00:00
|
|
|
const qreal &offset,
|
2007-10-03 17:02:39 +00:00
|
|
|
const QList<ConductorSegmentProfile *> &segments_list,
|
2007-09-20 20:16:08 +00:00
|
|
|
const qreal &precision
|
|
|
|
) const {
|
|
|
|
// construit le QHash qui sera retourne
|
2007-10-03 17:02:39 +00:00
|
|
|
QHash<ConductorSegmentProfile *, qreal> segments_hash;
|
|
|
|
foreach(ConductorSegmentProfile *csp, segments_list) {
|
2007-09-20 20:16:08 +00:00
|
|
|
segments_hash.insert(csp, csp -> length);
|
|
|
|
}
|
|
|
|
|
|
|
|
// memorise le signe de la longueur de chaque segement
|
2007-10-03 17:02:39 +00:00
|
|
|
QHash<ConductorSegmentProfile *, int> segments_signs;
|
|
|
|
foreach(ConductorSegmentProfile *csp, segments_hash.keys()) {
|
2007-09-20 20:16:08 +00:00
|
|
|
segments_signs.insert(csp, getSign(csp -> length));
|
|
|
|
}
|
|
|
|
|
|
|
|
//qDebug() << "repartition d'un offset de" << offset << "px sur" << segments_list.count() << "segments";
|
|
|
|
|
|
|
|
// repartit l'offset sur les segments
|
|
|
|
qreal remaining_offset = offset;
|
|
|
|
while (remaining_offset > precision || remaining_offset < -precision) {
|
|
|
|
// recupere le nombre de segments differents ayant une longueur non nulle
|
|
|
|
uint segments_count = 0;
|
2007-10-03 17:02:39 +00:00
|
|
|
foreach(ConductorSegmentProfile *csp, segments_hash.keys()) if (segments_hash[csp]) ++ segments_count;
|
2007-09-20 20:16:08 +00:00
|
|
|
//qDebug() << " remaining_offset =" << remaining_offset;
|
|
|
|
qreal local_offset = remaining_offset / segments_count;
|
|
|
|
//qDebug() << " repartition d'un offset local de" << local_offset << "px sur" << segments_count << "segments";
|
|
|
|
remaining_offset = 0.0;
|
2007-10-03 17:02:39 +00:00
|
|
|
foreach(ConductorSegmentProfile *csp, segments_hash.keys()) {
|
2007-09-20 20:16:08 +00:00
|
|
|
// ignore les segments de longueur nulle
|
|
|
|
if (!segments_hash[csp]) continue;
|
|
|
|
// applique l'offset au segment
|
|
|
|
//qreal segment_old_length = segments_hash[csp];
|
|
|
|
segments_hash[csp] += local_offset;
|
|
|
|
|
|
|
|
// (la longueur du segment change de signe) <=> (le segment n'a pu absorbe tout l'offset)
|
|
|
|
if (segments_signs[csp] != getSign(segments_hash[csp])) {
|
|
|
|
|
|
|
|
// on remet le trop-plein dans la reserve d'offset
|
|
|
|
remaining_offset += qAbs(segments_hash[csp]) * getSign(local_offset);
|
|
|
|
//qDebug() << " trop-plein de" << qAbs(segments_hash[csp]) * getSign(local_offset) << "remaining_offset =" << remaining_offset;
|
|
|
|
segments_hash[csp] = 0.0;
|
|
|
|
} else {
|
|
|
|
//qDebug() << " offset local de" << local_offset << "accepte";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(segments_hash);
|
|
|
|
}
|
|
|
|
|
2007-01-05 14:29:08 +00:00
|
|
|
/**
|
|
|
|
Calcule un trajet "par defaut" pour le conducteur
|
|
|
|
@param p1 Coordonnees du point d'amarrage de la borne 1
|
|
|
|
@param o1 Orientation de la borne 1
|
|
|
|
@param p2 Coordonnees du point d'amarrage de la borne 2
|
|
|
|
@param o2 Orientation de la borne 2
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::priv_calculeConductor(const QPointF &p1, QET::Orientation o1, const QPointF &p2, QET::Orientation o2) {
|
2007-01-05 14:29:08 +00:00
|
|
|
QPointF sp1, sp2, depart, newp1, newp2, arrivee, depart0, arrivee0;
|
2007-06-30 17:41:07 +00:00
|
|
|
QET::Orientation ori_depart, ori_arrivee;
|
2007-02-14 01:08:29 +00:00
|
|
|
|
|
|
|
// s'assure qu'il n'y a ni points
|
|
|
|
QList<QPointF> points;
|
|
|
|
|
2007-01-05 14:29:08 +00:00
|
|
|
// mappe les points par rapport a la scene
|
|
|
|
sp1 = mapFromScene(p1);
|
|
|
|
sp2 = mapFromScene(p2);
|
|
|
|
|
|
|
|
// prolonge les bornes
|
|
|
|
newp1 = extendTerminal(sp1, o1);
|
|
|
|
newp2 = extendTerminal(sp2, o2);
|
2006-12-03 17:44:20 +00:00
|
|
|
|
2007-01-05 14:29:08 +00:00
|
|
|
// distingue le depart de l'arrivee : le trajet se fait toujours de gauche a droite (apres prolongation)
|
2006-12-03 17:44:20 +00:00
|
|
|
if (newp1.x() <= newp2.x()) {
|
|
|
|
depart = newp1;
|
|
|
|
arrivee = newp2;
|
|
|
|
depart0 = sp1;
|
|
|
|
arrivee0 = sp2;
|
2007-01-05 14:29:08 +00:00
|
|
|
ori_depart = o1;
|
|
|
|
ori_arrivee = o2;
|
2006-10-27 15:47:22 +00:00
|
|
|
} else {
|
2006-12-03 17:44:20 +00:00
|
|
|
depart = newp2;
|
|
|
|
arrivee = newp1;
|
|
|
|
depart0 = sp2;
|
|
|
|
arrivee0 = sp1;
|
2007-01-05 14:29:08 +00:00
|
|
|
ori_depart = o2;
|
|
|
|
ori_arrivee = o1;
|
2006-10-27 15:47:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// debut du trajet
|
2007-01-05 14:29:08 +00:00
|
|
|
points << depart0;
|
2006-12-03 17:44:20 +00:00
|
|
|
|
|
|
|
// prolongement de la borne de depart
|
2007-01-05 14:29:08 +00:00
|
|
|
points << depart;
|
2006-12-03 17:44:20 +00:00
|
|
|
|
|
|
|
// commence le vrai trajet
|
2006-10-27 15:47:22 +00:00
|
|
|
if (depart.y() < arrivee.y()) {
|
|
|
|
// trajet descendant
|
2007-06-30 17:41:07 +00:00
|
|
|
if ((ori_depart == QET::North && (ori_arrivee == QET::South || ori_arrivee == QET::West)) || (ori_depart == QET::East && ori_arrivee == QET::West)) {
|
2009-04-11 13:09:47 +00:00
|
|
|
// cas "3"
|
2007-12-16 12:19:04 +00:00
|
|
|
int ligne_inter_x = qRound(depart.x() + arrivee.x()) / 2;
|
|
|
|
while (ligne_inter_x % Diagram::xGrid) -- ligne_inter_x;
|
2007-01-05 14:29:08 +00:00
|
|
|
points << QPointF(ligne_inter_x, depart.y());
|
|
|
|
points << QPointF(ligne_inter_x, arrivee.y());
|
2007-06-30 17:41:07 +00:00
|
|
|
} else if ((ori_depart == QET::South && (ori_arrivee == QET::North || ori_arrivee == QET::East)) || (ori_depart == QET::West && ori_arrivee == QET::East)) {
|
2009-04-11 13:09:47 +00:00
|
|
|
// cas "4"
|
2007-12-16 12:19:04 +00:00
|
|
|
int ligne_inter_y = qRound(depart.y() + arrivee.y()) / 2;
|
|
|
|
while (ligne_inter_y % Diagram::yGrid) -- ligne_inter_y;
|
2007-01-05 14:29:08 +00:00
|
|
|
points << QPointF(depart.x(), ligne_inter_y);
|
|
|
|
points << QPointF(arrivee.x(), ligne_inter_y);
|
2007-06-30 17:41:07 +00:00
|
|
|
} else if ((ori_depart == QET::North || ori_depart == QET::East) && (ori_arrivee == QET::North || ori_arrivee == QET::East)) {
|
2009-04-11 13:09:47 +00:00
|
|
|
points << QPointF(arrivee.x(), depart.y()); // cas "2"
|
2007-01-05 14:29:08 +00:00
|
|
|
} else {
|
2009-04-11 13:09:47 +00:00
|
|
|
points << QPointF(depart.x(), arrivee.y()); // cas "1"
|
2007-01-05 14:29:08 +00:00
|
|
|
}
|
2006-10-27 15:47:22 +00:00
|
|
|
} else {
|
|
|
|
// trajet montant
|
2007-06-30 17:41:07 +00:00
|
|
|
if ((ori_depart == QET::West && (ori_arrivee == QET::East || ori_arrivee == QET::South)) || (ori_depart == QET::North && ori_arrivee == QET::South)) {
|
2009-04-11 13:09:47 +00:00
|
|
|
// cas "3"
|
2007-12-16 12:19:04 +00:00
|
|
|
int ligne_inter_y = qRound(depart.y() + arrivee.y()) / 2;
|
|
|
|
while (ligne_inter_y % Diagram::yGrid) -- ligne_inter_y;
|
2007-01-05 14:29:08 +00:00
|
|
|
points << QPointF(depart.x(), ligne_inter_y);
|
|
|
|
points << QPointF(arrivee.x(), ligne_inter_y);
|
2007-06-30 17:41:07 +00:00
|
|
|
} else if ((ori_depart == QET::East && (ori_arrivee == QET::West || ori_arrivee == QET::North)) || (ori_depart == QET::South && ori_arrivee == QET::North)) {
|
2009-04-11 13:09:47 +00:00
|
|
|
// cas "4"
|
2007-12-16 12:19:04 +00:00
|
|
|
int ligne_inter_x = qRound(depart.x() + arrivee.x()) / 2;
|
|
|
|
while (ligne_inter_x % Diagram::xGrid) -- ligne_inter_x;
|
2007-01-05 14:29:08 +00:00
|
|
|
points << QPointF(ligne_inter_x, depart.y());
|
|
|
|
points << QPointF(ligne_inter_x, arrivee.y());
|
2007-06-30 17:41:07 +00:00
|
|
|
} else if ((ori_depart == QET::West || ori_depart == QET::North) && (ori_arrivee == QET::West || ori_arrivee == QET::North)) {
|
2009-04-11 13:09:47 +00:00
|
|
|
points << QPointF(depart.x(), arrivee.y()); // cas "2"
|
2007-01-05 14:29:08 +00:00
|
|
|
} else {
|
2009-04-11 13:09:47 +00:00
|
|
|
points << QPointF(arrivee.x(), depart.y()); // cas "1"
|
2007-01-05 14:29:08 +00:00
|
|
|
}
|
2006-10-27 15:47:22 +00:00
|
|
|
}
|
2006-12-03 17:44:20 +00:00
|
|
|
|
|
|
|
// fin du vrai trajet
|
2007-01-05 14:29:08 +00:00
|
|
|
points << arrivee;
|
2006-12-03 17:44:20 +00:00
|
|
|
|
|
|
|
// prolongement de la borne d'arrivee
|
2007-01-05 14:29:08 +00:00
|
|
|
points << arrivee0;
|
2006-12-03 17:44:20 +00:00
|
|
|
|
2007-09-20 20:16:08 +00:00
|
|
|
// inverse eventuellement l'ordre des points afin que le trajet soit exprime de la borne 1 vers la borne 2
|
|
|
|
if (newp1.x() > newp2.x()) {
|
|
|
|
QList<QPointF> points2;
|
|
|
|
for (int i = points.size() - 1 ; i >= 0 ; -- i) points2 << points.at(i);
|
|
|
|
points = points2;
|
|
|
|
}
|
|
|
|
|
2007-02-14 01:08:29 +00:00
|
|
|
pointsToSegments(points);
|
|
|
|
segmentsToPath();
|
2007-01-05 14:29:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Prolonge une borne.
|
|
|
|
@param terminal Le point correspondant a la borne
|
|
|
|
@param terminal_orientation L'orientation de la borne
|
|
|
|
@param ext_size la taille de la prolongation
|
|
|
|
@return le point correspondant a la borne apres prolongation
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
QPointF Conductor::extendTerminal(const QPointF &terminal, QET::Orientation terminal_orientation, qreal ext_size) {
|
2007-01-05 14:29:08 +00:00
|
|
|
QPointF extended_terminal;
|
|
|
|
switch(terminal_orientation) {
|
2007-06-30 17:41:07 +00:00
|
|
|
case QET::North:
|
2007-01-05 14:29:08 +00:00
|
|
|
extended_terminal = QPointF(terminal.x(), terminal.y() - ext_size);
|
|
|
|
break;
|
2007-06-30 17:41:07 +00:00
|
|
|
case QET::East:
|
2007-01-05 14:29:08 +00:00
|
|
|
extended_terminal = QPointF(terminal.x() + ext_size, terminal.y());
|
|
|
|
break;
|
2007-06-30 17:41:07 +00:00
|
|
|
case QET::South:
|
2007-01-05 14:29:08 +00:00
|
|
|
extended_terminal = QPointF(terminal.x(), terminal.y() + ext_size);
|
|
|
|
break;
|
2007-06-30 17:41:07 +00:00
|
|
|
case QET::West:
|
2007-01-05 14:29:08 +00:00
|
|
|
extended_terminal = QPointF(terminal.x() - ext_size, terminal.y());
|
|
|
|
break;
|
|
|
|
default: extended_terminal = terminal;
|
|
|
|
}
|
|
|
|
return(extended_terminal);
|
2006-10-27 15:47:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Dessine le conducteur sans antialiasing.
|
|
|
|
@param qp Le QPainter a utiliser pour dessiner le conducteur
|
2007-10-21 16:10:21 +00:00
|
|
|
@param options Les options de style pour le conducteur
|
2006-10-27 15:47:22 +00:00
|
|
|
@param qw Le QWidget sur lequel on dessine
|
|
|
|
*/
|
2009-11-22 16:12:22 +00:00
|
|
|
void Conductor::paint(QPainter *qp, const QStyleOptionGraphicsItem *options, QWidget *qw) {
|
|
|
|
Q_UNUSED(qw);
|
2006-10-27 15:47:22 +00:00
|
|
|
qp -> save();
|
2007-01-05 14:29:08 +00:00
|
|
|
qp -> setRenderHint(QPainter::Antialiasing, false);
|
2006-11-26 21:35:05 +00:00
|
|
|
|
2009-11-22 16:12:22 +00:00
|
|
|
// determine la couleur du conducteur
|
|
|
|
QColor final_conductor_color(properties_.color);
|
2007-01-05 14:29:08 +00:00
|
|
|
if (isSelected()) {
|
2009-11-22 16:12:22 +00:00
|
|
|
final_conductor_color = Qt::red;
|
|
|
|
} else {
|
|
|
|
if (Diagram *parent_diagram = diagram()) {
|
|
|
|
if (!parent_diagram -> drawColoredConductors()) {
|
|
|
|
final_conductor_color = Qt::black;
|
|
|
|
}
|
|
|
|
}
|
2007-01-05 14:29:08 +00:00
|
|
|
}
|
2006-11-26 21:35:05 +00:00
|
|
|
|
2009-11-22 16:12:22 +00:00
|
|
|
// affectation du QPen et de la QBrush modifies au QPainter
|
|
|
|
qp -> setBrush(conductor_brush);
|
|
|
|
QPen final_conductor_pen = conductor_pen;
|
|
|
|
|
|
|
|
// modification du QPen generique pour lui affecter la couleur et le style adequats
|
|
|
|
final_conductor_pen.setColor(final_conductor_color);
|
|
|
|
final_conductor_pen.setStyle(properties_.style);
|
|
|
|
final_conductor_pen.setJoinStyle(Qt::SvgMiterJoin); // meilleur rendu des pointilles
|
|
|
|
|
2009-04-05 11:48:26 +00:00
|
|
|
// utilisation d'un trait "cosmetique" en-dessous d'un certain zoom
|
|
|
|
if (options && options -> levelOfDetail < 1.0) {
|
2009-11-22 16:12:22 +00:00
|
|
|
final_conductor_pen.setCosmetic(true);
|
2009-04-05 11:48:26 +00:00
|
|
|
}
|
|
|
|
|
2009-11-22 16:12:22 +00:00
|
|
|
qp -> setPen(final_conductor_pen);
|
|
|
|
|
2007-01-05 14:29:08 +00:00
|
|
|
// dessin du conducteur
|
2006-11-26 21:35:05 +00:00
|
|
|
qp -> drawPath(path());
|
2007-10-14 14:44:33 +00:00
|
|
|
if (properties_.type == ConductorProperties::Single) {
|
2009-11-22 16:12:22 +00:00
|
|
|
qp -> setBrush(final_conductor_color);
|
2007-10-14 14:44:33 +00:00
|
|
|
properties_.singleLineProperties.draw(
|
2007-10-01 14:08:11 +00:00
|
|
|
qp,
|
|
|
|
middleSegment() -> isHorizontal() ? QET::Horizontal : QET::Vertical,
|
2007-10-04 14:30:52 +00:00
|
|
|
QRectF(middleSegment() -> middle() - QPointF(10.0, 7.5), QSizeF(20.0, 15.0))
|
2007-10-01 14:08:11 +00:00
|
|
|
);
|
2007-10-04 14:30:52 +00:00
|
|
|
if (isSelected()) qp -> setBrush(Qt::NoBrush);
|
2007-10-01 14:08:11 +00:00
|
|
|
}
|
2007-01-05 14:29:08 +00:00
|
|
|
|
2009-04-18 20:20:59 +00:00
|
|
|
// decalage ideal pour le rendu centre d'un carre / cercle de 2.0 px de cote / diametre
|
|
|
|
qreal pretty_offset = (options -> levelOfDetail == 1 ? 1.0 : 1.0);
|
2007-10-20 19:27:16 +00:00
|
|
|
|
2007-01-05 14:29:08 +00:00
|
|
|
// dessin des points d'accroche du conducteur si celui-ci est selectionne
|
|
|
|
if (isSelected()) {
|
2007-02-14 01:08:29 +00:00
|
|
|
QList<QPointF> points = segmentsToPoints();
|
|
|
|
QPointF previous_point;
|
2007-01-05 14:29:08 +00:00
|
|
|
for (int i = 1 ; i < (points.size() -1) ; ++ i) {
|
|
|
|
QPointF point = points.at(i);
|
2007-10-20 19:27:16 +00:00
|
|
|
|
|
|
|
// dessine le carre de saisie du segment
|
2007-02-14 01:08:29 +00:00
|
|
|
if (i > 1) {
|
|
|
|
qp -> fillRect(
|
|
|
|
QRectF(
|
2007-10-20 19:27:16 +00:00
|
|
|
((previous_point.x() + point.x()) / 2.0 ) - pretty_offset,
|
|
|
|
((previous_point.y() + point.y()) / 2.0 ) - pretty_offset,
|
2009-04-18 20:20:59 +00:00
|
|
|
2.0,
|
|
|
|
2.0
|
2007-02-14 01:08:29 +00:00
|
|
|
),
|
|
|
|
square_brush
|
|
|
|
);
|
|
|
|
}
|
|
|
|
previous_point = point;
|
2007-01-05 14:29:08 +00:00
|
|
|
}
|
|
|
|
}
|
2007-10-20 19:27:16 +00:00
|
|
|
|
|
|
|
// dessine les eventuelles jonctions
|
|
|
|
QList<QPointF> junctions_list = junctions();
|
|
|
|
if (!junctions_list.isEmpty()) {
|
2009-11-22 16:12:22 +00:00
|
|
|
final_conductor_pen.setStyle(Qt::SolidLine);
|
|
|
|
QBrush junction_brush(final_conductor_color, Qt::SolidPattern);
|
|
|
|
qp -> setPen(final_conductor_pen);
|
2007-10-20 19:27:16 +00:00
|
|
|
qp -> setBrush(junction_brush);
|
|
|
|
qp -> setRenderHint(QPainter::Antialiasing, true);
|
|
|
|
foreach(QPointF point, junctions_list) {
|
2009-04-18 20:20:59 +00:00
|
|
|
qp -> drawEllipse(QRectF(point.x() - pretty_offset, point.y() - pretty_offset, 2.0, 2.0));
|
2007-10-20 19:27:16 +00:00
|
|
|
}
|
|
|
|
}
|
2006-10-27 15:47:22 +00:00
|
|
|
qp -> restore();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Methode de preparation a la destruction du conducteur ; le conducteur se detache de ses deux bornes
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::destroy() {
|
2006-10-27 15:47:22 +00:00
|
|
|
destroyed = true;
|
2007-10-03 17:02:39 +00:00
|
|
|
terminal1 -> removeConductor(this);
|
|
|
|
terminal2 -> removeConductor(this);
|
2006-10-27 15:47:22 +00:00
|
|
|
}
|
|
|
|
|
2007-09-25 23:24:36 +00:00
|
|
|
/// @return le Diagram auquel ce conducteur appartient, ou 0 si ce conducteur est independant
|
2007-10-03 17:02:39 +00:00
|
|
|
Diagram *Conductor::diagram() const {
|
2007-09-25 23:24:36 +00:00
|
|
|
return(qobject_cast<Diagram *>(scene()));
|
|
|
|
}
|
|
|
|
|
2006-10-27 15:47:22 +00:00
|
|
|
/**
|
|
|
|
Methode de validation d'element XML
|
|
|
|
@param e Un element XML sense represente un Conducteur
|
|
|
|
@return true si l'element XML represente bien un Conducteur ; false sinon
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
bool Conductor::valideXml(QDomElement &e){
|
2006-10-27 15:47:22 +00:00
|
|
|
// verifie le nom du tag
|
2007-10-03 17:02:39 +00:00
|
|
|
if (e.tagName() != "conductor") return(false);
|
2006-10-27 15:47:22 +00:00
|
|
|
|
|
|
|
// verifie la presence des attributs minimaux
|
2007-03-09 19:18:55 +00:00
|
|
|
if (!e.hasAttribute("terminal1")) return(false);
|
|
|
|
if (!e.hasAttribute("terminal2")) return(false);
|
2006-10-27 15:47:22 +00:00
|
|
|
|
|
|
|
bool conv_ok;
|
|
|
|
// parse l'abscisse
|
2007-03-09 19:18:55 +00:00
|
|
|
e.attribute("terminal1").toInt(&conv_ok);
|
2006-10-27 15:47:22 +00:00
|
|
|
if (!conv_ok) return(false);
|
|
|
|
|
|
|
|
// parse l'ordonnee
|
2007-03-09 19:18:55 +00:00
|
|
|
e.attribute("terminal2").toInt(&conv_ok);
|
2006-10-27 15:47:22 +00:00
|
|
|
if (!conv_ok) return(false);
|
|
|
|
return(true);
|
|
|
|
}
|
2007-01-05 14:29:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
Gere les clics sur le conducteur.
|
|
|
|
@param e L'evenement decrivant le clic.
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::mousePressEvent(QGraphicsSceneMouseEvent *e) {
|
2007-01-05 14:29:08 +00:00
|
|
|
// clic gauche
|
|
|
|
if (e -> buttons() & Qt::LeftButton) {
|
2007-02-14 01:08:29 +00:00
|
|
|
// recupere les coordonnees du clic
|
2007-10-10 22:35:32 +00:00
|
|
|
press_point = e -> pos();
|
2007-02-14 01:08:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
parcourt les segments pour determiner si le clic a eu lieu
|
|
|
|
- sur l'extremite d'un segment
|
|
|
|
- sur le milieu d'un segment
|
|
|
|
- ailleurs
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
ConductorSegment *segment = segments;
|
2007-02-14 01:08:29 +00:00
|
|
|
while (segment -> hasNextSegment()) {
|
|
|
|
if (hasClickedOn(press_point, segment -> secondPoint())) {
|
2007-01-05 14:29:08 +00:00
|
|
|
moving_point = true;
|
2007-02-14 01:08:29 +00:00
|
|
|
moving_segment = false;
|
2007-02-15 20:00:20 +00:00
|
|
|
previous_z_value = zValue();
|
|
|
|
setZValue(5000.0);
|
2007-02-14 01:08:29 +00:00
|
|
|
moved_segment = segment;
|
|
|
|
break;
|
|
|
|
} else if (hasClickedOn(press_point, segment -> middle())) {
|
|
|
|
moving_point = false;
|
|
|
|
moving_segment = true;
|
2007-02-15 20:00:20 +00:00
|
|
|
previous_z_value = zValue();
|
|
|
|
setZValue(5000.0);
|
2007-02-14 01:08:29 +00:00
|
|
|
moved_segment = segment;
|
2007-01-05 14:29:08 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-02-14 01:08:29 +00:00
|
|
|
segment = segment -> nextSegment();
|
2007-01-05 14:29:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
QGraphicsPathItem::mousePressEvent(e);
|
2009-04-04 18:48:15 +00:00
|
|
|
if (e -> modifiers() & Qt::ControlModifier) {
|
|
|
|
setSelected(!isSelected());
|
|
|
|
}
|
2007-01-05 14:29:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Gere les deplacements de souris sur le conducteur.
|
|
|
|
@param e L'evenement decrivant le deplacement de souris.
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::mouseMoveEvent(QGraphicsSceneMouseEvent *e) {
|
2007-01-05 14:29:08 +00:00
|
|
|
// clic gauche
|
|
|
|
if (e -> buttons() & Qt::LeftButton) {
|
2007-02-14 01:08:29 +00:00
|
|
|
// position pointee par la souris
|
|
|
|
qreal mouse_x = e -> pos().x();
|
|
|
|
qreal mouse_y = e -> pos().y();
|
|
|
|
|
2007-10-10 22:35:32 +00:00
|
|
|
bool snap_conductors_to_grid = e -> modifiers() ^ Qt::ShiftModifier;
|
|
|
|
if (snap_conductors_to_grid) {
|
|
|
|
mouse_x = qRound(mouse_x / (Diagram::xGrid * 1.0)) * Diagram::xGrid;
|
|
|
|
mouse_y = qRound(mouse_y / (Diagram::yGrid * 1.0)) * Diagram::yGrid;
|
|
|
|
}
|
2007-01-05 14:29:08 +00:00
|
|
|
if (moving_point) {
|
2007-02-14 01:08:29 +00:00
|
|
|
// la modification par points revient bientot
|
|
|
|
/*
|
2007-01-05 14:29:08 +00:00
|
|
|
// position precedente du point
|
2007-02-14 01:08:29 +00:00
|
|
|
QPointF p = moved_segment -> secondPoint();
|
2007-01-05 14:29:08 +00:00
|
|
|
qreal p_x = p.x();
|
|
|
|
qreal p_y = p.y();
|
|
|
|
|
2007-02-14 01:08:29 +00:00
|
|
|
// calcul du deplacement
|
|
|
|
moved_segment -> moveX(mouse_x - p_x());
|
|
|
|
moved_segment -> moveY(mouse_y - p_y());
|
2007-01-05 14:29:08 +00:00
|
|
|
|
2007-02-14 01:08:29 +00:00
|
|
|
// application du deplacement
|
|
|
|
modified_path = true;
|
|
|
|
updatePoints();
|
|
|
|
segmentsToPath();
|
|
|
|
*/
|
|
|
|
} else if (moving_segment) {
|
|
|
|
// position precedente du point
|
|
|
|
QPointF p = moved_segment -> middle();
|
2007-01-05 14:29:08 +00:00
|
|
|
|
2007-02-14 01:08:29 +00:00
|
|
|
// calcul du deplacement
|
|
|
|
moved_segment -> moveX(mouse_x - p.x());
|
|
|
|
moved_segment -> moveY(mouse_y - p.y());
|
2007-01-05 14:29:08 +00:00
|
|
|
|
|
|
|
// application du deplacement
|
|
|
|
modified_path = true;
|
2007-09-20 20:16:08 +00:00
|
|
|
has_to_save_profile = true;
|
2007-02-14 01:08:29 +00:00
|
|
|
segmentsToPath();
|
2007-03-08 20:29:13 +00:00
|
|
|
calculateTextItemPosition();
|
2007-01-05 14:29:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
QGraphicsPathItem::mouseMoveEvent(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Gere les relachements de boutons de souris sur le conducteur
|
|
|
|
@param e L'evenement decrivant le lacher de bouton.
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) {
|
2007-01-05 14:29:08 +00:00
|
|
|
// clic gauche
|
2007-02-14 01:08:29 +00:00
|
|
|
moving_point = false;
|
|
|
|
moving_segment = false;
|
2007-09-20 20:16:08 +00:00
|
|
|
if (has_to_save_profile) {
|
|
|
|
saveProfile();
|
|
|
|
has_to_save_profile = false;
|
|
|
|
}
|
2007-02-15 20:00:20 +00:00
|
|
|
setZValue(previous_z_value);
|
2009-04-04 18:48:15 +00:00
|
|
|
if (!(e -> modifiers() & Qt::ControlModifier)) {
|
|
|
|
QGraphicsPathItem::mouseReleaseEvent(e);
|
|
|
|
}
|
2007-03-08 20:29:13 +00:00
|
|
|
calculateTextItemPosition();
|
2007-01-05 14:29:08 +00:00
|
|
|
}
|
|
|
|
|
2007-04-09 02:56:47 +00:00
|
|
|
/**
|
|
|
|
Gere les mouvements de souris au dessus du conducteur
|
|
|
|
@param e Le QGraphicsSceneHoverEvent decrivant l'evenement
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::hoverMoveEvent(QGraphicsSceneHoverEvent *e) {
|
2007-04-15 19:22:54 +00:00
|
|
|
/*
|
2007-02-15 20:00:20 +00:00
|
|
|
if (isSelected()) {
|
|
|
|
QPointF hover_point = mapFromScene(e -> pos());
|
2007-10-03 17:02:39 +00:00
|
|
|
ConductorSegment *segment = segments;
|
2007-02-15 20:00:20 +00:00
|
|
|
bool cursor_set = false;
|
|
|
|
while (segment -> hasNextSegment()) {
|
2007-04-15 19:22:54 +00:00
|
|
|
if (hasClickedOn(hover_point, segment -> secondPoint())) {
|
2007-02-15 20:00:20 +00:00
|
|
|
setCursor(Qt::CrossCursor);
|
|
|
|
cursor_set = true;
|
2007-04-15 19:22:54 +00:00
|
|
|
} else if (hasClickedOn(hover_point, segment -> middle())) {
|
2007-02-15 20:00:20 +00:00
|
|
|
setCursor(segment -> isVertical() ? Qt::SplitHCursor : Qt::SplitVCursor);
|
|
|
|
cursor_set = true;
|
|
|
|
}
|
|
|
|
segment = segment -> nextSegment();
|
|
|
|
}
|
|
|
|
if (!cursor_set) setCursor(Qt::ArrowCursor);
|
|
|
|
}
|
2007-04-15 19:22:54 +00:00
|
|
|
*/
|
2007-02-15 20:00:20 +00:00
|
|
|
QGraphicsPathItem::hoverMoveEvent(e);
|
|
|
|
}
|
|
|
|
|
2007-01-05 14:29:08 +00:00
|
|
|
/**
|
|
|
|
@return Le rectangle delimitant l'espace de dessin du conducteur
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
QRectF Conductor::boundingRect() const {
|
2007-01-05 14:29:08 +00:00
|
|
|
QRectF retour = QGraphicsPathItem::boundingRect();
|
|
|
|
retour.adjust(-5.0, -5.0, 5.0, 5.0);
|
|
|
|
return(retour);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
@return La forme / zone "cliquable" du conducteur
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
QPainterPath Conductor::shape() const {
|
2007-02-14 01:08:29 +00:00
|
|
|
QList<QPointF> points = segmentsToPoints();
|
2007-01-05 14:29:08 +00:00
|
|
|
QPainterPath area;
|
|
|
|
QPointF previous_point;
|
|
|
|
QPointF *point1, *point2;
|
|
|
|
foreach(QPointF point, points) {
|
|
|
|
if (!previous_point.isNull()) {
|
|
|
|
if (point.x() == previous_point.x()) {
|
|
|
|
if (point.y() <= previous_point.y()) {
|
|
|
|
point1 = &point;
|
|
|
|
point2 = &previous_point;
|
|
|
|
} else {
|
|
|
|
point1 = &previous_point;
|
|
|
|
point2 = &point;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (point.x() <= previous_point.x()) {
|
|
|
|
point1 = &point;
|
|
|
|
point2 = &previous_point;
|
|
|
|
} else {
|
|
|
|
point1 = &previous_point;
|
|
|
|
point2 = &point;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
qreal p1_x = point1 -> x();
|
|
|
|
qreal p1_y = point1 -> y();
|
|
|
|
qreal p2_x = point2 -> x();
|
|
|
|
qreal p2_y = point2 -> y();
|
|
|
|
area.setFillRule(Qt::OddEvenFill);
|
|
|
|
area.addRect(p1_x - 5.0, p1_y - 5.0, 10.0 + p2_x - p1_x, 10.0 + p2_y - p1_y);
|
|
|
|
}
|
|
|
|
previous_point = point;
|
|
|
|
area.setFillRule(Qt::WindingFill);
|
|
|
|
area.addRect(point.x() - 5.0, point.y() - 5.0, 10.0, 10.0);
|
|
|
|
}
|
|
|
|
return(area);
|
|
|
|
}
|
|
|
|
|
2007-04-09 02:56:47 +00:00
|
|
|
/**
|
|
|
|
Renvoie une valeur donnee apres l'avoir bornee entre deux autres valeurs,
|
|
|
|
en y ajoutant une marge interne.
|
|
|
|
@param tobound valeur a borner
|
|
|
|
@param bound1 borne 1
|
|
|
|
@param bound2 borne 2
|
2009-11-22 16:12:22 +00:00
|
|
|
@param space marge interne ajoutee
|
2007-04-09 02:56:47 +00:00
|
|
|
@return La valeur bornee
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
qreal Conductor::conductor_bound(qreal tobound, qreal bound1, qreal bound2, qreal space) {
|
2007-09-20 20:16:08 +00:00
|
|
|
qDebug() << "will bound" << tobound << "between" << bound1 << "and" << bound2 ;
|
2007-01-05 14:29:08 +00:00
|
|
|
if (bound1 < bound2) {
|
|
|
|
return(qBound(bound1 + space, tobound, bound2 - space));
|
|
|
|
} else {
|
|
|
|
return(qBound(bound2 + space, tobound, bound1 - space));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-09 02:56:47 +00:00
|
|
|
/**
|
|
|
|
Renvoie une valeur donnee apres l'avoir bornee avant ou apres une valeur.
|
|
|
|
@param tobound valeur a borner
|
|
|
|
@param bound borne
|
|
|
|
@param positive true pour borner la valeur avant la borne, false sinon
|
|
|
|
@return La valeur bornee
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
qreal Conductor::conductor_bound(qreal tobound, qreal bound, bool positive) {
|
2007-01-05 14:29:08 +00:00
|
|
|
qreal space = 5.0;
|
|
|
|
return(positive ? qMax(tobound, bound + space) : qMin(tobound, bound - space));
|
|
|
|
}
|
2007-02-14 01:08:29 +00:00
|
|
|
|
2007-04-09 02:56:47 +00:00
|
|
|
/**
|
2007-09-20 20:16:08 +00:00
|
|
|
@param type Type de Segments
|
2007-04-09 02:56:47 +00:00
|
|
|
@return Le nombre de segments composant le conducteur.
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
uint Conductor::nbSegments(QET::ConductorSegmentType type) const {
|
|
|
|
QList<ConductorSegment *> segments_list = segmentsList();
|
2007-09-20 20:16:08 +00:00
|
|
|
if (type == QET::Both) return(segments_list.count());
|
|
|
|
uint nb_seg = 0;
|
2007-10-03 17:02:39 +00:00
|
|
|
foreach(ConductorSegment *conductor_segment, segments_list) {
|
|
|
|
if (conductor_segment -> type() == type) ++ nb_seg;
|
2007-02-14 01:08:29 +00:00
|
|
|
}
|
|
|
|
return(nb_seg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Genere une liste de points a partir des segments de ce conducteur
|
|
|
|
@return La liste de points representant ce conducteur
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
QList<QPointF> Conductor::segmentsToPoints() const {
|
2007-02-14 01:08:29 +00:00
|
|
|
// liste qui sera retournee
|
|
|
|
QList<QPointF> points_list;
|
|
|
|
|
|
|
|
// on retourne la liste tout de suite s'il n'y a pas de segments
|
|
|
|
if (segments == NULL) return(points_list);
|
|
|
|
|
|
|
|
// recupere le premier point
|
|
|
|
points_list << segments -> firstPoint();
|
|
|
|
|
|
|
|
// parcourt les segments pour recuperer les autres points
|
2007-10-03 17:02:39 +00:00
|
|
|
ConductorSegment *segment = segments;
|
2007-02-14 01:08:29 +00:00
|
|
|
while(segment -> hasNextSegment()) {
|
|
|
|
points_list << segment -> secondPoint();
|
|
|
|
segment = segment -> nextSegment();
|
|
|
|
}
|
|
|
|
|
|
|
|
// recupere le dernier point
|
|
|
|
points_list << segment -> secondPoint();
|
|
|
|
|
|
|
|
//retourne la liste
|
|
|
|
return(points_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Regenere les segments de ce conducteur a partir de la liste de points passee en parametre
|
|
|
|
@param points_list Liste de points a utiliser pour generer les segments
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::pointsToSegments(QList<QPointF> points_list) {
|
2007-02-14 01:08:29 +00:00
|
|
|
// supprime les segments actuels
|
2007-11-04 17:37:10 +00:00
|
|
|
deleteSegments();
|
2007-02-14 01:08:29 +00:00
|
|
|
|
|
|
|
// cree les segments a partir de la liste de points
|
2007-10-03 17:02:39 +00:00
|
|
|
ConductorSegment *last_segment = NULL;
|
2007-02-14 01:08:29 +00:00
|
|
|
for (int i = 0 ; i < points_list.size() - 1 ; ++ i) {
|
2007-10-03 17:02:39 +00:00
|
|
|
last_segment = new ConductorSegment(points_list.at(i), points_list.at(i + 1), last_segment);
|
2007-02-14 01:08:29 +00:00
|
|
|
if (!i) segments = last_segment;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-04-09 02:56:47 +00:00
|
|
|
/**
|
|
|
|
Permet de savoir si un point est tres proche d'un autre. Cela sert surtout
|
|
|
|
pour determiner si un clic a ete effectue pres d'un point donne.
|
|
|
|
@param press_point Point effectivement clique
|
|
|
|
@param point point cliquable
|
|
|
|
@return true si l'on peut considerer que le point a ete clique, false sinon
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
bool Conductor::hasClickedOn(QPointF press_point, QPointF point) const {
|
2007-02-14 01:08:29 +00:00
|
|
|
return (
|
|
|
|
press_point.x() >= point.x() - 5.0 &&\
|
|
|
|
press_point.x() < point.x() + 5.0 &&\
|
|
|
|
press_point.y() >= point.y() - 5.0 &&\
|
|
|
|
press_point.y() < point.y() + 5.0
|
|
|
|
);
|
|
|
|
}
|
2007-02-18 19:33:15 +00:00
|
|
|
|
2007-04-09 02:56:47 +00:00
|
|
|
/**
|
|
|
|
Charge les caracteristiques du conducteur depuis un element XML.
|
|
|
|
@param e Un element XML
|
|
|
|
@return true si le chargement a reussi, false sinon
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
bool Conductor::fromXml(QDomElement &e) {
|
2007-10-01 14:08:11 +00:00
|
|
|
// recupere la "configuration" du conducteur
|
2007-10-14 14:44:33 +00:00
|
|
|
properties_.fromXml(e);
|
|
|
|
readProperties();
|
2009-11-29 21:56:48 +00:00
|
|
|
text_item -> setRotationAngle(e.attribute("rotation").toDouble());
|
2007-03-08 20:29:13 +00:00
|
|
|
|
2007-02-18 19:33:15 +00:00
|
|
|
// parcourt les elements XML "segment" et en extrait deux listes de longueurs
|
|
|
|
// les segments non valides sont ignores
|
|
|
|
QList<qreal> segments_x, segments_y;
|
|
|
|
for (QDomNode node = e.firstChild() ; !node.isNull() ; node = node.nextSibling()) {
|
|
|
|
// on s'interesse aux elements XML "segment"
|
|
|
|
QDomElement current_segment = node.toElement();
|
|
|
|
if (current_segment.isNull() || current_segment.tagName() != "segment") continue;
|
|
|
|
|
|
|
|
// le segment doit avoir une longueur
|
|
|
|
if (!current_segment.hasAttribute("length")) continue;
|
|
|
|
|
|
|
|
// cette longueur doit etre un reel
|
|
|
|
bool ok;
|
|
|
|
qreal segment_length = current_segment.attribute("length").toDouble(&ok);
|
|
|
|
if (!ok) continue;
|
|
|
|
|
|
|
|
if (current_segment.attribute("orientation") == "horizontal") {
|
|
|
|
segments_x << segment_length;
|
|
|
|
segments_y << 0.0;
|
|
|
|
} else {
|
|
|
|
segments_x << 0.0;
|
|
|
|
segments_y << segment_length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// s'il n'y a pas de segments, on renvoie true
|
|
|
|
if (!segments_x.size()) return(true);
|
|
|
|
// les longueurs recueillies doivent etre coherentes avec les positions des bornes
|
2007-02-18 21:45:44 +00:00
|
|
|
qreal width = 0.0, height = 0.0;
|
2007-02-18 19:33:15 +00:00
|
|
|
foreach (qreal t, segments_x) width += t;
|
|
|
|
foreach (qreal t, segments_y) height += t;
|
2007-10-03 17:02:39 +00:00
|
|
|
QPointF t1 = terminal1 -> amarrageConductor();
|
|
|
|
QPointF t2 = terminal2 -> amarrageConductor();
|
2007-09-20 20:16:08 +00:00
|
|
|
qreal expected_width = t2.x() - t1.x();
|
|
|
|
qreal expected_height = t2.y() - t1.y();
|
2009-04-18 17:23:04 +00:00
|
|
|
|
|
|
|
// on considere que le trajet est incoherent a partir d'une unite de difference avec l'espacement entre les bornes
|
2007-02-18 21:45:44 +00:00
|
|
|
if (
|
2009-04-18 17:23:04 +00:00
|
|
|
qAbs(expected_width - width) > 1.0 ||
|
|
|
|
qAbs(expected_height - height) > 1.0
|
|
|
|
) {
|
|
|
|
qDebug() << "Conductor::fromXml : les segments du conducteur ne semblent pas coherents - utilisation d'un trajet automatique";
|
|
|
|
return(false);
|
|
|
|
}
|
2007-02-18 19:33:15 +00:00
|
|
|
|
|
|
|
/* on recree les segments a partir des donnes XML */
|
|
|
|
// cree la liste de points
|
|
|
|
QList<QPointF> points_list;
|
2007-09-20 20:16:08 +00:00
|
|
|
points_list << t1;
|
2007-02-18 19:33:15 +00:00
|
|
|
for (int i = 0 ; i < segments_x.size() ; ++ i) {
|
|
|
|
points_list << QPointF(
|
|
|
|
points_list.last().x() + segments_x.at(i),
|
|
|
|
points_list.last().y() + segments_y.at(i)
|
|
|
|
);
|
|
|
|
}
|
2007-02-18 21:45:44 +00:00
|
|
|
|
2007-02-18 19:33:15 +00:00
|
|
|
pointsToSegments(points_list);
|
|
|
|
|
|
|
|
// initialise divers parametres lies a la modification des conducteurs
|
|
|
|
modified_path = true;
|
2007-09-28 21:58:42 +00:00
|
|
|
saveProfile(false);
|
2007-02-18 19:33:15 +00:00
|
|
|
|
|
|
|
segmentsToPath();
|
|
|
|
return(true);
|
|
|
|
}
|
|
|
|
|
2007-04-09 02:56:47 +00:00
|
|
|
/**
|
|
|
|
Exporte les caracteristiques du conducteur sous forme d'une element XML.
|
|
|
|
@param d Le document XML a utiliser pour creer l'element XML
|
|
|
|
@param table_adr_id Hash stockant les correspondances entre les ids des
|
|
|
|
bornes dans le document XML et leur adresse en memoire
|
|
|
|
@return Un element XML representant le conducteur
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
QDomElement Conductor::toXml(QDomDocument &d, QHash<Terminal *, int> &table_adr_id) const {
|
|
|
|
QDomElement e = d.createElement("conductor");
|
2007-03-09 19:18:55 +00:00
|
|
|
e.setAttribute("terminal1", table_adr_id.value(terminal1));
|
|
|
|
e.setAttribute("terminal2", table_adr_id.value(terminal2));
|
2007-02-24 18:37:07 +00:00
|
|
|
|
2007-02-18 19:33:15 +00:00
|
|
|
// on n'exporte les segments du conducteur que si ceux-ci ont
|
|
|
|
// ete modifies par l'utilisateur
|
2007-10-01 14:08:11 +00:00
|
|
|
if (modified_path) {
|
|
|
|
// parcours et export des segments
|
|
|
|
QDomElement current_segment;
|
2007-10-03 17:02:39 +00:00
|
|
|
foreach(ConductorSegment *segment, segmentsList()) {
|
2007-10-01 14:08:11 +00:00
|
|
|
current_segment = d.createElement("segment");
|
|
|
|
current_segment.setAttribute("orientation", segment -> isHorizontal() ? "horizontal" : "vertical");
|
2008-08-23 19:27:14 +00:00
|
|
|
current_segment.setAttribute("length", QString("%1").arg(segment -> length()));
|
2007-10-01 14:08:11 +00:00
|
|
|
e.appendChild(current_segment);
|
|
|
|
}
|
|
|
|
}
|
2007-02-18 19:33:15 +00:00
|
|
|
|
2007-10-01 14:08:11 +00:00
|
|
|
// exporte la "configuration" du conducteur
|
2009-04-03 19:30:25 +00:00
|
|
|
properties_.toXml(e);
|
2009-11-29 21:56:48 +00:00
|
|
|
if (text_item -> rotationAngle()) {
|
|
|
|
e.setAttribute("rotation", QString("%1").arg(text_item -> rotationAngle()));
|
|
|
|
}
|
2007-02-24 18:37:07 +00:00
|
|
|
return(e);
|
2007-02-18 19:33:15 +00:00
|
|
|
}
|
2007-03-08 20:29:13 +00:00
|
|
|
|
2007-09-20 20:16:08 +00:00
|
|
|
/// @return les segments de ce conducteur
|
2007-10-03 17:02:39 +00:00
|
|
|
const QList<ConductorSegment *> Conductor::segmentsList() const {
|
|
|
|
if (segments == NULL) return(QList<ConductorSegment *>());
|
2007-09-20 20:16:08 +00:00
|
|
|
|
2007-10-03 17:02:39 +00:00
|
|
|
QList<ConductorSegment *> segments_vector;
|
|
|
|
ConductorSegment *segment = segments;
|
2007-09-20 20:16:08 +00:00
|
|
|
|
|
|
|
while (segment -> hasNextSegment()) {
|
|
|
|
segments_vector << segment;
|
|
|
|
segment = segment -> nextSegment();
|
|
|
|
}
|
|
|
|
segments_vector << segment;
|
|
|
|
return(segments_vector);
|
|
|
|
}
|
|
|
|
|
2007-03-08 20:29:13 +00:00
|
|
|
/**
|
|
|
|
@return La longueur totale du conducteur
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
qreal Conductor::length() {
|
2007-03-08 20:29:13 +00:00
|
|
|
qreal length = 0.0;
|
|
|
|
|
2007-10-03 17:02:39 +00:00
|
|
|
ConductorSegment *s = segments;
|
2007-03-08 20:29:13 +00:00
|
|
|
while (s -> hasNextSegment()) {
|
|
|
|
length += qAbs(s -> length());
|
|
|
|
s = s -> nextSegment();
|
|
|
|
}
|
|
|
|
|
|
|
|
return(length);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
@return Le segment qui contient le point au milieu du conducteur
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
ConductorSegment *Conductor::middleSegment() {
|
2007-03-08 20:29:13 +00:00
|
|
|
if (segments == NULL) return(NULL);
|
|
|
|
|
|
|
|
qreal half_length = length() / 2.0;
|
|
|
|
|
2007-10-03 17:02:39 +00:00
|
|
|
ConductorSegment *s = segments;
|
2007-03-08 20:29:13 +00:00
|
|
|
qreal l = 0;
|
|
|
|
|
|
|
|
while (s -> hasNextSegment()) {
|
|
|
|
l += qAbs(s -> length());
|
|
|
|
if (l >= half_length) break;
|
|
|
|
s = s -> nextSegment();
|
|
|
|
}
|
|
|
|
// s est le segment qui contient le point au milieu du conducteur
|
|
|
|
return(s);
|
|
|
|
}
|
|
|
|
|
2007-04-09 02:56:47 +00:00
|
|
|
/**
|
|
|
|
Positionne le texte du conducteur au milieu du segment qui contient le
|
|
|
|
point au milieu du conducteur
|
|
|
|
@see middleSegment()
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::calculateTextItemPosition() {
|
2007-10-22 20:27:39 +00:00
|
|
|
if (properties_.type != ConductorProperties::Multi) return;
|
2007-03-08 20:29:13 +00:00
|
|
|
text_item -> setPos(middleSegment() -> middle());
|
|
|
|
}
|
2007-09-20 20:16:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
Sauvegarde le profil courant du conducteur pour l'utiliser ulterieurement
|
2007-10-03 17:02:39 +00:00
|
|
|
dans priv_modifieConductor.
|
2007-09-20 20:16:08 +00:00
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::saveProfile(bool undo) {
|
2007-10-22 20:27:39 +00:00
|
|
|
Qt::Corner current_path_type = currentPathType();
|
|
|
|
ConductorProfile old_profile(conductor_profiles[current_path_type]);
|
|
|
|
conductor_profiles[current_path_type].fromConductor(this);
|
2007-09-28 21:58:42 +00:00
|
|
|
Diagram *dia = diagram();
|
|
|
|
if (undo && dia) {
|
2007-10-22 20:27:39 +00:00
|
|
|
dia -> undoStack().push(new ChangeConductorCommand(this, old_profile, conductor_profiles[current_path_type], current_path_type));
|
2007-09-27 17:42:02 +00:00
|
|
|
}
|
2007-09-20 20:16:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
@param value1 Premiere valeur
|
|
|
|
@param value2 Deuxieme valeur
|
|
|
|
@return 1 si les deux valeurs sont de meme signe, -1 sinon
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
int Conductor::getCoeff(const qreal &value1, const qreal &value2) {
|
2007-09-20 20:16:08 +00:00
|
|
|
return(getSign(value1) * getSign(value2));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
@param value valeur
|
|
|
|
@return 1 si valeur est negatif, 1 s'il est positif ou nul
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
int Conductor::getSign(const qreal &value) {
|
2007-09-20 20:16:08 +00:00
|
|
|
return(value < 0 ? -1 : 1);
|
|
|
|
}
|
2007-09-27 17:42:02 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
Applique un nouveau profil a ce conducteur
|
|
|
|
@param cp Profil a appliquer a ce conducteur
|
2007-10-22 20:27:39 +00:00
|
|
|
@param path_type Type de trajet pour lequel ce profil convient
|
2007-09-27 17:42:02 +00:00
|
|
|
*/
|
2007-10-22 20:27:39 +00:00
|
|
|
void Conductor::setProfile(const ConductorProfile &cp, Qt::Corner path_type) {
|
|
|
|
conductor_profiles[path_type] = cp;
|
|
|
|
// si le type de trajet correspond a l'actuel
|
|
|
|
if (currentPathType() == path_type) {
|
|
|
|
if (conductor_profiles[path_type].isNull()) {
|
|
|
|
priv_calculeConductor(terminal1 -> amarrageConductor(), terminal1 -> orientation(), terminal2 -> amarrageConductor(), terminal2 -> orientation());
|
|
|
|
modified_path = false;
|
|
|
|
} else {
|
|
|
|
priv_modifieConductor(terminal1 -> amarrageConductor(), terminal1 -> orientation(), terminal2 -> amarrageConductor(), terminal2 -> orientation());
|
|
|
|
modified_path = true;
|
|
|
|
}
|
|
|
|
if (type() == ConductorProperties::Multi) {
|
|
|
|
calculateTextItemPosition();
|
|
|
|
}
|
2007-09-27 17:42:02 +00:00
|
|
|
}
|
|
|
|
}
|
2007-10-01 14:08:11 +00:00
|
|
|
|
2007-10-06 18:37:21 +00:00
|
|
|
/// @return le profil de ce conducteur
|
2007-10-22 20:27:39 +00:00
|
|
|
ConductorProfile Conductor::profile(Qt::Corner path_type) const {
|
|
|
|
return(conductor_profiles[path_type]);
|
2007-10-06 18:37:21 +00:00
|
|
|
}
|
|
|
|
|
2007-10-03 13:11:47 +00:00
|
|
|
/// @return le texte du conducteur
|
2007-10-03 17:02:39 +00:00
|
|
|
QString Conductor::text() const {
|
2007-10-03 13:11:47 +00:00
|
|
|
return(text_item -> toPlainText());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
@param t Nouveau texte du conducteur
|
|
|
|
*/
|
2007-10-03 17:02:39 +00:00
|
|
|
void Conductor::setText(const QString &t) {
|
2007-10-03 13:11:47 +00:00
|
|
|
text_item -> setPlainText(t);
|
2007-10-03 15:51:04 +00:00
|
|
|
text_item -> previous_text = t;
|
2007-10-03 13:11:47 +00:00
|
|
|
}
|
|
|
|
|
2007-10-14 14:44:33 +00:00
|
|
|
/// @param p les proprietes de ce conducteur
|
|
|
|
void Conductor::setProperties(const ConductorProperties &p) {
|
|
|
|
properties_ = p;
|
|
|
|
readProperties();
|
2007-10-01 14:08:11 +00:00
|
|
|
}
|
|
|
|
|
2007-10-14 14:44:33 +00:00
|
|
|
/// @return les proprietes de ce conducteur
|
|
|
|
ConductorProperties Conductor::properties() const {
|
|
|
|
return(properties_);
|
2007-10-01 14:08:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2007-10-14 14:44:33 +00:00
|
|
|
Relit les proprietes et les applique
|
2007-10-01 14:08:11 +00:00
|
|
|
*/
|
2007-10-14 14:44:33 +00:00
|
|
|
void Conductor::readProperties() {
|
2009-11-22 16:12:22 +00:00
|
|
|
// la couleur n'est vraiment applicable que lors du rendu du conducteur
|
2007-10-14 14:44:33 +00:00
|
|
|
setText(properties_.text);
|
|
|
|
text_item -> setVisible(properties_.type == ConductorProperties::Multi);
|
2007-10-12 18:33:28 +00:00
|
|
|
}
|
2007-10-20 19:27:16 +00:00
|
|
|
|
2008-07-09 21:14:30 +00:00
|
|
|
/**
|
|
|
|
Met a jour les proprietes du conducteur apres modification du champ de texte affiche
|
|
|
|
*/
|
|
|
|
void Conductor::displayedTextChanged() {
|
|
|
|
// verifie que le texte a reellement change
|
|
|
|
if (text_item -> toPlainText() == properties_.text) return;
|
|
|
|
|
|
|
|
// initialise l'objet UndoCommand correspondant
|
|
|
|
if (Diagram *my_diagram = diagram()) {
|
|
|
|
ConductorProperties new_properties(properties_);
|
|
|
|
new_properties.text = text_item -> toPlainText();
|
|
|
|
|
|
|
|
ChangeConductorPropertiesCommand *ccpc = new ChangeConductorPropertiesCommand(this);
|
|
|
|
ccpc -> setOldSettings(properties_);
|
|
|
|
ccpc -> setNewSettings(new_properties);
|
|
|
|
my_diagram -> undoStack().push(ccpc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-10-20 19:27:16 +00:00
|
|
|
/**
|
|
|
|
@return les conducteurs avec lesquels ce conducteur partage des bornes
|
|
|
|
communes
|
|
|
|
*/
|
|
|
|
QSet<Conductor *> Conductor::relatedConductors() const {
|
|
|
|
QList<Conductor *> other_conductors_list = terminal1 -> conductors();
|
|
|
|
other_conductors_list += terminal2 -> conductors();
|
|
|
|
QSet<Conductor *> other_conductors = other_conductors_list.toSet();
|
|
|
|
other_conductors.remove(const_cast<Conductor *>(this));
|
|
|
|
return(other_conductors);
|
|
|
|
}
|
|
|
|
|
2009-04-16 22:41:20 +00:00
|
|
|
/**
|
|
|
|
@param a reel
|
|
|
|
@param b reel
|
|
|
|
@param c reel
|
|
|
|
@return true si a est entre b et c ou est egal a l'un des deux
|
|
|
|
*/
|
|
|
|
bool isBetween(qreal a, qreal b, qreal c) {
|
|
|
|
if (b <= c) {
|
|
|
|
return(a >= b && a <= c);
|
|
|
|
} else {
|
|
|
|
return(a <= b && a >= c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
@param a point
|
|
|
|
@param b point
|
|
|
|
@param c point
|
|
|
|
@return true si le point a est contenu dans le rectangle delimite par les points b et c
|
|
|
|
*/
|
|
|
|
bool isContained(const QPointF &a, const QPointF &b, const QPointF &c) {
|
|
|
|
return(
|
|
|
|
isBetween(a.x(), b.x(), c.x()) &&
|
|
|
|
isBetween(a.y(), b.y(), c.y())
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2007-10-20 19:27:16 +00:00
|
|
|
/**
|
|
|
|
@return la liste des positions des jonctions avec d'autres conducteurs
|
|
|
|
*/
|
|
|
|
QList<QPointF> Conductor::junctions() const {
|
|
|
|
QList<QPointF> junctions_list;
|
|
|
|
|
|
|
|
// pour qu'il y ait des jonctions, il doit y avoir d'autres conducteurs et des bifurcations
|
|
|
|
QSet<Conductor *> other_conductors = relatedConductors();
|
|
|
|
QList<ConductorBend> bends_list = bends();
|
|
|
|
if (other_conductors.isEmpty() || bends_list.isEmpty()) {
|
|
|
|
return(junctions_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<QPointF> points = segmentsToPoints();
|
|
|
|
for (int i = 1 ; i < (points.size() -1) ; ++ i) {
|
|
|
|
QPointF point = points.at(i);
|
|
|
|
|
|
|
|
// determine si le point est une bifurcation ou non
|
|
|
|
bool is_bend = false;
|
2007-10-21 12:53:57 +00:00
|
|
|
Qt::Corner current_bend_type = Qt::TopLeftCorner;
|
2007-10-20 19:27:16 +00:00
|
|
|
foreach(ConductorBend cb, bends_list) {
|
|
|
|
if (cb.first == point) {
|
|
|
|
is_bend = true;
|
|
|
|
current_bend_type = cb.second;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// si le point n'est pas une bifurcation, il ne peut etre une jonction (enfin pas au niveau de ce conducteur)
|
|
|
|
if (!is_bend) continue;
|
|
|
|
|
|
|
|
bool is_junction = false;
|
|
|
|
QPointF scene_point = mapToScene(point);
|
|
|
|
foreach(Conductor *c, other_conductors) {
|
|
|
|
// exprime le point dans les coordonnees de l'autre conducteur
|
|
|
|
QPointF conductor_point = c -> mapFromScene(scene_point);
|
|
|
|
// recupere les segments de l'autre conducteur
|
|
|
|
QList<ConductorSegment *> c_segments = c -> segmentsList();
|
|
|
|
if (c_segments.isEmpty()) continue;
|
|
|
|
// parcoure les segments a la recherche d'un point commun
|
|
|
|
for (int j = 0 ; j < c_segments.count() ; ++ j) {
|
|
|
|
ConductorSegment *segment = c_segments[j];
|
|
|
|
// un point commun a ete trouve sur ce segment
|
2009-04-16 22:41:20 +00:00
|
|
|
if (isContained(conductor_point, segment -> firstPoint(), segment -> secondPoint())) {
|
2007-10-20 19:27:16 +00:00
|
|
|
is_junction = true;
|
|
|
|
// ce point commun ne doit pas etre une bifurcation identique a celle-ci
|
|
|
|
QList<ConductorBend> other_conductor_bends = c -> bends();
|
|
|
|
foreach(ConductorBend cb, other_conductor_bends) {
|
|
|
|
if (cb.first == conductor_point && cb.second == current_bend_type) {
|
|
|
|
is_junction = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (is_junction) junctions_list << point;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(junctions_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
@return la liste des bifurcations de ce conducteur ; ConductorBend est un
|
|
|
|
typedef pour une QPair\<QPointF, Qt::Corner\>. Le point indique la position
|
|
|
|
(en coordonnees locales) de la bifurcation tandis que le Corner indique le
|
|
|
|
type de bifurcation.
|
|
|
|
*/
|
|
|
|
QList<ConductorBend> Conductor::bends() const {
|
|
|
|
QList<ConductorBend> points;
|
|
|
|
if (!segments) return(points);
|
2007-12-16 12:19:04 +00:00
|
|
|
|
|
|
|
// recupere la liste des segments de taille non nulle
|
|
|
|
QList<ConductorSegment *> visible_segments;
|
2007-10-20 19:27:16 +00:00
|
|
|
ConductorSegment *segment = segments;
|
|
|
|
while (segment -> hasNextSegment()) {
|
2007-12-16 12:19:04 +00:00
|
|
|
if (!segment -> isPoint()) visible_segments << segment;
|
|
|
|
segment = segment -> nextSegment();
|
|
|
|
}
|
|
|
|
if (!segment -> isPoint()) visible_segments << segment;
|
|
|
|
|
|
|
|
ConductorSegment *next_segment;
|
|
|
|
for (int i = 0 ; i < visible_segments.count() -1 ; ++ i) {
|
|
|
|
segment = visible_segments[i];
|
|
|
|
next_segment = visible_segments[i + 1];
|
2007-10-20 19:27:16 +00:00
|
|
|
if (!segment -> isPoint() && !next_segment -> isPoint()) {
|
|
|
|
// si les deux segments ne sont pas dans le meme sens, on a une bifurcation
|
|
|
|
if (next_segment -> type() != segment -> type()) {
|
|
|
|
Qt::Corner bend_type;
|
|
|
|
qreal sl = segment -> length();
|
|
|
|
qreal nsl = next_segment -> length();
|
|
|
|
|
|
|
|
if (segment -> isHorizontal()) {
|
|
|
|
if (sl < 0 && nsl < 0) {
|
|
|
|
bend_type = Qt::BottomLeftCorner;
|
|
|
|
} else if (sl < 0 && nsl > 0) {
|
|
|
|
bend_type = Qt::TopLeftCorner;
|
|
|
|
} else if (sl > 0 && nsl < 0) {
|
|
|
|
bend_type = Qt::BottomRightCorner;
|
|
|
|
} else {
|
|
|
|
bend_type = Qt::TopRightCorner;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (sl < 0 && nsl < 0) {
|
|
|
|
bend_type = Qt::TopRightCorner;
|
|
|
|
} else if (sl < 0 && nsl > 0) {
|
|
|
|
bend_type = Qt::TopLeftCorner;
|
|
|
|
} else if (sl > 0 && nsl < 0) {
|
|
|
|
bend_type = Qt::BottomRightCorner;
|
|
|
|
} else {
|
|
|
|
bend_type = Qt::BottomLeftCorner;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
points << qMakePair(segment -> secondPoint(), bend_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(points);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
@param p Point, en coordonnees locales
|
|
|
|
@return true si le point p appartient au trajet du conducteur
|
|
|
|
*/
|
|
|
|
bool Conductor::containsPoint(const QPointF &p) const {
|
|
|
|
if (!segments) return(false);
|
|
|
|
ConductorSegment *segment = segments;
|
|
|
|
while (segment -> hasNextSegment()) {
|
|
|
|
QRectF rect(segment -> firstPoint(), segment -> secondPoint());
|
|
|
|
if (rect.contains(p)) return(true);
|
|
|
|
segment = segment -> nextSegment();
|
|
|
|
}
|
|
|
|
return(false);
|
|
|
|
}
|
2007-10-22 20:27:39 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
@param start Point de depart
|
|
|
|
@param end Point d'arrivee
|
|
|
|
@return le coin vers lequel se dirige le trajet de start vers end
|
|
|
|
*/
|
|
|
|
Qt::Corner Conductor::movementType(const QPointF &start, const QPointF &end) {
|
|
|
|
Qt::Corner result = Qt::BottomRightCorner;
|
|
|
|
if (start.x() <= end.x()) {
|
|
|
|
result = start.y() <= end.y() ? Qt::BottomRightCorner : Qt::TopRightCorner;
|
|
|
|
} else {
|
|
|
|
result = start.y() <= end.y() ? Qt::BottomLeftCorner : Qt::TopLeftCorner;
|
|
|
|
}
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// @return le type de trajet actuel de ce conducteur
|
|
|
|
Qt::Corner Conductor::currentPathType() const {
|
|
|
|
return(movementType(terminal1 -> amarrageConductor(), terminal2 -> amarrageConductor()));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// @return les profils de ce conducteur
|
|
|
|
ConductorProfilesGroup Conductor::profiles() const {
|
|
|
|
return(conductor_profiles);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
@param cpg Les nouveaux profils de ce conducteur
|
|
|
|
*/
|
|
|
|
void Conductor::setProfiles(const ConductorProfilesGroup &cpg) {
|
|
|
|
conductor_profiles = cpg;
|
|
|
|
if (conductor_profiles[currentPathType()].isNull()) {
|
|
|
|
priv_calculeConductor(terminal1 -> amarrageConductor(), terminal1 -> orientation(), terminal2 -> amarrageConductor(), terminal2 -> orientation());
|
|
|
|
modified_path = false;
|
|
|
|
} else {
|
|
|
|
priv_modifieConductor(terminal1 -> amarrageConductor(), terminal1 -> orientation(), terminal2 -> amarrageConductor(), terminal2 -> orientation());
|
|
|
|
modified_path = true;
|
|
|
|
}
|
|
|
|
if (type() == ConductorProperties::Multi) {
|
|
|
|
calculateTextItemPosition();
|
|
|
|
}
|
|
|
|
}
|
2007-11-04 17:37:10 +00:00
|
|
|
|
|
|
|
/// Supprime les segments
|
|
|
|
void Conductor::deleteSegments() {
|
|
|
|
if (segments != NULL) {
|
|
|
|
while (segments -> hasNextSegment()) delete segments -> nextSegment();
|
|
|
|
delete segments;
|
|
|
|
segments = NULL;
|
|
|
|
}
|
|
|
|
}
|