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