Salome HOME
8b0a1e422fc401e44ef3ffff3095d47949e57d72
[modules/geom.git] / src / DependencyTree / DependencyTree_Arrow.cxx
1 // Copyright (C) 2014-2023  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 #ifdef _MSC_VER
32 #define _USE_MATH_DEFINES
33 #endif
34
35 #include <math.h>
36
37 const qreal arrowSize = 20;
38
39 DependencyTree_Arrow::DependencyTree_Arrow( DependencyTree_Object* theStartItem,
40                                             DependencyTree_Object* theEndItem,
41                                             QGraphicsItem* parent, QGraphicsScene* /*scene*/ )
42 :QGraphicsLineItem( parent ),
43 myStartItem( theStartItem ),
44 myEndItem( theEndItem ),
45 myIsBiLink( false )
46 {
47   SUIT_ResourceMgr* resMgr = SUIT_Session::session()->resourceMgr();
48
49   myColor = resMgr->colorValue( "Geometry", "dependency_tree_arrow_color", QColor( 0, 0, 130 ) );
50   myHighlightColor = resMgr->colorValue( "Geometry", "dependency_tree_highlight_arrow_color", QColor( 0, 0, 255 ) );
51   mySelectColor = resMgr->colorValue( "Geometry", "dependency_tree_select_arrow_color", QColor( 255, 0, 0 ) );
52
53   myStartItem = theStartItem;
54   myEndItem = theEndItem;
55
56   myLine = QLineF( myStartItem->pos(), myEndItem->pos() );
57   myArrowHead  = createArrowHead( myStartItem->pos(), myEndItem->pos() );
58   myReverseArrowHead = createArrowHead( myEndItem->pos(), myStartItem->pos() );
59
60   mySelfDependencyArrow = QRectF();
61
62   setPen( QPen( myColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
63   setColor( myColor );
64
65   setFlag( QGraphicsItem::ItemIsSelectable, true );
66   setZValue( -1000.0 );
67 }
68
69 DependencyTree_Arrow::~DependencyTree_Arrow()
70 {
71 }
72
73 //=================================================================================
74 // function : boundingRect()
75 // purpose  : return the outer bounds of the item as a rectangle.
76 //            QGraphicsView uses this to determine whether the item requires redrawing
77 //=================================================================================
78 QRectF DependencyTree_Arrow::boundingRect() const
79 {
80   qreal extra;
81   QRectF boundingRect;
82   if( myStartItem == myEndItem ) {
83     extra = arrowSize / 2.0 + 2.0;
84     boundingRect = mySelfDependencyArrow;
85   }
86   else {
87     extra = ( pen().width() + 20 ) / 2.0;
88     boundingRect = QRectF( myLine.p1(), QSizeF( myLine.p2().x() - myLine.p1().x(),
89                                                 myLine.p2().y() - myLine.p1().y() ) );
90   }
91   return boundingRect.normalized().adjusted( -extra, -extra, extra, extra );
92 }
93
94 //=================================================================================
95 // function : shape()
96 // purpose  : return the shape of this item to define an area of preselection
97 //=================================================================================
98 QPainterPath DependencyTree_Arrow::shape() const
99 {
100   QPainterPath path;
101
102   if( myStartItem == myEndItem ) {
103     qreal extra = 5;
104     QPolygonF aPolygonBigger( mySelfDependencyArrow.normalized()
105               .adjusted( -extra, -extra, extra, extra ) );
106     QPolygonF aPolygonSmaller( mySelfDependencyArrow.normalized()
107               .adjusted( extra, extra, -extra, -extra ) );
108     path.addPolygon( aPolygonBigger.subtracted( aPolygonSmaller ) );
109     path.addPolygon(myArrowHead);
110   }
111   else {
112     QPolygonF aShapePolygon;
113     QPolygon anArrowHead = myArrowHead.toPolygon();
114     QPolygon anReverseArrowHead = myReverseArrowHead.toPolygon();
115     aShapePolygon << anArrowHead.point(1) << anArrowHead.point(0) << anArrowHead.point(2) <<
116       anReverseArrowHead.point(1) << anReverseArrowHead.point(0) << anReverseArrowHead.point(2);
117     path.addPolygon( aShapePolygon );
118   }
119   return path;
120  }
121
122 //=================================================================================
123 // function : setColor()
124 // purpose  : set default color for arrow
125 //=================================================================================
126 void DependencyTree_Arrow::setColor( const QColor& theColor )
127 {
128   myColor = theColor;
129 }
130
131 //=================================================================================
132 // function : setHighlightColor()
133 // purpose  : set color for highlighted arrow
134 //=================================================================================
135 void DependencyTree_Arrow::setHighlightColor( const QColor& theColor )
136 {
137   myHighlightColor = theColor;
138 }
139
140 //=================================================================================
141 // function : setSelectColor()
142 // purpose  : set color for selected arrow
143 //=================================================================================
144 void DependencyTree_Arrow::setSelectColor( const QColor& theColor )
145 {
146   mySelectColor = theColor;
147 }
148
149 //=================================================================================
150 // function : getStartItem()
151 // purpose  : get start item of arrow
152 //=================================================================================
153 DependencyTree_Object* DependencyTree_Arrow::getStartItem() const
154 {
155   return myStartItem;
156 }
157
158 //=================================================================================
159 // function : getEndItem()
160 // purpose  : get end item of arrow
161 //=================================================================================
162 DependencyTree_Object* DependencyTree_Arrow::getEndItem() const
163 {
164   return myEndItem;
165 }
166
167 //=================================================================================
168 // function : setIsBiLink()
169 // purpose  : set true if current arrow is bi-directional link, else set false
170 //=================================================================================
171 void DependencyTree_Arrow::setIsBiLink( bool theIsBiLink )
172 {
173   myIsBiLink = theIsBiLink;
174 }
175
176 //=================================================================================
177 // function : paint()
178 // purpose  : paint the contents of an item in local coordinates (called by QGraphicsView)
179 //=================================================================================
180 void DependencyTree_Arrow::paint( QPainter* painter, const QStyleOptionGraphicsItem*, QWidget* )
181 {
182   if( isSelected() ) {
183     painter->setBrush( mySelectColor );
184     painter->setPen( QPen( mySelectColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
185   }
186   else if( isUnderMouse() ) {
187     painter->setBrush( myHighlightColor );
188     painter->setPen( QPen( myHighlightColor, 3, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
189   }
190   else {
191     painter->setBrush( myColor );
192     painter->setPen( QPen( myColor, 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
193   }
194
195   if( myStartItem == myEndItem ) {
196     int lineSize = 60;
197     QPointF p1( myStartItem->pos().x() - myStartItem->boundingRect().width()/2,
198                 myStartItem->pos().y() );
199     QPointF p2( p1.x() - lineSize + myStartItem->boundingRect().height()/2, p1.y() );
200     QPointF p3( p2.x(), p2.y() - lineSize );
201     QPointF p4( p3.x() + lineSize, p3.y() );
202     QPointF p5( p4.x(), p4.y() + lineSize - myStartItem->boundingRect().height()/2 );
203     mySelfDependencyArrow = QRectF( p3.x(), p3.y(), lineSize, lineSize );
204     myArrowHead = createArrowHead( p4, p5, false );
205     QVector<QPointF> pointVector;
206     pointVector << p1 << p2 << p2 << p3 << p3 << p4 << p4 << p5;
207
208     painter->drawLines( pointVector);
209     painter->drawPolygon(myArrowHead);
210   }
211   else {
212     if (myStartItem->collidesWithItem(myEndItem))
213       return;
214
215     myArrowHead  = createArrowHead( myStartItem->pos(), myEndItem->pos() );
216     myReverseArrowHead = createArrowHead( myEndItem->pos(), myStartItem->pos() );
217
218     painter->drawLine( myLine );
219     painter->drawPolygon( myArrowHead );
220     if( myIsBiLink )
221       painter->drawPolygon( myReverseArrowHead );
222   }
223 }
224
225 //=================================================================================
226 // function : createArrowHead()
227 // purpose  : create a head of arrow from start point to end point
228 //=================================================================================
229 QPolygonF DependencyTree_Arrow::createArrowHead( QPointF theStartPoint, QPointF theEndPoint,
230                                                          bool theIsDynamic )
231 {
232   if( theIsDynamic ) {
233     QLineF centerLine( theStartPoint, theEndPoint );
234     QPolygonF endPolygon = QPolygonF( myEndItem->boundingRect() );
235     QPointF p1 = endPolygon.first() + theEndPoint;
236     QPointF p2;
237     QPointF intersectPoint;
238     QLineF polyLine;
239     for( int i = 1; i < endPolygon.count(); ++i ) {
240       p2 = endPolygon.at(i) + theEndPoint;
241       polyLine = QLineF( p1, p2 );
242       QLineF::IntersectType intersectType = polyLine.intersect( centerLine, &intersectPoint );
243       if( intersectType == QLineF::BoundedIntersection )
244         break;
245       p1 = p2;
246     }
247     myLine = QLineF( intersectPoint, theStartPoint );
248   }
249   else
250     myLine = QLineF( theEndPoint, theStartPoint );
251
252   double angle = acos(myLine.dx() / myLine.length());
253   if( myLine.dy() >= 0 )
254     angle = ( M_PI * 2 ) - angle;
255
256   QPointF arrowP1 = myLine.p1() + QPointF( sin( angle + M_PI / 3 ) * arrowSize,
257                                            cos( angle + M_PI / 3 ) * arrowSize );
258   QPointF arrowP2 = myLine.p1() + QPointF( sin( angle + M_PI - M_PI / 3 ) * arrowSize,
259                                            cos( angle + M_PI - M_PI / 3 ) * arrowSize );
260
261   QPolygonF anArrowHead;
262   anArrowHead << myLine.p1() << arrowP1 << arrowP2;
263   return anArrowHead;
264 }