1 /***************************************************************************
2     qgsadvanceddigitizingcanvasitem.cpp  -  map canvas item for CAD tools
3     ----------------------
4     begin                : October 2014
5     copyright            : (C) Denis Rouzaud
6     email                : denis.rouzaud@gmail.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 <QPainter>
17 
18 #include "qgsadvanceddigitizingdockwidget.h"
19 #include "qgsadvanceddigitizingcanvasitem.h"
20 #include "qgsmapcanvas.h"
21 
22 
QgsAdvancedDigitizingCanvasItem(QgsMapCanvas * canvas,QgsAdvancedDigitizingDockWidget * cadDockWidget)23 QgsAdvancedDigitizingCanvasItem::QgsAdvancedDigitizingCanvasItem( QgsMapCanvas *canvas, QgsAdvancedDigitizingDockWidget *cadDockWidget )
24   : QgsMapCanvasItem( canvas )
25   , mLockedPen( QPen( QColor( 0, 127, 0, 255 ), 1, Qt::DashLine ) )
26   , mConstruction1Pen( QPen( QColor( 127, 127, 127, 150 ), 1, Qt::DashLine ) )
27   , mConstruction2Pen( QPen( QColor( 127, 127, 127, 255 ), 1, Qt::DashLine ) )
28   , mSnapPen( QPen( QColor( 127, 0, 0, 150 ), 1 ) )
29   , mSnapLinePen( QPen( QColor( 127, 0, 0, 150 ), 1, Qt::DashLine ) )
30   , mCursorPen( QPen( QColor( 127, 127, 127, 255 ), 1 ) )
31   , mAdvancedDigitizingDockWidget( cadDockWidget )
32 {
33 }
34 
paint(QPainter * painter)35 void QgsAdvancedDigitizingCanvasItem::paint( QPainter *painter )
36 {
37   if ( !mAdvancedDigitizingDockWidget->cadEnabled() )
38     return;
39 
40   // Use visible polygon rather than extent to properly handle rotated maps
41   QPolygonF mapPoly = mMapCanvas->mapSettings().visiblePolygon();
42   double canvasWidth = QLineF( mapPoly[0], mapPoly[1] ).length();
43   double canvasHeight = QLineF( mapPoly[0], mapPoly[3] ).length();
44   QgsRectangle mapRect = QgsRectangle( mapPoly[0],
45                                        QgsPointXY(
46                                          mapPoly[0].x() + canvasWidth,
47                                          mapPoly[0].y() - canvasHeight
48                                        )
49                                      );
50   if ( rect() != mapRect )
51     setRect( mapRect );
52 
53   int nPoints = mAdvancedDigitizingDockWidget->pointsCount();
54   if ( !nPoints )
55     return;
56 
57   bool previousPointExist, penulPointExist;
58   const QgsPointXY curPoint = mAdvancedDigitizingDockWidget->currentPoint();
59   const QgsPointXY prevPoint = mAdvancedDigitizingDockWidget->previousPoint( &previousPointExist );
60   const QgsPointXY penulPoint = mAdvancedDigitizingDockWidget->penultimatePoint( &penulPointExist );
61   const bool snappedToVertex = mAdvancedDigitizingDockWidget->snappedToVertex();
62   const QList<QgsPointXY> snappedSegment = mAdvancedDigitizingDockWidget->snappedSegment();
63   const bool hasSnappedSegment = snappedSegment.count() == 2;
64 
65   const bool curPointExist = mapPoly.containsPoint( curPoint.toQPointF(), Qt::OddEvenFill );
66 
67   const double mupp = mMapCanvas->getCoordinateTransform()->mapUnitsPerPixel();
68   if ( mupp == 0 )
69     return;
70 
71   const double canvasRotationRad = mMapCanvas->rotation() * M_PI / 180;
72   const double canvasMaxDimension = std::max( canvasWidth / mupp, canvasHeight / mupp );
73 
74   QPointF curPointPix, prevPointPix, penulPointPix, snapSegmentPix1, snapSegmentPix2;
75 
76   if ( curPointExist )
77   {
78     curPointPix = toCanvasCoordinates( curPoint );
79   }
80   if ( previousPointExist )
81   {
82     prevPointPix = toCanvasCoordinates( prevPoint );
83   }
84   if ( penulPointExist )
85   {
86     penulPointPix = toCanvasCoordinates( penulPoint );
87   }
88   if ( hasSnappedSegment )
89   {
90     snapSegmentPix1 = toCanvasCoordinates( snappedSegment[0] );
91     snapSegmentPix2 = toCanvasCoordinates( snappedSegment[1] );
92   }
93 
94   painter->setRenderHint( QPainter::Antialiasing );
95   painter->setCompositionMode( QPainter::CompositionMode_Difference );
96 
97   // Draw point snap
98   if ( curPointExist && snappedToVertex )
99   {
100     painter->setPen( mSnapPen );
101     painter->drawEllipse( curPointPix, 10, 10 );
102   }
103 
104   // Draw segment snap
105   if ( hasSnappedSegment && !snappedToVertex )
106   {
107     painter->setPen( mSnapPen );
108     painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
109 
110     if ( curPointExist )
111     {
112       painter->setPen( mSnapLinePen );
113       painter->drawLine( snapSegmentPix1, curPointPix );
114     }
115   }
116 
117   // Draw segment par/per input
118   if ( mAdvancedDigitizingDockWidget->additionalConstraint() != QgsAdvancedDigitizingDockWidget::AdditionalConstraint::NoConstraint && hasSnappedSegment )
119   {
120     painter->setPen( mConstruction2Pen );
121     painter->drawLine( snapSegmentPix1, snapSegmentPix2 );
122   }
123 
124   // Draw angle
125   if ( nPoints > 1 )
126   {
127     double a0, a;
128     if ( mAdvancedDigitizingDockWidget->constraintAngle()->relative() && nPoints > 2 )
129     {
130       a0 = std::atan2( -( prevPoint.y() - penulPoint.y() ), prevPoint.x() - penulPoint.x() );
131     }
132     else
133     {
134       a0 = 0;
135     }
136     if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
137     {
138       a = a0 - mAdvancedDigitizingDockWidget->constraintAngle()->value() * M_PI / 180;
139     }
140     else
141     {
142       a = std::atan2( -( curPoint.y() - prevPoint.y() ), curPoint.x() - prevPoint.x() );
143     }
144 
145     a0 += canvasRotationRad;
146     a += canvasRotationRad;
147 
148     painter->setPen( mConstruction2Pen );
149     painter->drawArc( QRectF( prevPointPix.x() - 20,
150                               prevPointPix.y() - 20,
151                               40, 40 ),
152                       static_cast<int>( 16 * -a0 * 180 / M_PI ),
153                       static_cast<int>( 16 * ( a0 - a ) * 180 / M_PI ) );
154     painter->drawLine( prevPointPix,
155                        prevPointPix + 60 * QPointF( std::cos( a0 ), std::sin( a0 ) ) );
156 
157 
158     if ( mAdvancedDigitizingDockWidget->constraintAngle()->isLocked() )
159     {
160       painter->setPen( mLockedPen );
161       painter->drawLine( prevPointPix - canvasMaxDimension * QPointF( std::cos( a ), std::sin( a ) ),
162                          prevPointPix + canvasMaxDimension * QPointF( std::cos( a ), std::sin( a ) ) );
163     }
164   }
165 
166   // Draw distance
167   if ( nPoints > 1 && mAdvancedDigitizingDockWidget->constraintDistance()->isLocked() )
168   {
169     painter->setPen( mLockedPen );
170     double r = mAdvancedDigitizingDockWidget->constraintDistance()->value() / mupp;
171     painter->drawEllipse( prevPointPix, r, r );
172   }
173 
174   // Draw x
175   if ( mAdvancedDigitizingDockWidget->constraintX()->isLocked() )
176   {
177     double x = 0.0;
178     bool draw = true;
179     painter->setPen( mLockedPen );
180     if ( mAdvancedDigitizingDockWidget->constraintX()->relative() )
181     {
182       if ( nPoints > 1 )
183       {
184         x = mAdvancedDigitizingDockWidget->constraintX()->value() + prevPoint.x();
185       }
186       else
187       {
188         draw = false;
189       }
190     }
191     else
192     {
193       x = mAdvancedDigitizingDockWidget->constraintX()->value();
194     }
195     if ( draw )
196     {
197       painter->drawLine( toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) - canvasMaxDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ),
198                          toCanvasCoordinates( QgsPointXY( x, mapPoly[0].y() ) ) + canvasMaxDimension * QPointF( std::sin( -canvasRotationRad ), std::cos( -canvasRotationRad ) ) );
199     }
200   }
201 
202   // Draw y
203   if ( mAdvancedDigitizingDockWidget->constraintY()->isLocked() )
204   {
205     double y = 0.0;
206     bool draw = true;
207     painter->setPen( mLockedPen );
208     if ( mAdvancedDigitizingDockWidget->constraintY()->relative() )
209     {
210       if ( nPoints > 1 )
211       {
212         y = mAdvancedDigitizingDockWidget->constraintY()->value() + prevPoint.y();
213       }
214       else
215       {
216         draw = false;
217       }
218     }
219     else
220     {
221       y = mAdvancedDigitizingDockWidget->constraintY()->value();
222     }
223     if ( draw )
224     {
225       painter->drawLine( toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) - canvasMaxDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ),
226                          toCanvasCoordinates( QgsPointXY( mapPoly[0].x(), y ) ) + canvasMaxDimension * QPointF( std::cos( -canvasRotationRad ), -std::sin( -canvasRotationRad ) ) );
227 
228     }
229   }
230 
231   // Draw constr
232   if ( mAdvancedDigitizingDockWidget->additionalConstraint() == QgsAdvancedDigitizingDockWidget::AdditionalConstraint::NoConstraint )
233   {
234     if ( curPointExist && previousPointExist )
235     {
236       painter->setPen( mConstruction2Pen );
237       painter->drawLine( prevPointPix, curPointPix );
238     }
239 
240     if ( previousPointExist && penulPointExist )
241     {
242       painter->setPen( mConstruction1Pen );
243       painter->drawLine( penulPointPix, prevPointPix );
244     }
245   }
246 
247   if ( curPointExist )
248   {
249     painter->setPen( mCursorPen );
250     painter->drawLine( curPointPix + QPointF( -5, -5 ),
251                        curPointPix + QPointF( +5, +5 ) );
252     painter->drawLine( curPointPix + QPointF( -5, +5 ),
253                        curPointPix + QPointF( +5, -5 ) );
254   }
255 }
256