QGIS API Documentation  3.21.0-Master (909859188c)
qgsmodelarrowitem.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmodelarrowitem.cpp
3  ----------------------------------
4  Date : March 2020
5  Copyright : (C) 2020 Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include <math.h>
17 
18 #include "qgsmodelarrowitem.h"
19 #include "qgsapplication.h"
20 #include "qgsmodelgraphicsscene.h"
22 #include <QPainter>
23 #include <QApplication>
24 #include <QPalette>
25 
27 
28 
29 QgsModelArrowItem::QgsModelArrowItem( QgsModelComponentGraphicItem *startItem, Qt::Edge startEdge, int startIndex, bool startIsOutgoing, Marker startMarker,
30  QgsModelComponentGraphicItem *endItem, Qt::Edge endEdge, int endIndex, bool endIsIncoming, Marker endMarker )
31  : QObject( nullptr )
32  , mStartItem( startItem )
33  , mStartEdge( startEdge )
34  , mStartIndex( startIndex )
35  , mStartIsOutgoing( startIsOutgoing )
36  , mStartMarker( startMarker )
37  , mEndItem( endItem )
38  , mEndEdge( endEdge )
39  , mEndIndex( endIndex )
40  , mEndIsIncoming( endIsIncoming )
41  , mEndMarker( endMarker )
42 {
43  setCacheMode( QGraphicsItem::DeviceCoordinateCache );
44  setFlag( QGraphicsItem::ItemIsSelectable, false );
45  mColor = QApplication::palette().color( QPalette::WindowText );
46  mColor.setAlpha( 150 );
47  setPen( QPen( mColor, 8, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin ) );
48  setZValue( QgsModelGraphicsScene::ArrowLink );
49  updatePath();
50 
51  connect( mStartItem, &QgsModelComponentGraphicItem::updateArrowPaths, this, &QgsModelArrowItem::updatePath );
52  connect( mStartItem, &QgsModelComponentGraphicItem::repaintArrows, this, [ = ] { update(); } );
53  connect( mEndItem, &QgsModelComponentGraphicItem::updateArrowPaths, this, &QgsModelArrowItem::updatePath );
54  connect( mEndItem, &QgsModelComponentGraphicItem::repaintArrows, this, [ = ] { update(); } );
55 }
56 
57 QgsModelArrowItem::QgsModelArrowItem( QgsModelComponentGraphicItem *startItem, Qt::Edge startEdge, int startIndex, Marker startMarker, QgsModelComponentGraphicItem *endItem, Marker endMarker )
58  : QgsModelArrowItem( startItem, startEdge, startIndex, true, startMarker, endItem, Qt::LeftEdge, -1, true, endMarker )
59 {
60 }
61 
62 QgsModelArrowItem::QgsModelArrowItem( QgsModelComponentGraphicItem *startItem, Marker startMarker, QgsModelComponentGraphicItem *endItem, Qt::Edge endEdge, int endIndex, Marker endMarker )
63  : QgsModelArrowItem( startItem, Qt::LeftEdge, -1, true, startMarker, endItem, endEdge, endIndex, true, endMarker )
64 {
65 }
66 
67 QgsModelArrowItem::QgsModelArrowItem( QgsModelComponentGraphicItem *startItem, Marker startMarker, QgsModelComponentGraphicItem *endItem, Marker endMarker )
68  : QgsModelArrowItem( startItem, Qt::LeftEdge, -1, true, startMarker, endItem, Qt::LeftEdge, -1, true, endMarker )
69 {
70 }
71 
72 
73 void QgsModelArrowItem::paint( QPainter *painter, const QStyleOptionGraphicsItem *, QWidget * )
74 {
75  QColor color = mColor;
76 
77  if ( mStartItem->state() == QgsModelComponentGraphicItem::Selected || mEndItem->state() == QgsModelComponentGraphicItem::Selected )
78  color.setAlpha( 220 );
79  else if ( mStartItem->state() == QgsModelComponentGraphicItem::Hover || mEndItem->state() == QgsModelComponentGraphicItem::Hover )
80  color.setAlpha( 150 );
81  else
82  color.setAlpha( 80 );
83 
84  QPen p = pen();
85  p.setColor( color );
86  p.setWidth( 1 );
87  painter->setPen( p );
88  painter->setBrush( color );
89  painter->setRenderHint( QPainter::Antialiasing );
90 
91 
92  switch ( mStartMarker )
93  {
94  case Marker::Circle:
95  painter->drawEllipse( mStartPoint, 3.0, 3.0 );
96  break;
97  case Marker::ArrowHead:
98  drawArrowHead( painter, mStartPoint, path().pointAtPercent( 0.0 ) - path().pointAtPercent( 0.05 ) );
99  break;
100  }
101 
102  switch ( mEndMarker )
103  {
104  case Marker::Circle:
105  painter->drawEllipse( mEndPoint, 3.0, 3.0 );
106  break;
107  case Marker::ArrowHead:
108  drawArrowHead( painter, mEndPoint, path().pointAtPercent( 1.0 ) - path().pointAtPercent( 0.95 ) );
109  break;
110  }
111 
112  painter->setBrush( Qt::NoBrush );
113  painter->drawPath( path() );
114 }
115 
116 void QgsModelArrowItem::drawArrowHead( QPainter *painter, const QPointF &position, const QPointF &vector )
117 {
118  const float angle = atan2( vector.y(), vector.x() ) * 180.0 / M_PI;
119  painter->translate( position );
120  painter->rotate( angle );
121  QPolygonF arrowHead;
122  arrowHead << QPointF( 0, 0 ) << QPointF( -6, 4 ) << QPointF( -6, -4 ) << QPointF( 0, 0 );
123  painter->drawPolygon( arrowHead );
124  painter->rotate( -angle );
125  painter->translate( -position );
126 }
127 
128 void QgsModelArrowItem::setPenStyle( Qt::PenStyle style )
129 {
130  QPen p = pen();
131  p.setStyle( style );
132  setPen( p );
133  update();
134 }
135 
136 void QgsModelArrowItem::updatePath()
137 {
138  QList< QPointF > controlPoints;
139 
140  // is there a fixed start or end point?
141  QPointF startPt;
142  bool hasStartPt = false;
143  if ( mStartIndex != -1 )
144  {
145  startPt = mStartItem->linkPoint( mStartEdge, mStartIndex, !mStartIsOutgoing );
146  hasStartPt = true;
147  }
148  QPointF endPt;
149  bool hasEndPt = false;
150  if ( mEndIndex != -1 )
151  {
152  endPt = mEndItem->linkPoint( mEndEdge, mEndIndex, mEndIsIncoming );
153  hasEndPt = true;
154  }
155 
156  if ( !hasStartPt )
157  {
158  Qt::Edge startEdge;
159  QPointF pt;
160  if ( !hasEndPt )
161  pt = mStartItem->calculateAutomaticLinkPoint( mEndItem, startEdge );
162  else
163  pt = mStartItem->calculateAutomaticLinkPoint( endPt + mEndItem->pos(), startEdge );
164 
165  controlPoints.append( pt );
166  mStartPoint = pt;
167  controlPoints.append( bezierPointForCurve( pt, startEdge, !mStartIsOutgoing ) );
168  }
169  else
170  {
171  mStartPoint = mStartItem->pos() + startPt;
172  controlPoints.append( mStartItem->pos() + startPt );
173  controlPoints.append( bezierPointForCurve( mStartItem->pos() + startPt, mStartEdge == Qt::BottomEdge ? Qt::RightEdge : Qt::LeftEdge, !mStartIsOutgoing ) );
174  }
175 
176  if ( !hasEndPt )
177  {
178  Qt::Edge endEdge;
179  QPointF pt;
180  if ( !hasStartPt )
181  pt = mEndItem->calculateAutomaticLinkPoint( mStartItem, endEdge );
182  else
183  pt = mEndItem->calculateAutomaticLinkPoint( startPt + mStartItem->pos(), endEdge );
184 
185  controlPoints.append( bezierPointForCurve( pt, endEdge, mEndIsIncoming ) );
186  controlPoints.append( pt );
187  mEndPoint = pt;
188  }
189  else
190  {
191  mEndPoint = mEndItem->pos() + endPt ;
192  controlPoints.append( bezierPointForCurve( mEndItem->pos() + endPt, mEndEdge == Qt::BottomEdge ? Qt::RightEdge : Qt::LeftEdge, mEndIsIncoming ) );
193  controlPoints.append( mEndItem->pos() + endPt );
194  }
195 
196  QPainterPath path;
197  path.moveTo( controlPoints.at( 0 ) );
198  path.cubicTo( controlPoints.at( 1 ), controlPoints.at( 2 ), controlPoints.at( 3 ) );
199  setPath( path );
200 }
201 
202 QPointF QgsModelArrowItem::bezierPointForCurve( const QPointF &point, Qt::Edge edge, bool incoming ) const
203 {
204  switch ( edge )
205  {
206  case Qt::LeftEdge:
207  return point + QPointF( incoming ? -50 : 50, 0 );
208 
209  case Qt::RightEdge:
210  return point + QPointF( incoming ? -50 : 50, 0 );
211 
212  case Qt::TopEdge:
213  return point + QPointF( 0, incoming ? -30 : 30 );
214 
215  case Qt::BottomEdge:
216  return point + QPointF( 0, incoming ? -30 : 30 );
217  }
218  return QPointF();
219 }
220 
221 
223 
double ANALYSIS_EXPORT angle(QgsPoint *p1, QgsPoint *p2, QgsPoint *p3, QgsPoint *p4)
Calculates the angle between two segments (in 2 dimension, z-values are ignored)
Definition: MathUtils.cpp:786