QGIS API Documentation 3.99.0-Master (752b475928d)
Loading...
Searching...
No Matches
qgs3dmapcanvas.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgs3dmapcanvas.cpp
3 --------------------------------------
4 Date : July 2017
5 Copyright : (C) 2017 by Martin Dobias
6 Email : wonder 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
16#include <Qt3DCore/QAspectEngine>
17
18#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
19#include <Qt3DCore/QCoreAspect>
20#endif
21#include <Qt3DRender/QRenderSettings>
22#include <Qt3DRender/QRenderAspect>
23#include <Qt3DInput/QInputAspect>
24#include <Qt3DInput/QInputSettings>
25#include <Qt3DLogic/QLogicAspect>
26#include <Qt3DLogic/QFrameAction>
27
28#include "qgs3daxis.h"
29#include "qgs3dmapcanvas.h"
30#include "qgs3dmapscene.h"
31#include "qgswindow3dengine.h"
32#include "qgs3dmapsettings.h"
33#include "qgs3dmaptool.h"
35#include "qgsframegraph.h"
37#include "qgsrubberband3d.h"
38#include "qgs3dutils.h"
39#include "qgsraycastcontext.h"
40
41#include "moc_qgs3dmapcanvas.cpp"
42
43
45 : m_aspectEngine( new Qt3DCore::QAspectEngine )
46 , m_renderAspect( new Qt3DRender::QRenderAspect )
47 , m_inputAspect( new Qt3DInput::QInputAspect )
48 , m_logicAspect( new Qt3DLogic::QLogicAspect )
49 , m_renderSettings( new Qt3DRender::QRenderSettings )
50 , m_defaultCamera( new Qt3DRender::QCamera )
51 , m_inputSettings( new Qt3DInput::QInputSettings )
52 , m_root( new Qt3DCore::QEntity )
53{
54 setSurfaceType( QSurface::OpenGLSurface );
55
56 // register aspects
57#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
58 m_aspectEngine->registerAspect( new Qt3DCore::QCoreAspect );
59#endif
60 m_aspectEngine->registerAspect( m_renderAspect );
61 m_aspectEngine->registerAspect( m_inputAspect );
62 m_aspectEngine->registerAspect( m_logicAspect );
63
64 m_defaultCamera->setParent( m_root );
65 m_inputSettings->setEventSource( this );
66
67 const QgsSettings setting;
68 mEngine = new QgsWindow3DEngine( this );
69
70 connect( mEngine, &QgsAbstract3DEngine::imageCaptured, this, [this]( const QImage &image ) {
71 if ( image.save( mCaptureFileName, mCaptureFileFormat.toLocal8Bit().data() ) )
72 {
73 emit savedAsImage( mCaptureFileName );
74 }
75 } );
76
77 setCursor( Qt::OpenHandCursor );
78 installEventFilter( this );
79}
80
82{
83 if ( mMapTool )
84 delete mMapTool;
85 // make sure the scene is deleted while map settings object is still alive
86 mScene->deleteLater();
87 mScene = nullptr;
88 mMapSettings->deleteLater();
89 mMapSettings = nullptr;
90 qDeleteAll( mHighlights );
91 mHighlights.clear();
92
93 delete m_aspectEngine;
94}
95
96void Qgs3DMapCanvas::setRootEntity( Qt3DCore::QEntity *root )
97{
98 if ( m_userRoot != root )
99 {
100 if ( m_userRoot )
101 m_userRoot->setParent( static_cast<Qt3DCore::QNode *>( nullptr ) );
102 if ( root )
103 root->setParent( m_root );
104 m_userRoot = root;
105 }
106}
107
108void Qgs3DMapCanvas::setActiveFrameGraph( Qt3DRender::QFrameGraphNode *activeFrameGraph )
109{
110 m_renderSettings->setActiveFrameGraph( activeFrameGraph );
111}
112
113Qt3DRender::QFrameGraphNode *Qgs3DMapCanvas::activeFrameGraph() const
114{
115 return m_renderSettings->activeFrameGraph();
116}
117
118Qt3DRender::QCamera *Qgs3DMapCanvas::camera() const
119{
120 return m_defaultCamera;
121}
122
123Qt3DRender::QRenderSettings *Qgs3DMapCanvas::renderSettings() const
124{
125 return m_renderSettings;
126}
127
128void Qgs3DMapCanvas::showEvent( QShowEvent *e )
129{
130 if ( !m_initialized )
131 {
132 m_root->addComponent( m_renderSettings );
133 m_root->addComponent( m_inputSettings );
134 m_aspectEngine->setRootEntity( Qt3DCore::QEntityPtr( m_root ) );
135
136 m_initialized = true;
137 }
138 QWindow::showEvent( e );
139}
140
141void Qgs3DMapCanvas::resizeEvent( QResizeEvent * )
142{
143 m_defaultCamera->setAspectRatio( float( width() ) / std::max( 1.f, static_cast<float>( height() ) ) );
144
145 mEngine->setSize( size() );
146}
147
149{
150 // TODO: eventually we want to get rid of this
151 Q_ASSERT( !mMapSettings );
152 Q_ASSERT( !mScene );
153
154 Qgs3DMapScene *newScene = new Qgs3DMapScene( *mapSettings, mEngine );
155
156 mEngine->setSize( size() );
157 mEngine->setRootEntity( newScene );
158
159 if ( mScene )
160 {
161 mScene->deleteLater();
162 }
163 mScene = newScene;
167
168 delete mMapSettings;
169 mMapSettings = mapSettings;
170
171 resetView();
172
173 connect( cameraController(), &QgsCameraController::setCursorPosition, this, [this]( QPoint point ) {
174 QCursor::setPos( mapToGlobal( point ) );
175 } );
178 connect( cameraController(), &QgsCameraController::navigationModeChanged, this, &Qgs3DMapCanvas::onNavigationModeChanged );
179 connect( cameraController(), &QgsCameraController::requestDepthBufferCapture, this, &Qgs3DMapCanvas::captureDepthBuffer );
180
182
183 emit mapSettingsChanged();
184}
185
187{
188 return mScene ? mScene->cameraController() : nullptr;
189}
190
191QgsRayCastResult Qgs3DMapCanvas::castRay( const QPoint &screenPoint, QgsRayCastContext context )
192{
193 const QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( screenPoint, size(), camera() );
194 if ( context.maximumDistance() < 0 )
195 context.setMaximumDistance( camera()->farPlane() );
196 const QgsRayCastResult res = Qgs3DUtils::castRay( mScene, ray, context );
197 return res;
198}
199
200void Qgs3DMapCanvas::enableCrossSection( const QgsPointXY &startPoint, const QgsPointXY &endPoint, double width, bool setSideView )
201{
202 if ( !mScene )
203 return;
204
205 const QgsVector3D startVec { startPoint.x(), startPoint.y(), 0 };
206 const QgsVector3D endVec { endPoint.x(), endPoint.y(), 0 };
207 const QList<QVector4D> clippingPlanes = Qgs3DUtils::lineSegmentToClippingPlanes(
208 startVec,
209 endVec,
210 width,
211 mMapSettings->origin()
212 );
213
214 if ( setSideView )
215 {
216 // calculate the middle of the front side defined by clipping planes
217 QgsVector linePerpVec( ( endPoint - startPoint ).x(), ( endPoint - startPoint ).y() );
218 linePerpVec = -linePerpVec.normalized().perpVector();
219 const QgsVector3D linePerpVec3D( linePerpVec.x(), linePerpVec.y(), 0 );
220 const QgsVector3D frontStartPoint( startVec + linePerpVec3D * width );
221 const QgsVector3D frontEndPoint( endVec + linePerpVec3D * width );
222
224 frontStartPoint,
225 frontEndPoint,
226 mScene->elevationRange( true ),
227 mScene->cameraController()->camera()->fieldOfView(),
228 mMapSettings->origin()
229 );
230
231 mScene->cameraController()->setCameraPose( camPose );
232 }
233
234 mScene->enableClipping( clippingPlanes );
235 emit crossSectionEnabledChanged( true );
236}
237
239{
240 if ( !mScene )
241 return;
242
243 mScene->disableClipping();
244 emit crossSectionEnabledChanged( false );
245}
246
248{
249 return mScene ? !mScene->clipPlaneEquations().isEmpty() : false;
250}
251
253{
254 if ( !mScene )
255 return;
256
257 mScene->viewZoomFull();
258}
259
260void Qgs3DMapCanvas::setViewFromTop( const QgsPointXY &center, float distance, float rotation )
261{
262 if ( !mScene )
263 return;
264
265 const float worldX = center.x() - mMapSettings->origin().x();
266 const float worldY = center.y() - mMapSettings->origin().y();
267 mScene->cameraController()->setViewFromTop( worldX, worldY, distance, rotation );
268}
269
270void Qgs3DMapCanvas::saveAsImage( const QString &fileName, const QString &fileFormat )
271{
272 if ( !mScene || fileName.isEmpty() )
273 return;
274
275 mCaptureFileName = fileName;
276 mCaptureFileFormat = fileFormat;
277 // Setup a frame action that is used to wait until next frame
278 Qt3DLogic::QFrameAction *screenCaptureFrameAction = new Qt3DLogic::QFrameAction;
279 mScene->addComponent( screenCaptureFrameAction );
280 // Wait to have the render capture enabled in the next frame
281 connect( screenCaptureFrameAction, &Qt3DLogic::QFrameAction::triggered, this, [this, screenCaptureFrameAction]( float ) {
282 mEngine->requestCaptureImage();
283 mScene->removeComponent( screenCaptureFrameAction );
284 screenCaptureFrameAction->deleteLater();
285 } );
286}
287
288void Qgs3DMapCanvas::captureDepthBuffer()
289{
290 if ( !mScene )
291 return;
292
293 // Setup a frame action that is used to wait until next frame
294 Qt3DLogic::QFrameAction *screenCaptureFrameAction = new Qt3DLogic::QFrameAction;
295 mScene->addComponent( screenCaptureFrameAction );
296 // Wait to have the render capture enabled in the next frame
297 connect( screenCaptureFrameAction, &Qt3DLogic::QFrameAction::triggered, this, [this, screenCaptureFrameAction]( float ) {
298 mEngine->requestDepthBufferCapture();
299 mScene->removeComponent( screenCaptureFrameAction );
300 screenCaptureFrameAction->deleteLater();
301 } );
302}
303
305{
306 if ( !mScene )
307 return;
308
309 if ( tool == mMapTool )
310 return;
311
312 // For Camera Control tool
313 if ( mMapTool && !tool )
314 {
315 mScene->cameraController()->setEnabled( true );
316 setCursor( Qt::OpenHandCursor );
317 }
318
319 if ( mMapTool )
320 mMapTool->deactivate();
321
322 mMapTool = tool;
323
324 if ( mMapTool )
325 {
326 mMapTool->activate();
327 setCursor( mMapTool->cursor() );
328 }
329}
330
331bool Qgs3DMapCanvas::eventFilter( QObject *watched, QEvent *event )
332{
333 if ( watched != this )
334 return false;
335
336 if ( mScene && mScene->get3DAxis() && mScene->get3DAxis()->handleEvent( event ) )
337 {
338 event->accept();
339 return true;
340 }
341
342 // ShortcutOverride is sent if the pressed key is "claimed" by a shortcut,
343 // but we are given a chance to handle it anyway. We need to basically treat
344 // it as if it were a KeyPress.
345 if ( event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease || event->type() == QEvent::ShortcutOverride )
346 {
347 // if the camera controller will handle a key event, don't allow it to propagate
348 // outside of the 3d window or it may be grabbed by a parent window level shortcut
349 // and accordingly never be received by the camera controller
350 if ( cameraController() && cameraController()->keyboardEventFilter( static_cast<QKeyEvent *>( event ) ) )
351 {
352 event->accept();
353 return true;
354 }
355 return false;
356 }
357
358 if ( !mMapTool )
359 return false;
360
361 switch ( event->type() )
362 {
363 case QEvent::MouseButtonPress:
364 mMapTool->mousePressEvent( static_cast<QMouseEvent *>( event ) );
365 break;
366 case QEvent::MouseButtonRelease:
367 mMapTool->mouseReleaseEvent( static_cast<QMouseEvent *>( event ) );
368 break;
369 case QEvent::MouseMove:
370 mMapTool->mouseMoveEvent( static_cast<QMouseEvent *>( event ) );
371 break;
372 case QEvent::KeyPress:
373 mMapTool->keyPressEvent( static_cast<QKeyEvent *>( event ) );
374 break;
375 case QEvent::KeyRelease:
376 mMapTool->keyReleaseEvent( static_cast<QKeyEvent *>( event ) );
377 break;
378 case QEvent::Wheel:
379 mMapTool->mouseWheelEvent( static_cast<QWheelEvent *>( event ) );
380 break;
381 default:
382 break;
383 }
384 return false;
385}
386
388{
389 if ( mTemporalController )
390 disconnect( mTemporalController, &QgsTemporalController::updateTemporalRange, this, &Qgs3DMapCanvas::updateTemporalRange );
391
392 mTemporalController = temporalController;
393 connect( mTemporalController, &QgsTemporalController::updateTemporalRange, this, &Qgs3DMapCanvas::updateTemporalRange );
394}
395
396void Qgs3DMapCanvas::updateTemporalRange( const QgsDateTimeRange &temporalrange )
397{
398 if ( !mScene )
399 return;
400
401 mMapSettings->setTemporalRange( temporalrange );
402 mScene->updateTemporal();
403}
404
405void Qgs3DMapCanvas::onNavigationModeChanged( Qgis::NavigationMode mode )
406{
407 mMapSettings->setCameraNavigationMode( mode );
408}
409
411{
412 if ( !mScene )
413 return;
414
415 mScene->setViewFrom2DExtent( extent );
416}
417
419{
420 return mScene ? mScene->viewFrustum2DExtent() : QVector<QgsPointXY>();
421}
422
424{
425 // we only support point clouds for now
426 if ( layer->type() != Qgis::LayerType::PointCloud )
427 return;
428
429 const QgsGeometry geom = feature.geometry();
430 const QgsPoint pt( geom.vertexAt( 0 ) );
431
432 if ( !mHighlights.contains( layer ) )
433 {
434 QgsRubberBand3D *band = new QgsRubberBand3D( *mMapSettings, mEngine, mEngine->frameGraph()->rubberBandsRootEntity(), Qgis::GeometryType::Point );
435
436 const QgsSettings settings;
437 const QColor color = QColor( settings.value( QStringLiteral( "Map/highlight/color" ), Qgis::DEFAULT_HIGHLIGHT_COLOR.name() ).toString() );
438 band->setColor( color );
439 band->setMarkerType( QgsRubberBand3D::MarkerType::Square );
440 if ( QgsPointCloudLayer3DRenderer *pcRenderer = dynamic_cast<QgsPointCloudLayer3DRenderer *>( layer->renderer3D() ) )
441 {
442 band->setWidth( pcRenderer->symbol()->pointSize() + 1 );
443 }
444 mHighlights.insert( layer, band );
445
446 connect( layer, &QgsMapLayer::renderer3DChanged, this, &Qgs3DMapCanvas::updateHighlightSizes );
447 }
448 mHighlights[layer]->addPoint( pt );
449}
450
451void Qgs3DMapCanvas::updateHighlightSizes()
452{
453 if ( QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() ) )
454 {
455 if ( QgsPointCloudLayer3DRenderer *rnd = dynamic_cast<QgsPointCloudLayer3DRenderer *>( layer->renderer3D() ) )
456 {
457 if ( mHighlights.contains( layer ) )
458 {
459 mHighlights[layer]->setWidth( rnd->symbol()->pointSize() + 1 );
460 }
461 }
462 }
463}
464
466{
467 for ( auto it = mHighlights.keyBegin(); it != mHighlights.keyEnd(); it++ )
468 {
469 disconnect( it.base().key(), &QgsMapLayer::renderer3DChanged, this, &Qgs3DMapCanvas::updateHighlightSizes );
470 }
471
472 qDeleteAll( mHighlights );
473 mHighlights.clear();
474}
static const QColor DEFAULT_HIGHLIGHT_COLOR
Default highlight color.
Definition qgis.h:6184
@ Point
Points.
Definition qgis.h:359
NavigationMode
The navigation mode used by 3D cameras.
Definition qgis.h:4155
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:197
void saveAsImage(const QString &fileName, const QString &fileFormat)
Saves the current scene as an image.
QVector< QgsPointXY > viewFrustum2DExtent()
Calculates the 2D extent viewed by the 3D camera as the vertices of the viewed trapezoid.
Qgs3DMapSettings * mapSettings()
Returns access to the 3D scene configuration.
void setTemporalController(QgsTemporalController *temporalController)
Sets the temporal controller.
bool crossSectionEnabled() const
Returns true if the cross section mode is enabled or the 3d scene has other clipping planes applied.
void mapSettingsChanged()
Emitted when the the map setting is changed.
void crossSectionEnabledChanged(bool enabled)
Emitted when the cross section mode is enabled or disabled.
Qt3DRender::QCamera * camera() const
Returns the default camera of the 3D Window.
void viewed2DExtentFrom3DChanged(QVector< QgsPointXY > extent)
Emitted when the viewed 2D extent seen by the 3D camera has changed.
void enableCrossSection(const QgsPointXY &startPoint, const QgsPointXY &endPoint, double tolerance, bool setSideView=true)
Enables cross section mode for the 3D map canvas.
void fpsCountChanged(float fpsCount)
Emitted when the FPS count changes (at most every frame).
void setRootEntity(Qt3DCore::QEntity *root)
Sets the specified root entity of the scene.
void setViewFromTop(const QgsPointXY &center, float distance, float rotation=0)
Sets camera position to look down at the given point (in map coordinates) in given distance from plan...
void setActiveFrameGraph(Qt3DRender::QFrameGraphNode *activeFrameGraph)
Activates the specified activeFrameGraph.
void setMapSettings(Qgs3DMapSettings *mapSettings)
Configure map scene being displayed. Takes ownership.
void showEvent(QShowEvent *e) override
Manages the display events specified in e.
void cameraNavigationSpeedChanged(double speed)
Emitted when the camera navigation speed is changed.
void disableCrossSection()
disableCrossSection Disables the cross section mode and removes the scene's clipping planes
Qt3DRender::QRenderSettings * renderSettings() const
Returns the render settings of the 3D Window.
~Qgs3DMapCanvas() override
Qt3DRender::QFrameGraphNode * activeFrameGraph() const
Returns the node of the active frame graph.
void setMapTool(Qgs3DMapTool *tool)
Sets the active map tool that will receive events from the 3D canvas.
void setViewFrom2DExtent(const QgsRectangle &extent)
Resets camera view to show the extent (top view).
QgsRayCastResult castRay(const QPoint &screenPoint, QgsRayCastContext context)
Casts a ray towards the 3d scene and returns information about the intersected 3d entities.
void resizeEvent(QResizeEvent *) override
Resets the aspect ratio of the 3D window.
void highlightFeature(const QgsFeature &feature, QgsMapLayer *layer)
Highlights a feature from layer using a QgsRubberBand3D.
void resetView()
Resets camera position to the default: looking down at the origin of world coordinates.
void savedAsImage(const QString &fileName)
Emitted when the 3D map canvas was successfully saved as image.
void clearHighlights()
Clears all QgsRubberBand3D highlights.
void fpsCounterEnabledChanged(bool enabled)
Emitted when the FPS counter is enabled or disabeld.
QgsCameraController * cameraController()
Returns access to the view's camera controller. Returns nullptr if the scene has not been initialized...
bool eventFilter(QObject *watched, QEvent *event) override
Entity that encapsulates our 3D scene - contains all other entities (such as terrain) as children.
void viewed2DExtentFrom3DChanged(QVector< QgsPointXY > extent)
Emitted when the viewed 2D extent seen by the 3D camera has changed.
void fpsCountChanged(float fpsCount)
Emitted when the FPS count changes.
void updateTemporal()
Updates the temporale entities.
void fpsCounterEnabledChanged(bool fpsCounterEnabled)
Emitted when the FPS counter is activated or deactivated.
Definition of the world.
void setCameraMovementSpeed(double movementSpeed)
Sets the camera movement speed.
Base class for map tools operating on 3D map canvas.
static QList< QVector4D > lineSegmentToClippingPlanes(const QgsVector3D &startPoint, const QgsVector3D &endPoint, double distance, const QgsVector3D &origin)
Returns a list of 4 planes derived from a line extending from startPoint to endPoint.
static QgsRayCastResult castRay(Qgs3DMapScene *scene, const QgsRay3D &ray, const QgsRayCastContext &context)
Casts a ray through the scene and returns information about the intersecting entities (ray uses World...
static QgsRay3D rayFromScreenPoint(const QPoint &point, const QSize &windowSize, Qt3DRender::QCamera *camera)
Convert from clicked point on the screen to a ray in world coordinates.
static QgsCameraPose lineSegmentToCameraPose(const QgsVector3D &startPoint, const QgsVector3D &endPoint, const QgsDoubleRange &elevationRange, float fieldOfView, const QgsVector3D &worldOrigin)
Returns the camera pose for a camera looking at mid-point between startPoint and endPoint.
void requestDepthBufferCapture()
Starts a request for an image containing the depth buffer data of the engine.
void imageCaptured(const QImage &image)
Emitted after a call to requestCaptureImage() to return the captured image.
void depthBufferCaptured(const QImage &image)
Emitted after a call to requestDepthBufferCapture() to return the captured depth buffer.
Object that controls camera movement based on user input.
void navigationModeChanged(Qgis::NavigationMode mode)
Emitted when the navigation mode is changed using the hotkey ctrl + ~.
void requestDepthBufferCapture()
Emitted to ask for the depth buffer image.
void cameraMovementSpeedChanged(double speed)
Emitted whenever the camera movement speed is changed by the controller.
void depthBufferCaptured(const QImage &depthImage)
Sets the depth buffer image used by the camera controller to calculate world position from a pixel's ...
void setCursorPosition(QPoint point)
Emitted when the mouse cursor position should be moved to the specified point on the map viewport.
Encapsulates camera pose in a 3D scene.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsGeometry geometry
Definition qgsfeature.h:69
A geometry is the spatial representation of a feature.
QgsPoint vertexAt(int atVertex) const
Returns coordinates of a vertex.
Base class for all map layer types.
Definition qgsmaplayer.h:80
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.
Qgis::LayerType type
Definition qgsmaplayer.h:90
3D renderer that renders all points from a point cloud layer.
Represents a 2D point.
Definition qgspointxy.h:60
double y
Definition qgspointxy.h:64
double x
Definition qgspointxy.h:63
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
A representation of a ray in 3D.
Definition qgsray3d.h:31
Responsible for defining parameters of the ray casting operations in 3D map canvases.
float maximumDistance() const
The maximum distance from ray origin to look for hits when casting a ray.
void setMaximumDistance(float distance)
Sets the maximum distance from ray origin to look for hits when casting a ray.
Contains the results of ray casting operations in a 3D map canvas.
A rectangle specified with double values.
Stores settings for use within QGIS.
Definition qgssettings.h:65
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
A controller base class for temporal objects, contains a signal for notifying updates of the objects ...
void updateTemporalRange(const QgsDateTimeRange &range)
Signals that a temporal range has changed and needs to be updated in all connected objects.
void setTemporalRange(const QgsDateTimeRange &range)
Sets the temporal range for the object.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:30
Represent a 2-dimensional vector.
Definition qgsvector.h:31
double y() const
Returns the vector's y-component.
Definition qgsvector.h:153
QgsVector normalized() const
Returns the vector's normalized (or "unit") vector (ie same angle but length of 1....
Definition qgsvector.cpp:29
QgsVector perpVector() const
Returns the perpendicular vector to this vector (rotated 90 degrees counter-clockwise).
Definition qgsvector.h:161
double x() const
Returns the vector's x-component.
Definition qgsvector.h:144
On-screen 3D engine: it creates an OpenGL window (QWindow) and displays rendered 3D scenes there.
QgsTemporalRange< QDateTime > QgsDateTimeRange
QgsRange which stores a range of date times.
Definition qgsrange.h:761