QGIS API Documentation 3.41.0-Master (88383c3d16f)
Loading...
Searching...
No Matches
qgspointcloudlayerchunkloader_p.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudlayerchunkloader_p.cpp
3 --------------------------------------
4 Date : October 2020
5 Copyright : (C) 2020 by Peter Petrik
6 Email : zilolv dot sk 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
17#include "moc_qgspointcloudlayerchunkloader_p.cpp"
18
19#include "qgs3dutils.h"
20#include "qgsbox3d.h"
22#include "qgschunknode.h"
23#include "qgslogger.h"
24#include "qgspointcloudindex.h"
26#include "qgseventtracing.h"
27
28
33
34#include <QtConcurrent>
35#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
36#include <Qt3DRender/QAttribute>
37#else
38#include <Qt3DCore/QAttribute>
39#endif
40#include <Qt3DRender/QTechnique>
41#include <Qt3DRender/QShaderProgram>
42#include <Qt3DRender/QGraphicsApiFilter>
43#include <QPointSize>
44
46
47
49
50QgsPointCloudLayerChunkLoader::QgsPointCloudLayerChunkLoader( const QgsPointCloudLayerChunkLoaderFactory *factory, QgsChunkNode *node, std::unique_ptr<QgsPointCloud3DSymbol> symbol, const QgsCoordinateTransform &coordinateTransform, double zValueScale, double zValueOffset )
51 : QgsChunkLoader( node )
52 , mFactory( factory )
53 , mContext( factory->mRenderContext, coordinateTransform, std::move( symbol ), zValueScale, zValueOffset )
54{
55 QgsPointCloudIndex pc = mFactory->mPointCloudIndex;
56 mContext.setAttributes( pc.attributes() );
57
58 const QgsChunkNodeId nodeId = node->tileId();
59 const QgsPointCloudNodeId pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
60
61 Q_ASSERT( pc.hasNode( pcNode ) );
62
63 QgsDebugMsgLevel( QStringLiteral( "loading entity %1" ).arg( node->tileId().text() ), 2 );
64
65 if ( mContext.symbol()->symbolType() == QLatin1String( "single-color" ) )
66 mHandler.reset( new QgsSingleColorPointCloud3DSymbolHandler() );
67 else if ( mContext.symbol()->symbolType() == QLatin1String( "color-ramp" ) )
68 mHandler.reset( new QgsColorRampPointCloud3DSymbolHandler() );
69 else if ( mContext.symbol()->symbolType() == QLatin1String( "rgb" ) )
70 mHandler.reset( new QgsRGBPointCloud3DSymbolHandler() );
71 else if ( mContext.symbol()->symbolType() == QLatin1String( "classification" ) )
72 {
73 mHandler.reset( new QgsClassificationPointCloud3DSymbolHandler() );
74 const QgsClassificationPointCloud3DSymbol *classificationSymbol = dynamic_cast<const QgsClassificationPointCloud3DSymbol *>( mContext.symbol() );
75 mContext.setFilteredOutCategories( classificationSymbol->getFilteredOutCategories() );
76 }
77
78 //
79 // this will be run in a background thread
80 //
81 mFutureWatcher = new QFutureWatcher<void>( this );
82 connect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
83
84 const QgsBox3D box3D = node->box3D();
85 const QFuture<void> future = QtConcurrent::run( [pc, pcNode, box3D, this] {
86 const QgsEventTracing::ScopedEvent e( QStringLiteral( "3D" ), QStringLiteral( "PC chunk load" ) );
87
88 if ( mContext.isCanceled() )
89 {
90 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
91 return;
92 }
93
94 QgsPointCloudIndex pc2 = pc; // Copy to discard const
95 mHandler->processNode( pc2, pcNode, mContext );
96
97 if ( mContext.isCanceled() )
98 {
99 QgsDebugMsgLevel( QStringLiteral( "canceled" ), 2 );
100 return;
101 }
102
103 if ( mContext.symbol()->renderAsTriangles() )
104 mHandler->triangulate( pc2, pcNode, mContext, box3D );
105 } );
106
107 // emit finished() as soon as the handler is populated with features
108 mFutureWatcher->setFuture( future );
109}
110
111QgsPointCloudLayerChunkLoader::~QgsPointCloudLayerChunkLoader()
112{
113 if ( mFutureWatcher && !mFutureWatcher->isFinished() )
114 {
115 disconnect( mFutureWatcher, &QFutureWatcher<void>::finished, this, &QgsChunkQueueJob::finished );
116 mContext.cancelRendering();
117 mFutureWatcher->waitForFinished();
118 }
119}
120
121void QgsPointCloudLayerChunkLoader::cancel()
122{
123 mContext.cancelRendering();
124}
125
126Qt3DCore::QEntity *QgsPointCloudLayerChunkLoader::createEntity( Qt3DCore::QEntity *parent )
127{
128 QgsPointCloudIndex pc = mFactory->mPointCloudIndex;
129 const QgsChunkNodeId nodeId = mNode->tileId();
130 const QgsPointCloudNodeId pcNode( nodeId.d, nodeId.x, nodeId.y, nodeId.z );
131 Q_ASSERT( pc.hasNode( pcNode ) );
132
133 Qt3DCore::QEntity *entity = new Qt3DCore::QEntity( parent );
134 mHandler->finalize( entity, mContext );
135 return entity;
136}
137
139
140
141QgsPointCloudLayerChunkLoaderFactory::QgsPointCloudLayerChunkLoaderFactory( const Qgs3DRenderContext &context, const QgsCoordinateTransform &coordinateTransform, QgsPointCloudIndex pc, QgsPointCloud3DSymbol *symbol, double zValueScale, double zValueOffset, int pointBudget )
142 : mRenderContext( context )
143 , mCoordinateTransform( coordinateTransform )
144 , mPointCloudIndex( pc )
145 , mZValueScale( zValueScale )
146 , mZValueOffset( zValueOffset )
147 , mPointBudget( pointBudget )
148{
149 mSymbol.reset( symbol );
150
151 try
152 {
153 mExtent = mCoordinateTransform.transformBoundingBox( mRenderContext.extent(), Qgis::TransformDirection::Reverse );
154 }
155 catch ( const QgsCsException & )
156 {
157 // bad luck, can't reproject for some reason
158 QgsDebugError( QStringLiteral( "Transformation of extent failed." ) );
159 }
160}
161
162QgsChunkLoader *QgsPointCloudLayerChunkLoaderFactory::createChunkLoader( QgsChunkNode *node ) const
163{
164 const QgsChunkNodeId id = node->tileId();
165
166 Q_ASSERT( mPointCloudIndex.hasNode( QgsPointCloudNodeId( id.d, id.x, id.y, id.z ) ) );
167 QgsPointCloud3DSymbol *symbol = static_cast<QgsPointCloud3DSymbol *>( mSymbol->clone() );
168 return new QgsPointCloudLayerChunkLoader( this, node, std::unique_ptr<QgsPointCloud3DSymbol>( symbol ), mCoordinateTransform, mZValueScale, mZValueOffset );
169}
170
171int QgsPointCloudLayerChunkLoaderFactory::primitivesCount( QgsChunkNode *node ) const
172{
173 const QgsChunkNodeId id = node->tileId();
174 const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );
175 Q_ASSERT( mPointCloudIndex.hasNode( n ) );
176 return mPointCloudIndex.getNode( n ).pointCount();
177}
178
179
180static QgsBox3D nodeBoundsToBox3D( QgsBox3D nodeBounds, const QgsCoordinateTransform &coordinateTransform, double zValueOffset, double zValueScale )
181{
182 QgsVector3D extentMin3D( nodeBounds.xMinimum(), nodeBounds.yMinimum(), nodeBounds.zMinimum() * zValueScale + zValueOffset );
183 QgsVector3D extentMax3D( nodeBounds.xMaximum(), nodeBounds.yMaximum(), nodeBounds.zMaximum() * zValueScale + zValueOffset );
184 QgsCoordinateTransform extentTransform = coordinateTransform;
185 extentTransform.setBallparkTransformsAreAppropriate( true );
186 try
187 {
188 extentMin3D = extentTransform.transform( extentMin3D );
189 extentMax3D = extentTransform.transform( extentMax3D );
190 }
191 catch ( QgsCsException & )
192 {
193 QgsDebugError( QStringLiteral( "Error transforming node bounds coordinate" ) );
194 }
195 return QgsBox3D( extentMin3D.x(), extentMin3D.y(), extentMin3D.z(), extentMax3D.x(), extentMax3D.y(), extentMax3D.z() );
196}
197
198
199QgsChunkNode *QgsPointCloudLayerChunkLoaderFactory::createRootNode() const
200{
201 const QgsPointCloudNode pcNode = mPointCloudIndex.getNode( mPointCloudIndex.root() );
202 const QgsBox3D rootNodeBounds = pcNode.bounds();
203 QgsBox3D rootNodeBox3D = nodeBoundsToBox3D( rootNodeBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
204
205 const float error = pcNode.error();
206 QgsChunkNode *node = new QgsChunkNode( QgsChunkNodeId( 0, 0, 0, 0 ), rootNodeBox3D, error );
207 node->setRefinementProcess( mSymbol->renderAsTriangles() ? Qgis::TileRefinementProcess::Replacement : Qgis::TileRefinementProcess::Additive );
208 return node;
209}
210
211QVector<QgsChunkNode *> QgsPointCloudLayerChunkLoaderFactory::createChildren( QgsChunkNode *node ) const
212{
213 QVector<QgsChunkNode *> children;
214 const QgsChunkNodeId nodeId = node->tileId();
215 const float childError = node->error() / 2;
216
217 for ( int i = 0; i < 8; ++i )
218 {
219 int dx = i & 1, dy = !!( i & 2 ), dz = !!( i & 4 );
220 const QgsChunkNodeId childId( nodeId.d + 1, nodeId.x * 2 + dx, nodeId.y * 2 + dy, nodeId.z * 2 + dz );
221 const QgsPointCloudNodeId childPcId( childId.d, childId.x, childId.y, childId.z );
222 if ( !mPointCloudIndex.hasNode( childPcId ) )
223 continue;
224 const QgsPointCloudNode childNode = mPointCloudIndex.getNode( childPcId );
225 const QgsBox3D childBounds = childNode.bounds();
226 if ( !mExtent.isEmpty() && !childBounds.intersects( mExtent ) )
227 continue;
228
229 QgsBox3D childBox3D = nodeBoundsToBox3D( childBounds, mCoordinateTransform, mZValueOffset, mZValueScale );
230
231 QgsChunkNode *child = new QgsChunkNode( childId, childBox3D, childError, node );
232 child->setRefinementProcess( mSymbol->renderAsTriangles() ? Qgis::TileRefinementProcess::Replacement : Qgis::TileRefinementProcess::Additive );
233 children << child;
234 }
235 return children;
236}
237
239
240
241static QgsChunkNode *findChunkNodeFromNodeId( QgsChunkNode *rootNode, QgsPointCloudNodeId nodeId )
242{
243 // find path from the node to the root
244 QVector<QgsPointCloudNodeId> parentIds;
245 while ( nodeId.d() > 0 )
246 {
247 parentIds << nodeId;
248 nodeId = nodeId.parentNode();
249 }
250
251 // now descend from the root to the node in the QgsChunkNode hierarchy
252 QgsChunkNode *chunk = rootNode;
253 while ( !parentIds.empty() )
254 {
255 QgsPointCloudNodeId p = parentIds.takeLast();
256 QgsChunkNodeId childNodeId( p.d(), p.x(), p.y(), p.z() );
257
258 QgsChunkNode *chunkChild = nullptr;
259 QgsChunkNode *const *children = chunk->children();
260 for ( int i = 0; i < chunk->childCount(); ++i )
261 {
262 if ( children[i]->tileId() == childNodeId )
263 {
264 chunkChild = children[i];
265 break;
266 }
267 }
268 Q_ASSERT( chunkChild );
269 chunk = chunkChild;
270 }
271 return chunk;
272}
273
274
275QgsPointCloudLayerChunkedEntity::QgsPointCloudLayerChunkedEntity( Qgs3DMapSettings *map, QgsPointCloudLayer *pcl, QgsPointCloudIndex index, const QgsCoordinateTransform &coordinateTransform, QgsPointCloud3DSymbol *symbol, float maximumScreenSpaceError, bool showBoundingBoxes, double zValueScale, double zValueOffset, int pointBudget )
276 : QgsChunkedEntity( map, maximumScreenSpaceError, new QgsPointCloudLayerChunkLoaderFactory( Qgs3DRenderContext::fromMapSettings( map ), coordinateTransform, index, symbol, zValueScale, zValueOffset, pointBudget ), true, pointBudget )
277 , mLayer( pcl )
278{
279 setShowBoundingBoxes( showBoundingBoxes );
280
281 if ( pcl->supportsEditing() )
282 {
283 // when editing starts or stops, we need to update our index to use the editing index (or not)
284 connect( pcl, &QgsPointCloudLayer::editingStarted, this, &QgsPointCloudLayerChunkedEntity::updateIndex );
285 connect( pcl, &QgsPointCloudLayer::editingStopped, this, &QgsPointCloudLayerChunkedEntity::updateIndex );
286
287 mChunkUpdaterFactory.reset( new QgsChunkUpdaterFactory( mChunkLoaderFactory ) );
288
289 connect( pcl, &QgsPointCloudLayer::chunkAttributeValuesChanged, this, [this]( const QgsPointCloudNodeId &n ) {
290 QList<QgsChunkNode *> nodes;
291 nodes << findChunkNodeFromNodeId( mRootNode, n );
292 updateNodes( nodes, mChunkUpdaterFactory.get() );
293 } );
294 }
295}
296
297QgsPointCloudLayerChunkedEntity::~QgsPointCloudLayerChunkedEntity()
298{
299 // cancel / wait for jobs
300 cancelActiveJobs();
301}
302
303void QgsPointCloudLayerChunkedEntity::updateIndex()
304{
305 static_cast<QgsPointCloudLayerChunkLoaderFactory *>( mChunkLoaderFactory )->mPointCloudIndex = mLayer->index();
306}
307
308QVector<QgsRayCastingUtils::RayHit> QgsPointCloudLayerChunkedEntity::rayIntersection( const QgsRayCastingUtils::Ray3D &ray, const QgsRayCastingUtils::RayCastContext &context ) const
309{
310 QVector<QgsRayCastingUtils::RayHit> result;
311 QgsPointCloudLayerChunkLoaderFactory *factory = static_cast<QgsPointCloudLayerChunkLoaderFactory *>( mChunkLoaderFactory );
312
313 // transform ray
314 const QgsVector3D rayOriginMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() );
315 const QgsVector3D pointMapCoords = factory->mRenderContext.worldToMapCoordinates( ray.origin() + ray.origin().length() * ray.direction().normalized() );
316 QgsVector3D rayDirectionMapCoords = pointMapCoords - rayOriginMapCoords;
317 rayDirectionMapCoords.normalize();
318
319 const int screenSizePx = std::max( context.screenSize.height(), context.screenSize.width() );
320
321 const QgsPointCloud3DSymbol *symbol = factory->mSymbol.get();
322 // Symbol can be null in case of no rendering enabled
323 if ( !symbol )
324 return result;
325 const double pointSize = symbol->pointSize();
326
327 // We're using the angle as a tolerance, effectively meaning we're fetching points intersecting a cone.
328 // This may be revisited to use a cylinder instead, if the balance between near/far points does not scale
329 // well with different point sizes, screen sizes and fov values.
330 const double limitAngle = 2. * pointSize / screenSizePx * factory->mRenderContext.fieldOfView();
331
332 // adjust ray to elevation properties
333 const QgsVector3D adjustedRayOrigin = QgsVector3D( rayOriginMapCoords.x(), rayOriginMapCoords.y(), ( rayOriginMapCoords.z() - factory->mZValueOffset ) / factory->mZValueScale );
334 QgsVector3D adjustedRayDirection = QgsVector3D( rayDirectionMapCoords.x(), rayDirectionMapCoords.y(), rayDirectionMapCoords.z() / factory->mZValueScale );
335 adjustedRayDirection.normalize();
336
337 QgsPointCloudIndex index = factory->mPointCloudIndex;
338
339 const QgsPointCloudAttributeCollection attributeCollection = index.attributes();
340 QgsPointCloudRequest request;
341 request.setAttributes( attributeCollection );
342
343 double minDist = -1.;
344 const QList<QgsChunkNode *> activeNodes = this->activeNodes();
345 for ( QgsChunkNode *node : activeNodes )
346 {
347 const QgsChunkNodeId id = node->tileId();
348 const QgsPointCloudNodeId n( id.d, id.x, id.y, id.z );
349
350 if ( !index.hasNode( n ) )
351 continue;
352
353 const QgsAABB nodeBbox = Qgs3DUtils::mapToWorldExtent( node->box3D(), mMapSettings->origin() );
354 if ( !QgsRayCastingUtils::rayBoxIntersection( ray, nodeBbox ) )
355 continue;
356
357 std::unique_ptr<QgsPointCloudBlock> block( index.nodeData( n, request ) );
358 if ( !block )
359 continue;
360
361 const QgsVector3D blockScale = block->scale();
362 const QgsVector3D blockOffset = block->offset();
363
364 const char *ptr = block->data();
365 const QgsPointCloudAttributeCollection blockAttributes = block->attributes();
366 const std::size_t recordSize = blockAttributes.pointRecordSize();
367 int xOffset = 0, yOffset = 0, zOffset = 0;
368 const QgsPointCloudAttribute::DataType xType = blockAttributes.find( QStringLiteral( "X" ), xOffset )->type();
369 const QgsPointCloudAttribute::DataType yType = blockAttributes.find( QStringLiteral( "Y" ), yOffset )->type();
370 const QgsPointCloudAttribute::DataType zType = blockAttributes.find( QStringLiteral( "Z" ), zOffset )->type();
371 for ( int i = 0; i < block->pointCount(); ++i )
372 {
373 double x, y, z;
374 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize, xOffset, xType, yOffset, yType, zOffset, zType, blockScale, blockOffset, x, y, z );
375 const QgsVector3D point( x, y, z );
376
377 // check whether point is in front of the ray
378 // similar to QgsRay3D::isInFront(), but using doubles
379 QgsVector3D vectorToPoint = point - adjustedRayOrigin;
380 vectorToPoint.normalize();
381 if ( QgsVector3D::dotProduct( vectorToPoint, adjustedRayDirection ) < 0.0 )
382 continue;
383
384 // calculate the angle between the point and the projected point
385 // similar to QgsRay3D::angleToPoint(), but using doubles
386 const QgsVector3D projPoint = adjustedRayOrigin + adjustedRayDirection * QgsVector3D::dotProduct( point - adjustedRayOrigin, adjustedRayDirection );
387 const QgsVector3D v1 = projPoint - adjustedRayOrigin;
388 const QgsVector3D v2 = point - projPoint;
389 double angle = std::atan2( v2.length(), v1.length() ) * 180 / M_PI;
390 if ( angle > limitAngle )
391 continue;
392
393 const double dist = rayOriginMapCoords.distance( point );
394
395 if ( minDist < 0 || dist < minDist )
396 {
397 minDist = dist;
398 }
399 else if ( context.singleResult )
400 {
401 continue;
402 }
403
404 // Note : applying elevation properties is done in fromPointCloudIdentificationToIdentifyResults
405 QVariantMap pointAttr = QgsPointCloudAttribute::getAttributeMap( ptr, i * recordSize, blockAttributes );
406 pointAttr[QStringLiteral( "X" )] = x;
407 pointAttr[QStringLiteral( "Y" )] = y;
408 pointAttr[QStringLiteral( "Z" )] = z;
409
410 const QgsVector3D worldPoint = factory->mRenderContext.mapToWorldCoordinates( point );
411 QgsRayCastingUtils::RayHit hit( dist, worldPoint.toVector3D(), FID_NULL, pointAttr );
412 if ( context.singleResult )
413 result.clear();
414 result.append( hit );
415 }
416 }
417 return result;
418}
The Qgis class provides global constants for use throughout the application.
Definition qgis.h:54
@ Replacement
When tile is refined then its children should be used in place of itself.
@ Reverse
Reverse/inverse transform (from destination to source)
static QgsAABB mapToWorldExtent(const QgsRectangle &extent, double zMin, double zMax, const QgsVector3D &mapOrigin)
Converts map extent to axis aligned bounding box in 3D world coordinates.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:43
double yMaximum() const
Returns the maximum y value.
Definition qgsbox3d.h:246
bool intersects(const QgsBox3D &other) const
Returns true if box intersects with another box.
Definition qgsbox3d.cpp:144
double xMinimum() const
Returns the minimum x value.
Definition qgsbox3d.h:211
double zMaximum() const
Returns the maximum z value.
Definition qgsbox3d.h:274
double xMaximum() const
Returns the maximum x value.
Definition qgsbox3d.h:218
double zMinimum() const
Returns the minimum z value.
Definition qgsbox3d.h:267
double yMinimum() const
Returns the minimum y value.
Definition qgsbox3d.h:239
QgsPointCloudCategoryList getFilteredOutCategories() const
Gets the list of categories of the classification that should not be rendered.
Class for doing transforms between two map coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
void editingStopped()
Emitted when edited changes have been successfully written to the data provider.
void editingStarted()
Emitted when editing on this layer has started.
float pointSize() const
Returns the point size of the point cloud.
Collection of point cloud attributes.
int pointRecordSize() const
Returns total size of record.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
QVector< QgsPointCloudAttribute > attributes() const
Returns all attributes.
DataType
Systems of unit measurement.
static void getPointXYZ(const char *ptr, int i, std::size_t pointRecordSize, int xOffset, QgsPointCloudAttribute::DataType xType, int yOffset, QgsPointCloudAttribute::DataType yType, int zOffset, QgsPointCloudAttribute::DataType zType, const QgsVector3D &indexScale, const QgsVector3D &indexOffset, double &x, double &y, double &z)
Retrieves the x, y, z values for the point at index i.
static QVariantMap getAttributeMap(const char *data, std::size_t recordOffset, const QgsPointCloudAttributeCollection &attributeCollection)
Retrieves all the attributes of a point.
DataType type() const
Returns the data type.
Smart pointer for QgsAbstractPointCloudIndex.
std::unique_ptr< QgsPointCloudBlock > nodeData(const QgsPointCloudNodeId &n, const QgsPointCloudRequest &request)
Returns node data block.
bool hasNode(const QgsPointCloudNodeId &id) const
Returns whether the octree contain given node.
QgsPointCloudAttributeCollection attributes() const
Returns all attributes that are stored in the file.
Represents a map layer supporting display of point clouds.
void chunkAttributeValuesChanged(const QgsPointCloudNodeId &n)
Emitted when a node gets some attribute values of some points changed.
bool supportsEditing() const override
Returns whether the layer supports editing or not.
Represents a indexed point cloud node's position in octree.
int d() const
Returns d.
int y() const
Returns y.
int x() const
Returns x.
int z() const
Returns z.
QgsPointCloudNodeId parentNode() const
Returns the parent of the node.
Keeps metadata for indexed point cloud node.
float error() const
Returns node's error in map units (used to determine in whether the node has enough detail for the cu...
QgsBox3D bounds() const
Returns node's bounding cube in CRS coords.
Point cloud data request.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition qgsvector3d.h:31
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:50
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:52
QVector3D toVector3D() const
Converts the current object to QVector3D.
static double dotProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the dot product of two vectors.
double x() const
Returns X coordinate.
Definition qgsvector3d.h:48
void normalize()
Normalizes the current vector in place.
double length() const
Returns the length of the vector.
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)
#define FID_NULL
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:41
#define QgsDebugError(str)
Definition qgslogger.h:40
Helper struct to store ray casting parameters.
QSize screenSize
QSize of the 3d engine window.
bool singleResult
If set to true, only the closest point cloud hit will be returned (other entities always return only ...
Helper struct to store ray casting results.