QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
Loading...
Searching...
No Matches
qgsorientedbox3d.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsorientedbox3d.cpp
3 --------------------
4 begin : July 2023
5 copyright : (C) 2023 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ******************************************************************
8 ***************************************************************************/
9
10/***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 ***************************************************************************/
18
19#include "qgsorientedbox3d.h"
20
21#include "qgsbox3d.h"
23#include "qgsmatrix4x4.h"
24#include "qgsvector3d.h"
25
26#include <QQuaternion>
27
29
30QgsOrientedBox3D::QgsOrientedBox3D( const QList<double> &center, const QList<double> &halfAxes )
31{
32 if ( center.size() == 3 )
33 {
34 mCenter[0] = center.at( 0 );
35 mCenter[1] = center.at( 1 );
36 mCenter[2] = center.at( 2 );
37 }
38 if ( halfAxes.size() == 9 )
39 {
40 for ( int i = 0; i < 9; ++i )
41 {
42 mHalfAxes[i] = halfAxes.at( i );
43 }
44 }
45}
46
48{
49 mCenter[0] = center.x();
50 mCenter[1] = center.y();
51 mCenter[2] = center.z();
52 if ( halfAxes.size() == 3 )
53 {
54 for ( int i = 0; i < 3; ++i )
55 {
56 mHalfAxes[static_cast< int >( i * 3 )] = halfAxes.at( i ).x();
57 mHalfAxes[static_cast< int >( i * 3 + 1 )] = halfAxes.at( i ).y();
58 mHalfAxes[static_cast< int >( i * 3 + 2 )] = halfAxes.at( i ).z();
59 }
60 }
61}
62
63QgsOrientedBox3D::QgsOrientedBox3D( const QgsVector3D &center, const QgsVector3D &halfSizes, const QQuaternion &quaternion )
64{
65 mCenter[0] = center.x();
66 mCenter[1] = center.y();
67 mCenter[2] = center.z();
68
69 QgsVector3D v1 = QgsVector3D( quaternion.rotatedVector( QVector3D( 1, 0, 0 ) ) ) * halfSizes.x();
70 QgsVector3D v2 = QgsVector3D( quaternion.rotatedVector( QVector3D( 0, 1, 0 ) ) ) * halfSizes.y();
71 QgsVector3D v3 = QgsVector3D( quaternion.rotatedVector( QVector3D( 0, 0, 1 ) ) ) * halfSizes.z();
72 mHalfAxes[0] = v1.x();
73 mHalfAxes[1] = v1.y();
74 mHalfAxes[2] = v1.z();
75 mHalfAxes[3] = v2.x();
76 mHalfAxes[4] = v2.y();
77 mHalfAxes[5] = v2.z();
78 mHalfAxes[6] = v3.x();
79 mHalfAxes[7] = v3.y();
80 mHalfAxes[8] = v3.z();
81}
82
84{
85 return QgsOrientedBox3D( box.center(), QList< QgsVector3D > { QgsVector3D( box.width() * 0.5, 0, 0 ), QgsVector3D( 0, box.height() * 0.5, 0 ), QgsVector3D( 0, 0, box.depth() * 0.5 ) } );
86}
87
89{
90 return std::isnan( mCenter[0] ) || std::isnan( mCenter[1] ) || std::isnan( mCenter[2] );
91}
92
93QList< double > QgsOrientedBox3D::halfAxesList() const
94{
95 QList< double > res;
96 res.reserve( 9 );
97 for ( int i = 0; i < 9; ++i )
98 {
99 res.append( mHalfAxes[i] );
100 }
101 return res;
102}
103
105{
106 const double extent[3] {
107 std::fabs( mHalfAxes[0] ) + std::fabs( mHalfAxes[3] ) + std::fabs( mHalfAxes[6] ),
108 std::fabs( mHalfAxes[1] ) + std::fabs( mHalfAxes[4] ) + std::fabs( mHalfAxes[7] ),
109 std::fabs( mHalfAxes[2] ) + std::fabs( mHalfAxes[5] ) + std::fabs( mHalfAxes[8] ),
110 };
111
112 const double minX = mCenter[0] - extent[0];
113 const double maxX = mCenter[0] + extent[0];
114 const double minY = mCenter[1] - extent[1];
115 const double maxY = mCenter[1] + extent[1];
116 const double minZ = mCenter[2] - extent[2];
117 const double maxZ = mCenter[2] + extent[2];
118
119 return QgsBox3D( minX, minY, minZ, maxX, maxY, maxZ );
120}
121
122QVector<QgsVector3D> QgsOrientedBox3D::corners() const
123{
124 const QgsVector3D center( mCenter[0], mCenter[1], mCenter[2] );
125 const QgsVector3D a1( mHalfAxes[0], mHalfAxes[1], mHalfAxes[2] ), a0( -mHalfAxes[0], -mHalfAxes[1], -mHalfAxes[2] );
126 const QgsVector3D b1( mHalfAxes[3], mHalfAxes[4], mHalfAxes[5] ), b0( -mHalfAxes[3], -mHalfAxes[4], -mHalfAxes[5] );
127 const QgsVector3D c1( mHalfAxes[6], mHalfAxes[7], mHalfAxes[8] ), c0( -mHalfAxes[6], -mHalfAxes[7], -mHalfAxes[8] );
128
129 QVector<QgsVector3D> cor( 8 );
130 QgsVector3D *corData = cor.data();
131 for ( int i = 0; i < 8; ++i, ++corData )
132 {
133 const QgsVector3D aa = ( i % 2 == 0 ? a1 : a0 );
134 const QgsVector3D bb = ( ( i / 2 ) % 2 == 0 ? b1 : b0 );
135 const QgsVector3D cc = ( i / 4 == 0 ? c1 : c0 );
136 const QgsVector3D q = aa + bb + cc;
137 *corData = center + q;
138 }
139 return cor;
140}
141
143{
144 QgsVector3D axis1( mHalfAxes[0], mHalfAxes[1], mHalfAxes[2] );
145 QgsVector3D axis2( mHalfAxes[3], mHalfAxes[4], mHalfAxes[5] );
146 QgsVector3D axis3( mHalfAxes[6], mHalfAxes[7], mHalfAxes[8] );
147 return QgsVector3D( 2 * axis1.length(), 2 * axis2.length(), 2 * axis3.length() );
148}
149
151{
152 QgsVector3D axis1( mHalfAxes[0], mHalfAxes[1], mHalfAxes[2] );
153 QgsVector3D axis2( mHalfAxes[3], mHalfAxes[4], mHalfAxes[5] );
154 QgsVector3D axis3( mHalfAxes[6], mHalfAxes[7], mHalfAxes[8] );
155 return 2 * std::max( std::max( axis1.length(), axis2.length() ), axis3.length() );
156}
157
159{
160 // reproject corners to destination CRS
161 QVector<QgsVector3D> c = corners();
162 Q_ASSERT( c.count() == 8 );
163 for ( int i = 0; i < 8; ++i )
164 {
165 c[i] = ct.transform( c[i] );
166 }
167
168 // find AABB for the 8 transformed points
169 QgsVector3D v0 = c[0], v1 = c[0];
170 for ( const QgsVector3D &v : std::as_const( c ) )
171 {
172 if ( v.x() < v0.x() )
173 v0.setX( v.x() );
174 if ( v.y() < v0.y() )
175 v0.setY( v.y() );
176 if ( v.z() < v0.z() )
177 v0.setZ( v.z() );
178 if ( v.x() > v1.x() )
179 v1.setX( v.x() );
180 if ( v.y() > v1.y() )
181 v1.setY( v.y() );
182 if ( v.z() > v1.z() )
183 v1.setZ( v.z() );
184 }
185 return QgsBox3D( v0.x(), v0.y(), v0.z(), v1.x(), v1.y(), v1.z() );
186}
187
189{
190 const double *ptr = transform.constData();
191 const QgsMatrix4x4 mm( ptr[0], ptr[4], ptr[8], 0, ptr[1], ptr[5], ptr[9], 0, ptr[2], ptr[6], ptr[10], 0, 0, 0, 0, 1 );
192
193 const QgsVector3D trCenter = transform.map( QgsVector3D( mCenter[0], mCenter[1], mCenter[2] ) );
194
195 const QgsVector3D col1 = mm.map( QgsVector3D( mHalfAxes[0], mHalfAxes[1], mHalfAxes[2] ) );
196 const QgsVector3D col2 = mm.map( QgsVector3D( mHalfAxes[3], mHalfAxes[4], mHalfAxes[5] ) );
197 const QgsVector3D col3 = mm.map( QgsVector3D( mHalfAxes[6], mHalfAxes[7], mHalfAxes[8] ) );
198
199 return QgsOrientedBox3D(
200 QList<double>() << trCenter.x() << trCenter.y() << trCenter.z(), QList<double>() << col1.x() << col1.y() << col1.z() << col2.x() << col2.y() << col2.z() << col3.x() << col3.y() << col3.z()
201 );
202}
203
205{
206 // use the Separating Axis Theorem (SAT) for OBB (Oriented Bounding Box) collision detection.
207 // based off section 5 in OBBTree: A Hierarchical Structure for Rapid Interference Detection (1996)
208
209 const QgsVector3D thisCenter = center();
210 const QgsVector3D thisHalfAxis[3] { { mHalfAxes[0], mHalfAxes[1], mHalfAxes[2] }, { mHalfAxes[3], mHalfAxes[4], mHalfAxes[5] }, { mHalfAxes[6], mHalfAxes[7], mHalfAxes[8] } };
211 const QgsVector3D otherCenter = other.center();
212 const QgsVector3D otherHalfAxis
213 [3] { { other.mHalfAxes[0], other.mHalfAxes[1], other.mHalfAxes[2] }, { other.mHalfAxes[3], other.mHalfAxes[4], other.mHalfAxes[5] }, { other.mHalfAxes[6], other.mHalfAxes[7], other.mHalfAxes[8] } };
214
215 for ( int a = 0; a < 3; ++a )
216 {
217 const QgsVector3D *aAxis = thisHalfAxis + a;
218 for ( int b = 0; b < 3; ++b )
219 {
220 const QgsVector3D *bAxis = otherHalfAxis + b;
221 QgsVector3D lAxis = QgsVector3D::crossProduct( *aAxis, *bAxis );
222 if ( lAxis.isNull() )
223 continue;
224
225 lAxis.normalize();
226
227 const double tl = std::abs( QgsVector3D::dotProduct( lAxis, otherCenter ) - QgsVector3D::dotProduct( lAxis, thisCenter ) );
228 const double ra = std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[0] ) )
229 + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[1] ) )
230 + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[2] ) );
231 const double rb = std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[0] ) )
232 + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[1] ) )
233 + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[2] ) );
234 const double penetration = ( ra + rb ) - tl;
235 if ( penetration <= 0 )
236 return false;
237 }
238 }
239
240 for ( int a = 0; a < 3; ++a )
241 {
242 QgsVector3D lAxis = *( thisHalfAxis + a );
243 lAxis.normalize();
244
245 const double tl = std::abs( QgsVector3D::dotProduct( lAxis, otherCenter ) - QgsVector3D::dotProduct( lAxis, thisCenter ) );
246 const double ra = std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[0] ) )
247 + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[1] ) )
248 + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[2] ) );
249 const double rb = std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[0] ) )
250 + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[1] ) )
251 + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[2] ) );
252 const double penetration = ( ra + rb ) - tl;
253 if ( penetration <= 0 )
254 return false;
255 }
256
257 for ( int b = 0; b < 3; ++b )
258 {
259 QgsVector3D lAxis = *( otherHalfAxis + b );
260 lAxis.normalize();
261
262 const double tl = std::abs( QgsVector3D::dotProduct( lAxis, otherCenter ) - QgsVector3D::dotProduct( lAxis, thisCenter ) );
263 const double ra = std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[0] ) )
264 + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[1] ) )
265 + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[2] ) );
266 const double rb = std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[0] ) )
267 + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[1] ) )
268 + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[2] ) );
269 const double penetration = ( ra + rb ) - tl;
270 if ( penetration <= 0 )
271 return false;
272 }
273
274 return true;
275}
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:45
QgsVector3D center() const
Returns the center of the box as a vector.
Definition qgsbox3d.cpp:124
Handles coordinate transforms between two coordinate systems.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
A simple 4x4 matrix implementation useful for transformation in 3D space.
QgsVector3D map(const QgsVector3D &vector) const
Matrix-vector multiplication (vector is converted to homogeneous coordinates [X,Y,...
const double * constData() const
Returns pointer to the matrix data (stored in column-major order).
const double * halfAxes() const
Returns the half axes matrix;.
QgsBox3D extent() const
Returns the overall bounding box of the object.
double longestSide() const
Returns size of the longest side of the box.
bool isNull() const
Returns true if the box is a null box.
QgsOrientedBox3D()
Constructor for a null oriented box.
bool intersects(const QgsOrientedBox3D &other) const
Returns true if the box intersects the other box.
QVector< QgsVector3D > corners() const
Returns an array of all corners as 3D vectors.
static QgsOrientedBox3D fromBox3D(const QgsBox3D &box)
Constructs an oriented box from an axis-aligned bounding box.
QList< double > halfAxesList() const
Returns the half axes matrix;.
QgsBox3D reprojectedExtent(const QgsCoordinateTransform &ct) const
Reprojects corners of this box using the given coordinate transform and returns axis-aligned box cont...
QgsVector3D center() const
Returns the vector to the center of the box.
QgsVector3D size() const
Returns size of sides of the box.
QgsOrientedBox3D transformed(const QgsMatrix4x4 &transform) const
Returns box transformed by a 4x4 matrix.
A 3D vector (similar to QVector3D) with the difference that it uses double precision instead of singl...
Definition qgsvector3d.h:33
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:60
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:62
void setZ(double z)
Sets Z coordinate.
Definition qgsvector3d.h:80
bool isNull() const
Returns true if all three coordinates are zero.
Definition qgsvector3d.h:54
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:58
void setX(double x)
Sets X coordinate.
Definition qgsvector3d.h:68
void normalize()
Normalizes the current vector in place.
static QgsVector3D crossProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the cross product of two vectors.
void setY(double y)
Sets Y coordinate.
Definition qgsvector3d.h:74
double length() const
Returns the length of the vector.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c