QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgslayoutitemnodeitem.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemnodeitem.cpp
3 begin : March 2016
4 copyright : (C) 2016 Paul Blottiere, Oslandia
5 email : paul dot blottiere at oslandia dot com
6 ***************************************************************************/
7
8/***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
18
19#include <cmath>
20#include <limits>
21
22#include "qgslayout.h"
24#include "qgsmarkersymbol.h"
25#include "qgssymbol.h"
26
27#include <QString>
28#include <QStyleOptionGraphicsItem>
29
30#include "moc_qgslayoutitemnodeitem.cpp"
31
32using namespace Qt::StringLiterals;
33
34void QgsLayoutNodesItem::setNodes( const QPolygonF &nodes )
35{
38 emit clipPathChanged();
39}
40
42{
43 return mCurrentRectangle;
44}
45
50
56
59{
60 init();
61
62 const QRectF boundingRect = polygon.boundingRect();
64
65 const QPointF topLeft = boundingRect.topLeft();
66 mPolygon = polygon.translated( -topLeft );
67}
68
69void QgsLayoutNodesItem::init()
70{
71 // no cache - the node based items cannot reliably determine their real bounds (e.g. due to mitred corners).
72 // this blocks use of the pixmap based cache for these
73 setCacheMode( QGraphicsItem::NoCache );
74 setBackgroundEnabled( false );
75 setFrameEnabled( false );
76
78}
79
81{
82 QPainter *painter = context.renderContext().painter();
83 painter->setPen( Qt::NoPen );
84 painter->setBrush( Qt::NoBrush );
85
88
90 _draw( context );
91
92 if ( mDrawNodes && layout()->renderContext().isPreviewRender() )
93 drawNodes( context );
94}
95
100
101double QgsLayoutNodesItem::computeDistance( QPointF pt1, QPointF pt2 ) const
102{
103 return std::sqrt( std::pow( pt1.x() - pt2.x(), 2 ) + std::pow( pt1.y() - pt2.y(), 2 ) );
104}
105
106bool QgsLayoutNodesItem::addNode( QPointF pt, const bool checkArea, const double radius )
107{
108 const QPointF start = mapFromScene( pt );
109 double minDistance = std::numeric_limits<double>::max();
110 const double maxDistance = ( checkArea ) ? radius : minDistance;
111 bool rc = false;
112 int idx = -1;
113
114 for ( int i = 0; i != mPolygon.size(); i++ )
115 {
116 // get nodes of polyline
117 const QPointF pt1 = mPolygon.at( i );
118 QPointF pt2 = mPolygon.at( 0 );
119 if ( ( i + 1 ) != mPolygon.size() )
120 pt2 = mPolygon.at( i + 1 );
121
122 // compute line eq
123 const double coef = ( pt2.y() - pt1.y() ) / ( pt2.x() - pt1.x() );
124 const double b = pt1.y() - coef * pt1.x();
125
126 double distance = std::numeric_limits<double>::max();
127 if ( std::isinf( coef ) )
128 distance = std::fabs( pt1.x() - start.x() );
129 else
130 {
131 const double coef2 = ( -1 / coef );
132 const double b2 = start.y() - coef2 * start.x();
133
134 QPointF inter;
135 if ( std::isinf( coef2 ) )
136 {
137 distance = std::fabs( pt1.y() - start.y() );
138 inter.setX( start.x() );
139 inter.setY( pt1.y() );
140 }
141 else
142 {
143 const double interx = ( b - b2 ) / ( coef2 - coef );
144 const double intery = interx * coef2 + b2;
145 inter.setX( interx );
146 inter.setY( intery );
147 }
148
149 // check if intersection is within the line
150 const double length1 = computeDistance( inter, pt1 );
151 const double length2 = computeDistance( inter, pt2 );
152 const double length3 = computeDistance( pt1, pt2 );
153 const double length4 = length1 + length2;
154
155 if ( std::fabs( length3 - length4 ) < std::numeric_limits<float>::epsilon() )
156 distance = computeDistance( inter, start );
157 }
158
159 if ( distance < minDistance && distance < maxDistance )
160 {
161 minDistance = distance;
162 idx = i;
163 }
164 }
165
166 if ( idx >= 0 )
167 {
168 rc = _addNode( idx, start, maxDistance );
170 emit clipPathChanged();
171 }
172
173 return rc;
174}
175
176void QgsLayoutNodesItem::drawNodes( QgsLayoutItemRenderContext &context ) const
177{
178 context.renderContext().painter()->setRenderHint( QPainter::Antialiasing, false );
179
180 const double rectSize = 9.0 / context.viewScaleFactor();
181
182 QVariantMap properties;
183 properties.insert( u"name"_s, u"cross"_s );
184 properties.insert( u"color_border"_s, u"red"_s );
185
186 std::unique_ptr<QgsMarkerSymbol> symbol = QgsMarkerSymbol::createSimple( properties );
187 symbol->setSize( rectSize );
188 symbol->setAngle( 45 );
189
190 symbol->startRender( context.renderContext() );
191 for ( const QPointF pt : std::as_const( mPolygon ) )
192 symbol->renderPoint( pt * context.viewScaleFactor(), nullptr, context.renderContext() );
193 symbol->stopRender( context.renderContext() );
194
195 if ( mSelectedNode >= 0 && mSelectedNode < mPolygon.size() )
196 drawSelectedNode( context );
197}
198
199void QgsLayoutNodesItem::drawSelectedNode( QgsLayoutItemRenderContext &context ) const
200{
201 const double rectSize = 9.0 / context.viewScaleFactor();
202
203 QVariantMap properties;
204 properties.insert( u"name"_s, u"square"_s );
205 properties.insert( u"color"_s, u"0, 0, 0, 0"_s );
206 properties.insert( u"color_border"_s, u"blue"_s );
207 properties.insert( u"width_border"_s, u"4"_s );
208
209 std::unique_ptr<QgsMarkerSymbol> symbol = QgsMarkerSymbol::createSimple( properties );
210 symbol->setSize( rectSize );
211
212 symbol->startRender( context.renderContext() );
213 symbol->renderPoint( mPolygon.at( mSelectedNode ) * context.viewScaleFactor(), nullptr, context.renderContext() );
214 symbol->stopRender( context.renderContext() );
215}
216
217int QgsLayoutNodesItem::nodeAtPosition( QPointF node, const bool searchInRadius, const double radius ) const
218{
219 const QPointF pt = mapFromScene( node );
220 double nearestDistance = std::numeric_limits<double>::max();
221 const double maxDistance = ( searchInRadius ) ? radius : nearestDistance;
222 double distance = 0;
223 int idx = -1;
224
225 int i = 0;
226 for ( const QPointF polyPt : std::as_const( mPolygon ) )
227 {
228 distance = computeDistance( pt, polyPt );
229 if ( distance < nearestDistance && distance < maxDistance )
230 {
231 nearestDistance = distance;
232 idx = i;
233 }
234 i++;
235 }
236
237 return idx;
238}
239
240bool QgsLayoutNodesItem::nodePosition( const int index, QPointF &position ) const
241{
242 bool rc( false );
243
244 if ( index >= 0 && index < mPolygon.size() )
245 {
246 position = mapToScene( mPolygon.at( index ) );
247 rc = true;
248 }
249
250 return rc;
251}
252
253bool QgsLayoutNodesItem::removeNode( const int index )
254{
255 const bool rc = _removeNode( index );
256 if ( rc )
257 {
259 emit clipPathChanged();
260 }
261 return rc;
262}
263
264bool QgsLayoutNodesItem::moveNode( const int index, QPointF pt )
265{
266 bool rc( false );
267
268 if ( index >= 0 && index < mPolygon.size() )
269 {
270 const QPointF nodeItem = mapFromScene( pt );
271 mPolygon.replace( index, nodeItem );
273 emit clipPathChanged();
274 rc = true;
275 }
276
277 return rc;
278}
279
280bool QgsLayoutNodesItem::readPropertiesFromElement( const QDomElement &itemElem, const QDomDocument &, const QgsReadWriteContext &context )
281{
282 // restore style
283 const QDomElement styleSymbolElem = itemElem.firstChildElement( u"symbol"_s );
284 if ( !styleSymbolElem.isNull() )
285 _readXmlStyle( styleSymbolElem, context );
286
287 // restore nodes
288 mPolygon.clear();
289 const QDomNodeList nodesList = itemElem.elementsByTagName( u"node"_s );
290 for ( int i = 0; i < nodesList.size(); i++ )
291 {
292 const QDomElement nodeElem = nodesList.at( i ).toElement();
293 QPointF newPt;
294 newPt.setX( nodeElem.attribute( u"x"_s ).toDouble() );
295 newPt.setY( nodeElem.attribute( u"y"_s ).toDouble() );
296 mPolygon.append( newPt );
297 }
298
299 emit changed();
300 emit clipPathChanged();
301 return true;
302}
303
305{
306 // get the bounding rect for the polygon currently displayed
307 const QRectF boundingRect = mPolygon.boundingRect();
308
309 // compute x/y ratio
310 const float ratioX = !qgsDoubleNear( boundingRect.width(), 0.0 ) ? rect().width() / boundingRect.width() : 0;
311 const float ratioY = !qgsDoubleNear( boundingRect.height(), 0.0 ) ? rect().height() / boundingRect.height() : 0;
312
313 // scaling
314 QTransform trans;
315 trans = trans.scale( ratioX, ratioY );
316 mPolygon = trans.map( mPolygon );
317 emit clipPathChanged();
318}
319
321{
322 bool rc = false;
323
324 if ( index >= 0 && index < mPolygon.size() )
325 {
326 mSelectedNode = index;
327 rc = true;
328 }
329
330 return rc;
331}
332
334{
335 // set the new scene rectangle
336 const QRectF br = mPolygon.boundingRect();
337
338 const QPointF topLeft = mapToScene( br.topLeft() );
339 //will trigger updateBoundingRect if necessary
340 attemptSetSceneRect( QRectF( topLeft.x(), topLeft.y(), br.width(), br.height() ) );
341
342 // update polygon position
343 mPolygon.translate( -br.topLeft().x(), -br.topLeft().y() );
344}
345
347{
348 QRectF br = rect();
350 prepareGeometryChange();
352
353 // update
354 update();
355}
356
357bool QgsLayoutNodesItem::writePropertiesToElement( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const
358{
359 // style
360 _writeXmlStyle( doc, elem, context );
361
362 // write nodes
363 QDomElement nodesElem = doc.createElement( u"nodes"_s );
364 for ( const QPointF pt : std::as_const( mPolygon ) )
365 {
366 QDomElement nodeElem = doc.createElement( u"node"_s );
367 nodeElem.setAttribute( u"x"_s, QString::number( pt.x() ) );
368 nodeElem.setAttribute( u"y"_s, QString::number( pt.y() ) );
369 nodesElem.appendChild( nodeElem );
370 }
371 elem.appendChild( nodesElem );
372
373 return true;
374}
@ Default
Allow raster-based rendering in situations where it is required for correct rendering or where it wil...
Definition qgis.h:2799
@ PreferVector
Prefer vector-based rendering, when the result will still be visually near-identical to a raster-base...
Definition qgis.h:2800
Contains settings and helpers relating to a render of a QgsLayoutItem.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
double viewScaleFactor() const
Returns the current view zoom (scale factor).
friend class QgsLayout
QgsLayoutItem(QgsLayout *layout, bool manageZValue=true)
Constructor for QgsLayoutItem, with the specified parent layout.
virtual void setFrameEnabled(bool drawFrame)
Sets whether this item has a frame drawn around it or not.
@ FlagDisableSceneCaching
Item should not have QGraphicsItem caching enabled.
void sizePositionChanged()
Emitted when the item's size or position changes.
void clipPathChanged()
Emitted when the item's clipping path has changed.
QFlags< Flag > Flags
void attemptSetSceneRect(const QRectF &rect, bool includesFrame=false)
Attempts to update the item's position and size to match the passed rect in layout coordinates.
void setBackgroundEnabled(bool drawBackground)
Sets whether this item has a background drawn under it or not.
QgsLayoutItem::Flags itemFlags() const override
Returns the item's flags, which indicate how the item behaves.
virtual void updateBoundingRect()
Called when the bounding rect of the item should recalculated.
virtual bool _removeNode(int nodeIndex)=0
Method called in removeNode.
QPolygonF nodes() const
Returns the nodes the shape consists of.
double mMaxSymbolBleed
Max symbol bleed.
virtual void _writeXmlStyle(QDomDocument &doc, QDomElement &elmt, const QgsReadWriteContext &context) const =0
Method called in writeXml.
QRectF mCurrentRectangle
Current bounding rectangle of shape.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
bool setSelectedNode(int index)
Selects a node by index.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void updateSceneRect()
Update the current scene rectangle for this item.
virtual void _readXmlStyle(const QDomElement &elmt, const QgsReadWriteContext &context)=0
Method called in readXml.
bool removeNode(int index)
Remove a node with specified index from the shape.
QRectF boundingRect() const override
double computeDistance(QPointF pt1, QPointF pt2) const
Compute an euclidean distance between 2 nodes.
bool nodePosition(int index, QPointF &position) const
Gets the position of a node in scene coordinates.
bool addNode(QPointF point, bool checkArea=true, double radius=10)
Add a node in current shape.
bool moveNode(int index, QPointF node)
Moves a node to a new position.
double estimatedFrameBleed() const override
Returns the estimated amount the item's frame bleeds outside the item's actual rectangle.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
virtual void _draw(QgsLayoutItemRenderContext &context, const QStyleOptionGraphicsItem *itemStyle=nullptr)=0
Method called in paint.
QgsLayoutNodesItem(QgsLayout *layout)
Constructor for QgsLayoutNodesItem, attached to the specified layout.
virtual bool _addNode(int nodeIndex, QPointF newNode, double radius)=0
Method called in addNode.
void rescaleToFitBoundingBox()
Rescale the current shape according to the item's bounding box.
void setNodes(const QPolygonF &nodes)
Sets the nodes the shape consists of.
QPolygonF mPolygon
Shape's nodes.
int nodeAtPosition(QPointF point, bool searchInRadius=true, double radius=10) const
Search for the nearest node in the shape within a maximal area.
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
static std::unique_ptr< QgsMarkerSymbol > createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
A container for the context for various read/write operations on objects.
QPainter * painter()
Returns the destination QPainter for the render operation.
void setRasterizedRenderingPolicy(Qgis::RasterizedRenderingPolicy policy)
Sets the policy controlling when rasterisation of content during renders is permitted.
Qgis::RasterizedRenderingPolicy rasterizedRenderingPolicy() const
Returns the policy controlling when rasterisation of content during renders is permitted.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975