diff --git a/conducer.cpp b/conducer.cpp index 85bc90a37..3a66a94fe 100644 --- a/conducer.cpp +++ b/conducer.cpp @@ -1,5 +1,6 @@ #include #include "conducer.h" +#include "conducersegment.h" #include "element.h" bool Conducer::pen_and_brush_initialized = false; @@ -36,6 +37,7 @@ Conducer::Conducer(Terminal *p1, Terminal* p2, Element *parent, QGraphicsScene * pen_and_brush_initialized = true; } // calcul du rendu du conducteur + segments = NULL; priv_calculeConducer(terminal1 -> amarrageConducer(), terminal1 -> orientation(), terminal2 -> amarrageConducer(), terminal2 -> orientation()); setFlags(QGraphicsItem::ItemIsSelectable); } @@ -47,7 +49,7 @@ Conducer::Conducer(Terminal *p1, Terminal* p2, Element *parent, QGraphicsScene * void Conducer::update(const QRectF &rect) { // utilise soit la fonction priv_modifieConducteur soit la fonction priv_calculeConducteur void (Conducer::* fonction_update) (const QPointF &, Terminal::Orientation, const QPointF &, Terminal::Orientation); - fonction_update = (points.count() && modified_path) ? &Conducer::priv_modifieConducer : &Conducer::priv_calculeConducer; + fonction_update = (nbSegments() && modified_path) ? &Conducer::priv_modifieConducer : &Conducer::priv_calculeConducer; // appelle la bonne fonction pour calculer l'aspect du conducteur (this ->* fonction_update)( @@ -76,7 +78,7 @@ void Conducer::updateWithNewPos(const QRectF &rect, const Terminal *b, const QPo p1 = terminal1 -> amarrageConducer(); p2 = terminal2 -> amarrageConducer(); } - if (points.count() && modified_path) + if (nbSegments() && modified_path) priv_modifieConducer(p1, terminal1 -> orientation(), p2, terminal2 -> orientation()); else priv_calculeConducer(p1, terminal1 -> orientation(), p2, terminal2 -> orientation()); @@ -86,15 +88,27 @@ void Conducer::updateWithNewPos(const QRectF &rect, const Terminal *b, const QPo /** Genere le QPainterPath a partir de la liste des points */ -void Conducer::pointsToPath() { +void Conducer::segmentsToPath() { + // chemin qui sera dessine QPainterPath path; - bool moveto_done = false; - foreach(QPointF point, points) { - if (!moveto_done) { - path.moveTo(point); - moveto_done = true; - } else path.lineTo(point); + + // 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 + ConducerSegment *segment = segments; + while(segment -> hasNextSegment()) { + path.lineTo(segment -> secondPoint()); + segment = segment -> nextSegment(); } + + // termine le chemin + path.lineTo(segment -> secondPoint()); + + // affecte le chemin au conducteur setPath(path); } @@ -106,7 +120,7 @@ void Conducer::pointsToPath() { @param o2 Orientation de la borne 2 */ void Conducer::priv_modifieConducer(const QPointF &p1, Terminal::Orientation, const QPointF &p2, Terminal::Orientation) { - Q_ASSERT_X(points.count() > 1, "priv_modifieConducer", "pas de points a modifier"); + Q_ASSERT_X(nbSegments() > 1, "priv_modifieConducer", "pas de points a modifier"); // recupere les dernieres coordonnees connues des bornes QPointF old_p1 = mapFromScene(terminal1 -> amarrageConducer()); @@ -130,7 +144,8 @@ void Conducer::priv_modifieConducer(const QPointF &p1, Terminal::Orientation, co // genere les nouveaux points int limite = moves_x.size() - 1; int coeff = type_trajet_x ? 1 : -1; - points.clear(); + + QList points; points << (type_trajet_x ? new_p1 : new_p2); for (int i = 0 ; i < limite ; ++ i) { QPointF previous_point = points.last(); @@ -140,8 +155,8 @@ void Conducer::priv_modifieConducer(const QPointF &p1, Terminal::Orientation, co ); } points << (type_trajet_x ? new_p2 : new_p1); - - pointsToPath(); + pointsToSegments(points); + segmentsToPath(); } /** @@ -154,7 +169,10 @@ void Conducer::priv_modifieConducer(const QPointF &p1, Terminal::Orientation, co void Conducer::priv_calculeConducer(const QPointF &p1, Terminal::Orientation o1, const QPointF &p2, Terminal::Orientation o2) { QPointF sp1, sp2, depart, newp1, newp2, arrivee, depart0, arrivee0; Terminal::Orientation ori_depart, ori_arrivee; - points.clear(); + + // s'assure qu'il n'y a ni points + QList points; + type_trajet_x = p1.x() < p2.x(); // mappe les points par rapport a la scene sp1 = mapFromScene(p1); @@ -230,7 +248,8 @@ void Conducer::priv_calculeConducer(const QPointF &p1, Terminal::Orientation o1, // prolongement de la borne d'arrivee points << arrivee0; - pointsToPath(); + pointsToSegments(points); + segmentsToPath(); } /** @@ -272,7 +291,6 @@ void Conducer::paint(QPainter *qp, const QStyleOptionGraphicsItem */*qsogi*/, QW // affectation du QPen et de la QBrush modifies au QPainter qp -> setBrush(conducer_brush); - //qp -> setBrush(Qt::green); qp -> setPen(conducer_pen); if (isSelected()) { QPen tmp = qp -> pen(); @@ -286,9 +304,25 @@ void Conducer::paint(QPainter *qp, const QStyleOptionGraphicsItem */*qsogi*/, QW // dessin des points d'accroche du conducteur si celui-ci est selectionne if (isSelected()) { qp -> setRenderHint(QPainter::Antialiasing, true); + QList points = segmentsToPoints(); + QPointF previous_point; + QBrush square_brush(Qt::darkGreen); for (int i = 1 ; i < (points.size() -1) ; ++ i) { QPointF point = points.at(i); + + if (i > 1) { + qp -> fillRect( + QRectF( + ((previous_point.x() + point.x()) / 2.0 ) - 2.5, + ((previous_point.y() + point.y()) / 2.0 ) - 2.5, + 5.0, + 5.0 + ), + square_brush + ); + } qp -> drawEllipse(QRectF(point.x() - 3.0, point.y() - 3.0, 6.0, 6.0)); + previous_point = point; } } qp -> restore(); @@ -364,20 +398,29 @@ bool Conducer::valideXml(QDomElement &e){ void Conducer::mousePressEvent(QGraphicsSceneMouseEvent *e) { // clic gauche if (e -> buttons() & Qt::LeftButton) { + // recupere les coordonnees du clic press_point = mapFromScene(e -> pos()); - moving_point = false; - for (int i = 1 ; i < points.count() ; ++ i) { - QPointF point = points.at(i); - if ( - 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 - ) { + + /* + parcourt les segments pour determiner si le clic a eu lieu + - sur l'extremite d'un segment + - sur le milieu d'un segment + - ailleurs + */ + ConducerSegment *segment = segments; + while (segment -> hasNextSegment()) { + if (hasClickedOn(press_point, segment -> secondPoint())) { moving_point = true; - moved_point = i; + moving_segment = false; + moved_segment = segment; + break; + } else if (hasClickedOn(press_point, segment -> middle())) { + moving_point = false; + moving_segment = true; + moved_segment = segment; break; } + segment = segment -> nextSegment(); } } QGraphicsPathItem::mousePressEvent(e); @@ -398,103 +441,39 @@ void Conducer::mousePressEvent(QGraphicsSceneMouseEvent *e) { void Conducer::mouseMoveEvent(QGraphicsSceneMouseEvent *e) { // clic gauche if (e -> buttons() & Qt::LeftButton) { + // position pointee par la souris + qreal mouse_x = e -> pos().x(); + qreal mouse_y = e -> pos().y(); + if (moving_point) { - /* recuperation de quelques joyeusetes tres souvent consultees */ - // indice du dernier point ( = point non modifiable) - int ind_max_point = points.count() - 1; - + // la modification par points revient bientot + /* // position precedente du point - QPointF p = points.at(moved_point); + QPointF p = moved_segment -> secondPoint(); qreal p_x = p.x(); qreal p_y = p.y(); - // position pointee par la souris - qreal mouse_x = e -> pos().x(); - qreal mouse_y = e -> pos().y(); - - // position du point apres le deplacement - qreal new_pos_x; - qreal new_pos_y; - - if (moved_point == 1 || moved_point == ind_max_point - 1) { - /* premier et dernier points modifiables du conducteur */ - // repere le point qui va imposer la contrainte de base - int ind_depend = moved_point == 1 ? 0 : ind_max_point; - qreal depend_x = points.at(ind_depend).x(); - qreal depend_y = points.at(ind_depend).y(); - - // repere le point voisin suivant - int ind_voisin = moved_point == 1 ? 2 : moved_point - 1; - qreal voisin_x = points.at(ind_voisin).x(); - qreal voisin_y = points.at(ind_voisin).y(); - - if (p_x == depend_x && p_y != depend_y) { - // deplacements limites a l'axe vertical - new_pos_x = p_x; - // si on peut aller plus loin que le point voisin suivant, on le fait... en deplacant le point voisin - if (p_x > voisin_x - 1 && p_x < voisin_x + 1) new_pos_y = conducer_bound(mouse_y, depend_y, voisin_y); - else { - new_pos_y = conducer_bound(mouse_y, depend_y, depend_y < voisin_y); - points.replace(ind_voisin, QPointF(voisin_x, new_pos_y)); - } - } else { - // deplacements limites a l'axe horizontal - // si on peut aller plus loin que le point voisin suivant, on le fait... en deplacant le point voisin - if (p_y > voisin_y - 1 && p_y < voisin_y + 1) new_pos_x = conducer_bound(mouse_x, depend_x, voisin_x); - else { - new_pos_x = conducer_bound(mouse_x, depend_x, depend_x < voisin_x); - points.replace(ind_voisin, QPointF(new_pos_x, voisin_y)); - } - new_pos_y = p_y; - } - } else { - /* autres points */ - new_pos_x = mouse_x; - new_pos_y = mouse_y; - - /* deplace les deux points voisins (sans cela, le deplacement du point n'est pas possible) */ - // point precedent - int ind_point_precedent = moved_point - 1; - qreal pp_x = points.at(ind_point_precedent).x(); - qreal pp_y = points.at(ind_point_precedent).y(); - if (ind_point_precedent != 1) { - if (pp_x > p_x - 1 && pp_x < p_x + 1) { - points.replace(ind_point_precedent, QPointF(new_pos_x, pp_y)); - } else { - points.replace(ind_point_precedent, QPointF(pp_x, new_pos_y)); - } - } else { - if (pp_x > p_x - 1 && pp_x < p_x + 1) { - new_pos_x = p_x; - } else { - new_pos_y = p_y; - } - } - - // point suivant - int ind_point_suivant = moved_point + 1; - qreal ps_x = points.at(ind_point_suivant).x(); - qreal ps_y = points.at(ind_point_suivant).y(); - if (ind_point_suivant != ind_max_point - 1) { - if (ps_x > p_x - 1 && ps_x < p_x + 1) { - points.replace(ind_point_suivant, QPointF(new_pos_x, ps_y)); - } else { - points.replace(ind_point_suivant, QPointF(ps_x, new_pos_y)); - } - } else { - if (ps_x > p_x - 1 && ps_x < p_x + 1) { - new_pos_x = p_x; - } else { - new_pos_y = p_y; - } - } - } + // calcul du deplacement + moved_segment -> moveX(mouse_x - p_x()); + moved_segment -> moveY(mouse_y - p_y()); // application du deplacement modified_path = true; - points.replace(moved_point, QPointF(new_pos_x, new_pos_y)); updatePoints(); - pointsToPath(); + segmentsToPath(); + */ + } else if (moving_segment) { + // position precedente du point + QPointF p = moved_segment -> middle(); + + // calcul du deplacement + moved_segment -> moveX(mouse_x - p.x()); + moved_segment -> moveY(mouse_y - p.y()); + + // application du deplacement + modified_path = true; + updatePoints(); + segmentsToPath(); } } QGraphicsPathItem::mouseMoveEvent(e); @@ -506,10 +485,9 @@ void Conducer::mouseMoveEvent(QGraphicsSceneMouseEvent *e) { */ void Conducer::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) { // clic gauche - if (e -> buttons() & Qt::LeftButton) { - moving_point = false; - QGraphicsPathItem::mouseReleaseEvent(e); - } + moving_point = false; + moving_segment = false; + QGraphicsPathItem::mouseReleaseEvent(e); } /** @@ -525,6 +503,7 @@ QRectF Conducer::boundingRect() const { @return La forme / zone "cliquable" du conducteur */ QPainterPath Conducer::shape() const { + QList points = segmentsToPoints(); QPainterPath area; QPointF previous_point; QPointF *point1, *point2; @@ -565,6 +544,7 @@ QPainterPath Conducer::shape() const { Met à jour deux listes de reels. */ void Conducer::updatePoints() { + QList points = segmentsToPoints(); int s = points.size(); moves_x.clear(); moves_y.clear(); @@ -591,3 +571,74 @@ qreal Conducer::conducer_bound(qreal tobound, qreal bound, bool positive) { qreal space = 5.0; return(positive ? qMax(tobound, bound + space) : qMin(tobound, bound - space)); } + +int Conducer::nbSegments() { + if (segments == NULL) return(0); + int nb_seg = 1; + ConducerSegment *segment = segments; + while (segment -> hasNextSegment()) { + ++ nb_seg; + segment = segment -> nextSegment(); + } + return(nb_seg); +} + +/** + Genere une liste de points a partir des segments de ce conducteur + @return La liste de points representant ce conducteur +*/ +QList Conducer::segmentsToPoints() const { + // liste qui sera retournee + QList 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 + ConducerSegment *segment = segments; + 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 +*/ +void Conducer::pointsToSegments(QList points_list) { + // supprime les segments actuels + if (segments != NULL) { + ConducerSegment *segment = segments; + while (segment -> hasNextSegment()) { + ConducerSegment *nextsegment = segment -> nextSegment(); + delete segment; + segment = nextsegment; + } + } + + // cree les segments a partir de la liste de points + ConducerSegment *last_segment = NULL; + for (int i = 0 ; i < points_list.size() - 1 ; ++ i) { + last_segment = new ConducerSegment(points_list.at(i), points_list.at(i + 1), last_segment); + if (!i) segments = last_segment; + } +} + +bool Conducer::hasClickedOn(QPointF press_point, QPointF point) { + 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 + ); +} diff --git a/conducer.h b/conducer.h index 5616c39d5..953800d07 100644 --- a/conducer.h +++ b/conducer.h @@ -2,6 +2,7 @@ #define CONDUCTEUR_H #include #include "terminal.h" + class ConducerSegment; class Element; /** Cette classe represente un conducteur. Un conducteur relie deux bornes d'element. @@ -35,7 +36,7 @@ private: /// booleen indiquant si le fil est encore valide bool destroyed; - QList points; + ConducerSegment *segments; QList moves_x; QList moves_y; qreal orig_dist_2_terms_x; @@ -43,16 +44,23 @@ bool type_trajet_x; QPointF press_point; bool moving_point; + bool moving_segment; int moved_point; + ConducerSegment *moved_segment; bool modified_path; static QPen conducer_pen; static QBrush conducer_brush; static bool pen_and_brush_initialized; - void pointsToPath(); + void segmentsToPath(); void updatePoints(); void priv_calculeConducer(const QPointF &, Terminal::Orientation, const QPointF &, Terminal::Orientation); void priv_modifieConducer(const QPointF &, Terminal::Orientation, const QPointF &, Terminal::Orientation); + int nbSegments(); + + QList segmentsToPoints() const; + void pointsToSegments(QList); + bool hasClickedOn(QPointF, QPointF); static QPointF extendTerminal(const QPointF &, Terminal::Orientation, qreal = 12.0); static bool surLeMemeAxe(Terminal::Orientation, Terminal::Orientation); static bool estHorizontale(Terminal::Orientation a); diff --git a/conducersegment.cpp b/conducersegment.cpp new file mode 100644 index 000000000..dc1db4a3f --- /dev/null +++ b/conducersegment.cpp @@ -0,0 +1,478 @@ +#include "conducersegment.h" +#include + +ConducerSegment::ConducerSegment(QPointF p1, QPointF p2, ConducerSegment *cs1, ConducerSegment *cs2) { + setFirstPoint(p1); + setSecondPoint(p2); + setPreviousSegment(cs1); + setNextSegment(cs2); +} + +ConducerSegment::~ConducerSegment() { + if (hasPreviousSegment()) previousSegment() -> setNextSegment(nextSegment()); + if (hasNextSegment()) nextSegment() -> setPreviousSegment(previousSegment()); +} + + +/** + Permet de savoir s'il est possible de deplacer le premier point du segment + sans creer d'incoherence. La valeur du mouvement maximum qu'il est possible de faire + sans incoherence est stockee dans le second parametre. + @param asked_dx La valeur du mouvement demande + @param possible_dx La valeur du mouvement possible (au maximum) + @return true si le mouvement est possible ; false s'il doit etre limite +*/ +bool ConducerSegment::canMove1stPointX(qreal asked_dx, qreal &possible_dx) { + + Q_ASSERT_X(isVertical(), "ConducerSegment::canMove1stPointX", "segment non vertical"); + + /// On ne bouge jamais le premier point d'un segment statique. + if (!hasPreviousSegment()) { + possible_dx = 0.0; + return(false); + } + // a ce stade, on a forcement un segment precedent + + /// Si le segment precedent n'est pas statique, le mouvement est possible. + if (previous_segment -> hasPreviousSegment()) { + possible_dx = asked_dx; + return(true); + } + // a ce stade, le segment precedent est forcement statique + + /// Si le segment precedent est vertical, le mouvement est possible : + /// il induira la creation d'un segment horizontal supplementaire. + if (previous_segment -> isVertical()) { + possible_dx = asked_dx; + return(true); + } + // a ce stade, le segment precedent est forcement horizontal + + // recupere quelques donnees + qreal prev_segment_first_x = previous_segment -> point1.x(); + qreal first_x = point1.x(); + + /// Il se peut que le mouvement doive etre limite de facon a ce + /// que le segment statique conserve une taille minimale. + if (previous_segment -> length() > 0.0) { + if (first_x + asked_dx < prev_segment_first_x + 12.0) { + possible_dx = -(first_x - prev_segment_first_x - 12.0); + return(false); + } else { + possible_dx = asked_dx; + return(true); + } + } else { + if (first_x + asked_dx >= prev_segment_first_x - 12.0) { + possible_dx = prev_segment_first_x - 12.0 - first_x; + return(false); + } else { + possible_dx = asked_dx; + return(true); + } + } +} + +/** + Permet de savoir s'il est possible de deplacer le second point du segment + sans creer d'incoherence. La valeur du mouvement maximum qu'il est possible de faire + sans incoherence est stockee dans le second parametre. + @param asked_dx La valeur du mouvement demande + @param possible_dx La valeur du mouvement possible (au maximum) + @return true si le mouvement est possible ; false s'il doit etre limite +*/ +bool ConducerSegment::canMove2ndPointX(qreal asked_dx, qreal &possible_dx) { + + Q_ASSERT_X(isVertical(), "ConducerSegment::canMove2ndPointX", "segment non vertical"); + + /// On ne modifie jamais l'ordonnee du second point d'un segment statique. + if (!hasNextSegment()) { + possible_dx = 0.0; + return(false); + } + // a ce stade, on a forcement un segment suivant + + /// Si le segment suivant n'est pas statique, le mouvement est possible. + if (next_segment -> hasNextSegment()) { + possible_dx = asked_dx; + return(true); + } + // a ce stade, le segment suivant est forcement statique + + /// Si le segment suivant est vertical, le mouvement est possible : + /// il induira la creation d'un segment horizontal supplementaire. + if (next_segment -> isVertical()) { + possible_dx = asked_dx; + return(true); + } + // a ce stade, le segment suivant est forcement horizontal + + // recupere quelques donnees + qreal next_segment_second_x = next_segment -> point2.x(); + qreal second_x = point2.x(); + + /// Il se peut que le mouvement doive etre limite de facon a ce + /// que le segment statique conserve une taille minimale. + if (next_segment -> length() < 0.0) { + if (second_x + asked_dx < next_segment_second_x + 12.0) { + possible_dx = second_x - next_segment_second_x - 12.0; + return(false); + } else { + possible_dx = asked_dx; + return(true); + } + } else { + if (second_x + asked_dx >= next_segment_second_x - 12.0) { + possible_dx = next_segment_second_x - 12.0 - second_x; + return(false); + } else { + possible_dx = asked_dx; + return(true); + } + } +} + +/** + Permet de savoir s'il est possible de deplacer le premier point du segment + sans creer d'incoherence. La valeur du mouvement maximum qu'il est possible de faire + sans incoherence est stockee dans le second parametre. + @param asked_dy La valeur du mouvement demande + @param possible_dy La valeur du mouvement possible (au maximum) + @return true si le mouvement est possible ; false s'il doit etre limite +*/ +bool ConducerSegment::canMove1stPointY(qreal asked_dy, qreal &possible_dy) { + + Q_ASSERT_X(isHorizontal(), "ConducerSegment::canMove1stPointY", "segment non horizontal"); + + /// On ne bouge jamais le premier point d'un segment statique. + if (!hasPreviousSegment()) { + possible_dy = 0.0; + return(false); + } + // a ce stade, on a forcement un segment precedent + + /// Si le segment precedent n'est pas statique, le mouvement est possible. + if (previous_segment -> hasPreviousSegment()) { + possible_dy = asked_dy; + return(true); + } + // a ce stade, le segment precedent est forcement statique + + /// Si le segment precedent est horizontal, le mouvement est possible : + /// il induira la creation d'un segment vertical supplementaire. + if (previous_segment -> isHorizontal()) { + possible_dy = asked_dy; + return(true); + } + // a ce stade, le segment precedent est forcement vertical + + // recupere quelques donnees + qreal prev_segment_first_y = previous_segment -> point1.y(); + qreal first_y = point1.y(); + + /// Il se peut que le mouvement doive etre limite de facon a ce + /// que le segment statique conserve une taille minimale. + if (previous_segment -> length() > 0.0) { + if (first_y + asked_dy < prev_segment_first_y + 12.0) { + possible_dy = -(first_y - prev_segment_first_y - 12.0); + return(false); + } else { + possible_dy = asked_dy; + return(true); + } + } else { + if (first_y + asked_dy >= prev_segment_first_y - 12.0) { + possible_dy = prev_segment_first_y - 12.0 - first_y; + return(false); + } else { + possible_dy = asked_dy; + return(true); + } + } +} + +/** + Permet de savoir s'il est possible de deplacer le second point du segment + sans creer d'incoherence. La valeur du mouvement maximum qu'il est possible de faire + sans incoherence est stockee dans le second parametre. + @param asked_dy La valeur du mouvement demande + @param possible_dy La valeur du mouvement possible (au maximum) + @return true si le mouvement est possible ; false s'il doit etre limite +*/ +bool ConducerSegment::canMove2ndPointY(qreal asked_dy, qreal &possible_dy) { + + Q_ASSERT_X(isHorizontal(), "ConducerSegment::canMove2ndPointY", "segment non horizontal"); + + /// On ne modifie jamais l'abscisse du second point d'un segment statique. + if (!hasNextSegment()) { + possible_dy = 0.0; + return(false); + } + // a ce stade, on a forcement un segment suivant + + /// Si le segment suivant n'est pas statique, le mouvement est possible. + if (next_segment -> hasNextSegment()) { + possible_dy = asked_dy; + return(true); + } + // a ce stade, le segment suivant est forcement statique + + /// Si le segment suivant est horizontal, le mouvement est possible : + /// il induira la creation d'un segment vertical supplementaire. + if (next_segment -> isHorizontal()) { + possible_dy = asked_dy; + return(true); + } + // a ce stade, le segment suivant est forcement vertical + + // recupere quelques donnees + qreal next_segment_second_y = next_segment -> point2.y(); + qreal second_y = point2.y(); + + /// Il se peut que le mouvement doive etre limite de facon a ce + /// que le segment statique conserve une taille minimale. + if (next_segment -> length() < 0.0) { + if (second_y + asked_dy < next_segment_second_y + 12.0) { + possible_dy = second_y - next_segment_second_y - 12.0; + return(false); + } else { + possible_dy = asked_dy; + return(true); + } + } else { + if (second_y + asked_dy >= next_segment_second_y - 12.0) { + possible_dy = next_segment_second_y - 12.0 - second_y; + return(false); + } else { + possible_dy = asked_dy; + return(true); + } + } +} + +void ConducerSegment::moveX(qreal dx) { + if (isHorizontal()) return; + Q_ASSERT_X(isVertical(), "ConducerSegment::moveX", "segment non vertical"); + + bool has_prev_segment = hasPreviousSegment(); + bool has_next_segment = hasNextSegment(); + + if (!has_prev_segment || !has_next_segment) return; + + // determine si le mouvement demande doit etre limite et, le cas echeant, a quelle valeur il faut le limiter + qreal real_dx_for_1st_point = 0.0; + qreal real_dx_for_2nd_point = 0.0; + canMove1stPointX(dx, real_dx_for_1st_point); + canMove2ndPointX(dx, real_dx_for_2nd_point); + + qreal final_movement = (dx <= 0.0) ? qMax(real_dx_for_1st_point, real_dx_for_2nd_point) : qMin(real_dx_for_1st_point, real_dx_for_2nd_point); + + // applique le mouvement au premier point + if (has_prev_segment) { + point1.rx() += final_movement; + if (!previous_segment -> hasPreviousSegment() && previous_segment -> isVertical()) { + new ConducerSegment( + previous_segment -> point2, + point1, + previous_segment, + this + ); + } else previous_segment -> setSecondPoint(point1); + } + + // applique le mouvement au second point + if (has_next_segment) { + point2.rx() += final_movement; + if (!next_segment -> hasNextSegment() && next_segment -> isVertical()) { + new ConducerSegment( + point2, + next_segment -> point1, + this, + next_segment + ); + } else next_segment -> setFirstPoint(point2); + } +} + +void ConducerSegment::moveY(qreal dy) { + if (isVertical()) return; + Q_ASSERT_X(isHorizontal(), "ConducerSegment::moveY", "segment non horizontal"); + + bool has_prev_segment = hasPreviousSegment(); + bool has_next_segment = hasNextSegment(); + + if (!has_prev_segment || !has_next_segment) return; + + // determine si le mouvement demande doit etre limite et, le cas echeant, a quelle valeur il faut le limiter + qreal real_dy_for_1st_point = 0.0; + qreal real_dy_for_2nd_point = 0.0; + canMove1stPointY(dy, real_dy_for_1st_point); + canMove2ndPointY(dy, real_dy_for_2nd_point); + + qreal final_movement = (dy <= 0.0) ? qMax(real_dy_for_1st_point, real_dy_for_2nd_point) : qMin(real_dy_for_1st_point, real_dy_for_2nd_point); + + // applique le mouvement au premier point + if (has_prev_segment) { + point1.ry() += final_movement; + if (!previous_segment -> hasPreviousSegment() && previous_segment -> isHorizontal()) { + new ConducerSegment( + previous_segment -> point2, + point1, + previous_segment, + this + ); + } else previous_segment -> setSecondPoint(point1); + } + + // applique le mouvement au second point + if (has_next_segment) { + point2.ry() += final_movement; + if (!next_segment -> hasNextSegment() && next_segment -> isHorizontal()) { + new ConducerSegment( + point2, + next_segment -> point1, + this, + next_segment + ); + } else next_segment -> setFirstPoint(point2); + } +} +/* +void ConducerSegment::moveY(qreal dy) { + if (isVertical()) return; + Q_ASSERT_X(isHorizontal(), "ConducerSegment::moveY", "segment non horizontal"); + + bool has_prev_segment = hasPreviousSegment(); + bool has_next_segment = hasNextSegment(); + + if (!has_prev_segment || !has_next_segment) return; + + // s'il y a un segment precedent + if (has_prev_segment) { + // et que celui-ci est statique, + if (!previous_segment -> hasPreviousSegment()) { + // on agit differemment selon que le segment statique est vertical... + if (previous_segment -> isVertical()) { + // auquel cas, on limite le deplacement + qreal real_dy; + /// @todo limiter le deplacement + real_dy = 0.0; + point1.ry() += real_dy; + previous_segment -> setSecondPoint(point1); + } else { + // ou horizontal : auquel cas, on ajoute un segment vertical + point1.ry() += dy; + new ConducerSegment( + previous_segment -> point2, + point1, + previous_segment, + this + ); + } + } else { + // si le segment n'est pas statique, on applique le deplacement + point1.ry() += dy; + previous_segment -> setSecondPoint(point1); + } + } + + // s'il y a un segment suivant + if (has_next_segment) { + // et que celui-ci est statique, + if (!next_segment -> hasNextSegment()) { + // on agit differemment selon que le segment statique est vertical... + if (next_segment -> isVertical()) { + // auquel cas, on limite le deplacement + qreal real_dy; + /// @todo limiter le deplacement + real_dy = 0.0; + point2.ry() += real_dy; + next_segment -> setFirstPoint(point2); + } else { + // ou horizontal : auquel cas, on ajoute un segment horizontal + point2.ry() += dy; + new ConducerSegment( + point2, + next_segment -> point1, + this, + next_segment + ); + } + } else { + // si le segment n'est pas statique, on applique le deplacement + point2.ry() += dy; + next_segment -> setFirstPoint(point2); + } + } +} +*/ +void ConducerSegment::setPreviousSegment(ConducerSegment *ps) { + previous_segment = ps; + if (hasPreviousSegment()) { + if (previousSegment() -> nextSegment() != this) previousSegment() -> setNextSegment(this); + } +} + +void ConducerSegment::setNextSegment(ConducerSegment *ns) { + next_segment = ns; + if (hasNextSegment()) { + if (nextSegment() -> previousSegment() != this) nextSegment() -> setPreviousSegment(this); + } +} + +ConducerSegment *ConducerSegment::previousSegment() { + return(previous_segment); +} + +ConducerSegment *ConducerSegment::nextSegment() { + return(next_segment); +} + +bool ConducerSegment::isVertical() { + return(point1.x() == point2.x()); +} + +bool ConducerSegment::isHorizontal() { + return(point1.y() == point2.y()); +} + +QPointF ConducerSegment::firstPoint() { + return(point1); +} + +QPointF ConducerSegment::secondPoint() { + return(point2); +} + +void ConducerSegment::setFirstPoint(QPointF p) { + point1 = p; +} + +void ConducerSegment::setSecondPoint(QPointF p) { + point2 = p; +} + +bool ConducerSegment::hasPreviousSegment() { + return(previous_segment != NULL); +} + +bool ConducerSegment::hasNextSegment() { + return(next_segment != NULL); +} + +QPointF ConducerSegment::middle() { + return( + QPointF( + (point1.x() + point2.x()) / 2.0, + (point1.y() + point2.y()) / 2.0 + ) + ); +} + +qreal ConducerSegment::length() { + if (isHorizontal()) { + return(secondPoint().x() - firstPoint().x()); + } else { + return(secondPoint().y() - firstPoint().y()); + } +} diff --git a/conducersegment.h b/conducersegment.h new file mode 100644 index 000000000..2d8aaa5c7 --- /dev/null +++ b/conducersegment.h @@ -0,0 +1,43 @@ +#ifndef CONDUCER_SEGMENT_H + #define CONDUCER_SEGMENT_H + #include + /** + Cette classe represente un segment de conducteur. + */ + class ConducerSegment { + // constructeurs et destructeur + public: + ConducerSegment(QPointF, QPointF, ConducerSegment * = NULL, ConducerSegment * = NULL); + ~ConducerSegment(); + + // attributs + private: + ConducerSegment *previous_segment; + ConducerSegment *next_segment; + QPointF point1; + QPointF point2; + + // methodes + public: + void moveX(qreal); + void moveY(qreal); + ConducerSegment *previousSegment(); + ConducerSegment *nextSegment(); + bool hasPreviousSegment(); + bool hasNextSegment(); + void setPreviousSegment(ConducerSegment *); + void setNextSegment(ConducerSegment *); + QPointF firstPoint(); + QPointF secondPoint(); + void setFirstPoint(QPointF); + void setSecondPoint(QPointF); + QPointF middle(); + bool isHorizontal(); + bool isVertical(); + qreal length(); + bool canMove1stPointX(qreal, qreal &); + bool canMove2ndPointX(qreal, qreal &); + bool canMove1stPointY(qreal, qreal &); + bool canMove2ndPointY(qreal, qreal &); + }; +#endif diff --git a/qelectrotech.pro b/qelectrotech.pro index f96f5da3b..94be790bf 100644 --- a/qelectrotech.pro +++ b/qelectrotech.pro @@ -19,7 +19,8 @@ HEADERS += aboutqet.h \ exportdialog.h \ fixedelement.h \ qetapp.h \ - terminal.h + terminal.h \ + conducersegment.h SOURCES += aboutqet.cpp \ borderinset.cpp \ conducer.cpp \ @@ -32,7 +33,8 @@ SOURCES += aboutqet.cpp \ fixedelement.cpp \ main.cpp \ qetapp.cpp \ - terminal.cpp + terminal.cpp \ + conducersegment.cpp RESOURCES += qelectrotech.qrc TRANSLATIONS += lang/qet_en.ts lang/qt_fr.ts QT += xml diff --git a/terminal.cpp b/terminal.cpp index 1ff1330e1..90979d2a2 100644 --- a/terminal.cpp +++ b/terminal.cpp @@ -170,8 +170,10 @@ void Terminal::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) { t.setColor(couleur_hovered); p -> setPen(t); p -> setBrush(couleur_hovered); - if (hovered) p -> drawEllipse(((int)f.x())-2, ((int)f.y())-2, 5, 5); - else p -> drawPoint(f); + if (hovered) { + p -> setRenderHint(QPainter::Antialiasing, true); + p -> drawEllipse(((int)f.x())-2, ((int)f.y())-2, 5, 5); + } else p -> drawPoint(f); p -> restore(); }