Salome HOME
- clean programming code
[modules/geom.git] / src / DependencyTree / DependencyTree_Arrow.cxx
1 // Copyright (C) 2014  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 // internal includes
21 #include "DependencyTree_Arrow.h"
22 #include "DependencyTree_Object.h"
23
24 // GUI includes
25 #include <SUIT_Session.h>
26 #include <SUIT_ResourceMgr.h>
27
28 // Qt includes
29 #include <QPainter>
30
31 #include <math.h>
32
33 const qreal arrowSize = 20;
34
35 DependencyTree_Arrow::DependencyTree_Arrow( DependencyTree_Object* theStartItem,
36                                             DependencyTree_Object* theEndItem,
37                                             QGraphicsItem* parent, QGraphicsScene* scene )
38 :QGraphicsLineItem( parent, scene ),
39 myIsBiLink( false ),
40 myStartItem( theStartItem ),
41 myEndItem( theEndItem )
42 {
43   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
44
45   myColor = resMgr->colorValue( "Geometry", "dependency_tree_arrow_color", QColor( 0, 0, 130 ) );
46   myHighlightColor = resMgr->colorValue( "Geometry", "dependency_tree_highlight_arrow_color", QColor( 0, 0, 255 ) );
47   mySelectColor = resMgr->colorValue( "Geometry", "dependency_tree_select_arrow_color", QColor( 255, 0, 0 ) );
48
49   myStartItem = theStartItem;
50   myEndItem = theEndItem;
51
52   myLine = QLineF( myStartItem->pos(), myEndItem->pos() );
53   myArrowHead  = createArrowHead( myStartItem->pos(), myEndItem->pos() );
54   myReverseArrowHead = createArrowHead( myEndItem->pos(), myStartItem->pos() );
55
56   mySelfDependencyArrow = QRectF();
57
58   setPen( QPen( myColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
59   setColor( myColor );
60
61   setFlag( QGraphicsItem::ItemIsSelectable, true );
62   setZValue( -1000.0 );
63 }
64
65 DependencyTree_Arrow::~DependencyTree_Arrow()
66 {
67 }
68
69 //=================================================================================
70 // function : boundingRect()
71 // purpose  : return the outer bounds of the item as a rectangle.
72 //            QGraphicsView uses this to determine whether the item requires redrawing
73 //=================================================================================
74 QRectF DependencyTree_Arrow::boundingRect() const
75 {
76   qreal extra;
77   QRectF boundingRect;
78   if( myStartItem == myEndItem ) {
79     extra = arrowSize / 2.0 + 2.0;
80     boundingRect = mySelfDependencyArrow;
81   }
82   else {
83     extra = ( pen().width() + 20 ) / 2.0;
84     boundingRect = QRectF( myLine.p1(), QSizeF( myLine.p2().x() - myLine.p1().x(),
85                                                 myLine.p2().y() - myLine.p1().y() ) );
86   }
87   return boundingRect.normalized().adjusted( -extra, -extra, extra, extra );
88 }
89
90 //=================================================================================
91 // function : shape()
92 // purpose  : return the shape of this item to define an area of preselection
93 //=================================================================================
94 QPainterPath DependencyTree_Arrow::shape() const
95 {
96   QPainterPath path;
97
98   if( myStartItem == myEndItem ) {
99     qreal extra = 5;
100     QPolygonF aPolygonBigger( mySelfDependencyArrow.normalized()
101               .adjusted( -extra, -extra, extra, extra ) );
102     QPolygonF aPolygonSmaller( mySelfDependencyArrow.normalized()
103               .adjusted( extra, extra, -extra, -extra ) );
104     path.addPolygon( aPolygonBigger.subtracted( aPolygonSmaller ) );
105     path.addPolygon(myArrowHead);
106   }
107   else {
108     QPolygonF aShapePolygon;
109     QPolygon anArrowHead = myArrowHead.toPolygon();
110     QPolygon anReverseArrowHead = myReverseArrowHead.toPolygon();
111     aShapePolygon << anArrowHead.point(1) << anArrowHead.point(0) << anArrowHead.point(2) <<
112       anReverseArrowHead.point(1) << anReverseArrowHead.point(0) << anReverseArrowHead.point(2);
113     path.addPolygon( aShapePolygon );
114   }
115   return path;
116  }
117
118 //=================================================================================
119 // function : setColor()
120 // purpose  : set default color for arrow
121 //=================================================================================
122 void DependencyTree_Arrow::setColor( const QColor& theColor )
123 {
124   myColor = theColor;
125 }
126
127 //=================================================================================
128 // function : setHighlightColor()
129 // purpose  : set color for highlighted arrow
130 //=================================================================================
131 void DependencyTree_Arrow::setHighlightColor( const QColor& theColor )
132 {
133   myHighlightColor = theColor;
134 }
135
136 //=================================================================================
137 // function : setSelectColor()
138 // purpose  : set color for selected arrow
139 //=================================================================================
140 void DependencyTree_Arrow::setSelectColor( const QColor& theColor )
141 {
142   mySelectColor = theColor;
143 }
144
145 //=================================================================================
146 // function : getStartItem()
147 // purpose  : get start item of arrow
148 //=================================================================================
149 DependencyTree_Object* DependencyTree_Arrow::getStartItem() const
150 {
151   return myStartItem;
152 }
153
154 //=================================================================================
155 // function : getEndItem()
156 // purpose  : get end item of arrow
157 //=================================================================================
158 DependencyTree_Object* DependencyTree_Arrow::getEndItem() const
159 {
160   return myEndItem;
161 }
162
163 //=================================================================================
164 // function : setIsBiLink()
165 // purpose  : set true if current arrow is bi-directional link, else set false
166 //=================================================================================
167 void DependencyTree_Arrow::setIsBiLink( bool theIsBiLink )
168 {
169   myIsBiLink = theIsBiLink;
170 }
171
172 //=================================================================================
173 // function : paint()
174 // purpose  : paint the contents of an item in local coordinates (called by QGraphicsView)
175 //=================================================================================
176 void DependencyTree_Arrow::paint( QPainter* painter, const QStyleOptionGraphicsItem*, QWidget* )
177 {
178   if( isSelected() ) {
179     painter->setBrush( mySelectColor );
180     painter->setPen( QPen( mySelectColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
181   }
182   else if( isUnderMouse() ) {
183     painter->setBrush( myHighlightColor );
184     painter->setPen( QPen( myHighlightColor, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
185   }
186   else {
187     painter->setBrush( myColor );
188     painter->setPen( QPen( myColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
189   }
190
191   if( myStartItem == myEndItem ) {
192     int lineSize = 60;
193     QPointF p1( myStartItem->pos().x() - myStartItem->boundingRect().width()/2,
194                 myStartItem->pos().y() );
195     QPointF p2( p1.x() - lineSize + myStartItem->boundingRect().height()/2, p1.y() );
196     QPointF p3( p2.x(), p2.y() - lineSize );
197     QPointF p4( p3.x() + lineSize, p3.y() );
198     QPointF p5( p4.x(), p4.y() + lineSize - myStartItem->boundingRect().height()/2 );
199     mySelfDependencyArrow = QRectF( p3.x(), p3.y(), lineSize, lineSize );
200     myArrowHead = createArrowHead( p4, p5, false );
201     QVector<QPointF> pointVector;
202     pointVector << p1 << p2 << p2 << p3 << p3 << p4 << p4 << p5;
203
204     painter->drawLines( pointVector);
205     painter->drawPolygon(myArrowHead);
206   }
207   else {
208     if (myStartItem->collidesWithItem(myEndItem))
209       return;
210
211     myArrowHead  = createArrowHead( myStartItem->pos(), myEndItem->pos() );
212     myReverseArrowHead = createArrowHead( myEndItem->pos(), myStartItem->pos() );
213
214     painter->drawLine( myLine );
215     painter->drawPolygon( myArrowHead );
216     if( myIsBiLink )
217       painter->drawPolygon( myReverseArrowHead );
218   }
219 }
220
221 //=================================================================================
222 // function : createArrowHead()
223 // purpose  : create a head of arrow from start point to end point
224 //=================================================================================
225 QPolygonF DependencyTree_Arrow::createArrowHead( QPointF theStartPoint, QPointF theEndPoint,
226                                                          bool theIsDynamic )
227 {
228   if( theIsDynamic ) {
229     QLineF centerLine( theStartPoint, theEndPoint );
230     QPolygonF endPolygon = QPolygonF( myEndItem->boundingRect() );
231     QPointF p1 = endPolygon.first() + theEndPoint;
232     QPointF p2;
233     QPointF intersectPoint;
234     QLineF polyLine;
235     for( int i = 1; i < endPolygon.count(); ++i ) {
236       p2 = endPolygon.at(i) + theEndPoint;
237       polyLine = QLineF( p1, p2 );
238       QLineF::IntersectType intersectType = polyLine.intersect( centerLine, &intersectPoint );
239       if( intersectType == QLineF::BoundedIntersection )
240         break;
241       p1 = p2;
242     }
243     myLine = QLineF( intersectPoint, theStartPoint );
244   }
245   else
246     myLine = QLineF( theEndPoint, theStartPoint );
247
248   double angle = acos(myLine.dx() / myLine.length());
249   if( myLine.dy() >= 0 )
250     angle = ( M_PI * 2 ) - angle;
251
252   QPointF arrowP1 = myLine.p1() + QPointF( sin( angle + M_PI / 3 ) * arrowSize,
253                                            cos( angle + M_PI / 3 ) * arrowSize );
254   QPointF arrowP2 = myLine.p1() + QPointF( sin( angle + M_PI - M_PI / 3 ) * arrowSize,
255                                            cos( angle + M_PI - M_PI / 3 ) * arrowSize );
256
257   QPolygonF anArrowHead;
258   anArrowHead << myLine.p1() << arrowP1 << arrowP2;
259   return anArrowHead;
260 }