QGIS API Documentation 3.99.0-Master (e9821da5c6b)
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
60{
61 init();
62
63 const QRectF boundingRect = polygon.boundingRect();
65
66 const QPointF topLeft = boundingRect.topLeft();
67 mPolygon = polygon.translated( -topLeft );
68}
69
70void QgsLayoutNodesItem::init()
71{
72 // no cache - the node based items cannot reliably determine their real bounds (e.g. due to mitred corners).
73 // this blocks use of the pixmap based cache for these
74 setCacheMode( QGraphicsItem::NoCache );
75 setBackgroundEnabled( false );
76 setFrameEnabled( false );
77
79}
80
82{
83 QPainter *painter = context.renderContext().painter();
84 painter->setPen( Qt::NoPen );
85 painter->setBrush( Qt::NoBrush );
86
89
91 _draw( context );
92
93 if ( mDrawNodes && layout()->renderContext().isPreviewRender() )
94 drawNodes( context );
95}
96
101
103 QPointF pt2 ) const
104{
105 return std::sqrt( std::pow( pt1.x() - pt2.x(), 2 ) + std::pow( pt1.y() - pt2.y(), 2 ) );
106}
107
109 const bool checkArea,
110 const double radius )
111{
112 const QPointF start = mapFromScene( pt );
113 double minDistance = std::numeric_limits<double>::max();
114 const double maxDistance = ( checkArea ) ? radius : minDistance;
115 bool rc = false;
116 int idx = -1;
117
118 for ( int i = 0; i != mPolygon.size(); i++ )
119 {
120 // get nodes of polyline
121 const QPointF pt1 = mPolygon.at( i );
122 QPointF pt2 = mPolygon.at( 0 );
123 if ( ( i + 1 ) != mPolygon.size() )
124 pt2 = mPolygon.at( i + 1 );
125
126 // compute line eq
127 const double coef = ( pt2.y() - pt1.y() ) / ( pt2.x() - pt1.x() );
128 const double b = pt1.y() - coef * pt1.x();
129
130 double distance = std::numeric_limits<double>::max();
131 if ( std::isinf( coef ) )
132 distance = std::fabs( pt1.x() - start.x() );
133 else
134 {
135 const double coef2 = ( -1 / coef );
136 const double b2 = start.y() - coef2 * start.x();
137
138 QPointF inter;
139 if ( std::isinf( coef2 ) )
140 {
141 distance = std::fabs( pt1.y() - start.y() );
142 inter.setX( start.x() );
143 inter.setY( pt1.y() );
144 }
145 else
146 {
147 const double interx = ( b - b2 ) / ( coef2 - coef );
148 const double intery = interx * coef2 + b2;
149 inter.setX( interx );
150 inter.setY( intery );
151 }
152
153 // check if intersection is within the line
154 const double length1 = computeDistance( inter, pt1 );
155 const double length2 = computeDistance( inter, pt2 );
156 const double length3 = computeDistance( pt1, pt2 );
157 const double length4 = length1 + length2;
158
159 if ( std::fabs( length3 - length4 ) < std::numeric_limits<float>::epsilon() )
160 distance = computeDistance( inter, start );
161 }
162
163 if ( distance < minDistance && distance < maxDistance )
164 {
165 minDistance = distance;
166 idx = i;
167 }
168 }
169
170 if ( idx >= 0 )
171 {
172 rc = _addNode( idx, start, maxDistance );
174 emit clipPathChanged();
175 }
176
177 return rc;
178}
179
180void QgsLayoutNodesItem::drawNodes( QgsLayoutItemRenderContext &context ) const
181{
182 context.renderContext().painter()->setRenderHint( QPainter::Antialiasing, false );
183
184 const double rectSize = 9.0 / context.viewScaleFactor();
185
186 QVariantMap properties;
187 properties.insert( u"name"_s, u"cross"_s );
188 properties.insert( u"color_border"_s, u"red"_s );
189
190 std::unique_ptr<QgsMarkerSymbol> symbol = QgsMarkerSymbol::createSimple( properties );
191 symbol->setSize( rectSize );
192 symbol->setAngle( 45 );
193
194 symbol->startRender( context.renderContext() );
195 for ( const QPointF pt : std::as_const( mPolygon ) )
196 symbol->renderPoint( pt * context.viewScaleFactor(), nullptr, context.renderContext() );
197 symbol->stopRender( context.renderContext() );
198
199 if ( mSelectedNode >= 0 && mSelectedNode < mPolygon.size() )
200 drawSelectedNode( context );
201}
202
203void QgsLayoutNodesItem::drawSelectedNode( QgsLayoutItemRenderContext &context ) const
204{
205 const double rectSize = 9.0 / context.viewScaleFactor();
206
207 QVariantMap properties;
208 properties.insert( u"name"_s, u"square"_s );
209 properties.insert( u"color"_s, u"0, 0, 0, 0"_s );
210 properties.insert( u"color_border"_s, u"blue"_s );
211 properties.insert( u"width_border"_s, u"4"_s );
212
213 std::unique_ptr<QgsMarkerSymbol> symbol = QgsMarkerSymbol::createSimple( properties );
214 symbol->setSize( rectSize );
215
216 symbol->startRender( context.renderContext() );
217 symbol->renderPoint( mPolygon.at( mSelectedNode ) * context.viewScaleFactor(), nullptr, context.renderContext() );
218 symbol->stopRender( context.renderContext() );
219}
220
222 const bool searchInRadius,
223 const double radius ) const
224{
225 const QPointF pt = mapFromScene( node );
226 double nearestDistance = std::numeric_limits<double>::max();
227 const double maxDistance = ( searchInRadius ) ? radius : nearestDistance;
228 double distance = 0;
229 int idx = -1;
230
231 int i = 0;
232 for ( const QPointF polyPt : std::as_const( mPolygon ) )
233 {
234 distance = computeDistance( pt, polyPt );
235 if ( distance < nearestDistance && distance < maxDistance )
236 {
237 nearestDistance = distance;
238 idx = i;
239 }
240 i++;
241 }
242
243 return idx;
244}
245
246bool QgsLayoutNodesItem::nodePosition( const int index, QPointF &position ) const
247{
248 bool rc( false );
249
250 if ( index >= 0 && index < mPolygon.size() )
251 {
252 position = mapToScene( mPolygon.at( index ) );
253 rc = true;
254 }
255
256 return rc;
257}
258
259bool QgsLayoutNodesItem::removeNode( const int index )
260{
261 const bool rc = _removeNode( index );
262 if ( rc )
263 {
265 emit clipPathChanged();
266 }
267 return rc;
268}
269
270bool QgsLayoutNodesItem::moveNode( const int index, QPointF pt )
271{
272 bool rc( false );
273
274 if ( index >= 0 && index < mPolygon.size() )
275 {
276 const QPointF nodeItem = mapFromScene( pt );
277 mPolygon.replace( index, nodeItem );
279 emit clipPathChanged();
280 rc = true;
281 }
282
283 return rc;
284}
285
286bool QgsLayoutNodesItem::readPropertiesFromElement( const QDomElement &itemElem,
287 const QDomDocument &, const QgsReadWriteContext &context )
288{
289 // restore style
290 const QDomElement styleSymbolElem = itemElem.firstChildElement( u"symbol"_s );
291 if ( !styleSymbolElem.isNull() )
292 _readXmlStyle( styleSymbolElem, context );
293
294 // restore nodes
295 mPolygon.clear();
296 const QDomNodeList nodesList = itemElem.elementsByTagName( u"node"_s );
297 for ( int i = 0; i < nodesList.size(); i++ )
298 {
299 const QDomElement nodeElem = nodesList.at( i ).toElement();
300 QPointF newPt;
301 newPt.setX( nodeElem.attribute( u"x"_s ).toDouble() );
302 newPt.setY( nodeElem.attribute( u"y"_s ).toDouble() );
303 mPolygon.append( newPt );
304 }
305
306 emit changed();
307 emit clipPathChanged();
308 return true;
309}
310
312{
313 // get the bounding rect for the polygon currently displayed
314 const QRectF boundingRect = mPolygon.boundingRect();
315
316 // compute x/y ratio
317 const float ratioX = !qgsDoubleNear( boundingRect.width(), 0.0 )
318 ? rect().width() / boundingRect.width() : 0;
319 const float ratioY = !qgsDoubleNear( boundingRect.height(), 0.0 )
320 ? rect().height() / boundingRect.height() : 0;
321
322 // scaling
323 QTransform trans;
324 trans = trans.scale( ratioX, ratioY );
325 mPolygon = trans.map( mPolygon );
326 emit clipPathChanged();
327}
328
330{
331 bool rc = false;
332
333 if ( index >= 0 && index < mPolygon.size() )
334 {
335 mSelectedNode = index;
336 rc = true;
337 }
338
339 return rc;
340}
341
343{
344 // set the new scene rectangle
345 const QRectF br = mPolygon.boundingRect();
346
347 const QPointF topLeft = mapToScene( br.topLeft() );
348 //will trigger updateBoundingRect if necessary
349 attemptSetSceneRect( QRectF( topLeft.x(), topLeft.y(), br.width(), br.height() ) );
350
351 // update polygon position
352 mPolygon.translate( -br.topLeft().x(), -br.topLeft().y() );
353}
354
356{
357 QRectF br = rect();
359 prepareGeometryChange();
361
362 // update
363 update();
364}
365
366bool QgsLayoutNodesItem::writePropertiesToElement( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const
367{
368 // style
369 _writeXmlStyle( doc, elem, context );
370
371 // write nodes
372 QDomElement nodesElem = doc.createElement( u"nodes"_s );
373 for ( const QPointF pt : std::as_const( mPolygon ) )
374 {
375 QDomElement nodeElem = doc.createElement( u"node"_s );
376 nodeElem.setAttribute( u"x"_s, QString::number( pt.x() ) );
377 nodeElem.setAttribute( u"y"_s, QString::number( pt.y() ) );
378 nodesElem.appendChild( nodeElem );
379 }
380 elem.appendChild( nodesElem );
381
382 return true;
383}
@ Default
Allow raster-based rendering in situations where it is required for correct rendering or where it wil...
Definition qgis.h:2762
@ PreferVector
Prefer vector-based rendering, when the result will still be visually near-identical to a raster-base...
Definition qgis.h:2763
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:6924