QGIS API Documentation 3.99.0-Master (21b3aa880ba)
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 >
86 {
87 QgsVector3D( box.width() * 0.5, 0, 0 ),
88 QgsVector3D( 0, box.height() * 0.5, 0 ),
89 QgsVector3D( 0, 0, box.depth() * 0.5 )
90 } );
91}
92
94{
95 return std::isnan( mCenter[0] ) || std::isnan( mCenter[1] ) || std::isnan( mCenter[2] );
96}
97
98QList< double > QgsOrientedBox3D::halfAxesList() const
99{
100 QList< double > res;
101 res.reserve( 9 );
102 for ( int i = 0; i < 9; ++i )
103 {
104 res.append( mHalfAxes[i] );
105 }
106 return res;
107}
108
110{
111 const double extent[3]
112 {
113 std::fabs( mHalfAxes[0] ) + std::fabs( mHalfAxes[3] ) + std::fabs( mHalfAxes[6] ),
114 std::fabs( mHalfAxes[1] ) + std::fabs( mHalfAxes[4] ) + std::fabs( mHalfAxes[7] ),
115 std::fabs( mHalfAxes[2] ) + std::fabs( mHalfAxes[5] ) + std::fabs( mHalfAxes[8] ),
116 };
117
118 const double minX = mCenter[0] - extent[0];
119 const double maxX = mCenter[0] + extent[0];
120 const double minY = mCenter[1] - extent[1];
121 const double maxY = mCenter[1] + extent[1];
122 const double minZ = mCenter[2] - extent[2];
123 const double maxZ = mCenter[2] + extent[2];
124
125 return QgsBox3D( minX, minY, minZ, maxX, maxY, maxZ );
126}
127
128QVector<QgsVector3D> QgsOrientedBox3D::corners() const
129{
130 const QgsVector3D center( mCenter[0], mCenter[1], mCenter[2] );
131 const QgsVector3D a1( mHalfAxes[0], mHalfAxes[1], mHalfAxes[2] ), a0( -mHalfAxes[0], -mHalfAxes[1], -mHalfAxes[2] );
132 const QgsVector3D b1( mHalfAxes[3], mHalfAxes[4], mHalfAxes[5] ), b0( -mHalfAxes[3], -mHalfAxes[4], -mHalfAxes[5] );
133 const QgsVector3D c1( mHalfAxes[6], mHalfAxes[7], mHalfAxes[8] ), c0( -mHalfAxes[6], -mHalfAxes[7], -mHalfAxes[8] );
134
135 QVector<QgsVector3D> cor( 8 );
136 QgsVector3D *corData = cor.data();
137 for ( int i = 0; i < 8; ++i, ++corData )
138 {
139 const QgsVector3D aa = ( i % 2 == 0 ? a1 : a0 );
140 const QgsVector3D bb = ( ( i / 2 ) % 2 == 0 ? b1 : b0 );
141 const QgsVector3D cc = ( i / 4 == 0 ? c1 : c0 );
142 const QgsVector3D q = aa + bb + cc;
143 *corData = center + q;
144 }
145 return cor;
146}
147
149{
150 QgsVector3D axis1( mHalfAxes[0], mHalfAxes[1], mHalfAxes[2] );
151 QgsVector3D axis2( mHalfAxes[3], mHalfAxes[4], mHalfAxes[5] );
152 QgsVector3D axis3( mHalfAxes[6], mHalfAxes[7], mHalfAxes[8] );
153 return QgsVector3D( 2 * axis1.length(), 2 * axis2.length(), 2 * axis3.length() );
154}
155
157{
158 QgsVector3D axis1( mHalfAxes[0], mHalfAxes[1], mHalfAxes[2] );
159 QgsVector3D axis2( mHalfAxes[3], mHalfAxes[4], mHalfAxes[5] );
160 QgsVector3D axis3( mHalfAxes[6], mHalfAxes[7], mHalfAxes[8] );
161 return 2 * std::max( std::max( axis1.length(), axis2.length() ), axis3.length() );
162}
163
165{
166 // reproject corners to destination CRS
167 QVector<QgsVector3D> c = corners();
168 Q_ASSERT( c.count() == 8 );
169 for ( int i = 0; i < 8; ++i )
170 {
171 c[i] = ct.transform( c[i] );
172 }
173
174 // find AABB for the 8 transformed points
175 QgsVector3D v0 = c[0], v1 = c[0];
176 for ( const QgsVector3D &v : std::as_const( c ) )
177 {
178 if ( v.x() < v0.x() ) v0.setX( v.x() );
179 if ( v.y() < v0.y() ) v0.setY( v.y() );
180 if ( v.z() < v0.z() ) v0.setZ( v.z() );
181 if ( v.x() > v1.x() ) v1.setX( v.x() );
182 if ( v.y() > v1.y() ) v1.setY( v.y() );
183 if ( v.z() > v1.z() ) 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,
192 ptr[1], ptr[5], ptr[9], 0,
193 ptr[2], ptr[6], ptr[10], 0,
194 0, 0, 0, 1 );
195
196 const QgsVector3D trCenter = transform.map( QgsVector3D( mCenter[0], mCenter[1], mCenter[2] ) );
197
198 const QgsVector3D col1 = mm.map( QgsVector3D( mHalfAxes[0], mHalfAxes[1], mHalfAxes[2] ) );
199 const QgsVector3D col2 = mm.map( QgsVector3D( mHalfAxes[3], mHalfAxes[4], mHalfAxes[5] ) );
200 const QgsVector3D col3 = mm.map( QgsVector3D( mHalfAxes[6], mHalfAxes[7], mHalfAxes[8] ) );
201
202 return QgsOrientedBox3D( QList<double>() << trCenter.x() << trCenter.y() << trCenter.z(),
203 QList<double>() << col1.x() << col1.y() << col1.z()
204 << col2.x() << col2.y() << col2.z()
205 << col3.x() << col3.y() << col3.z() );
206}
207
209{
210 // use the Separating Axis Theorem (SAT) for OBB (Oriented Bounding Box) collision detection.
211 // based off section 5 in OBBTree: A Hierarchical Structure for Rapid Interference Detection (1996)
212
213 const QgsVector3D thisCenter = center();
214 const QgsVector3D thisHalfAxis[3]
215 {
216 { mHalfAxes[0], mHalfAxes[1], mHalfAxes[2] },
217 { mHalfAxes[3], mHalfAxes[4], mHalfAxes[5] },
218 { mHalfAxes[6], mHalfAxes[7], mHalfAxes[8] }
219 };
220 const QgsVector3D otherCenter = other.center();
221 const QgsVector3D otherHalfAxis[3]
222 {
223 { other.mHalfAxes[0], other.mHalfAxes[1], other.mHalfAxes[2] },
224 { other.mHalfAxes[3], other.mHalfAxes[4], other.mHalfAxes[5] },
225 { other.mHalfAxes[6], other.mHalfAxes[7], other.mHalfAxes[8] }
226 };
227
228 for ( int a = 0; a < 3; ++a )
229 {
230 const QgsVector3D *aAxis = thisHalfAxis + a;
231 for ( int b = 0; b < 3; ++b )
232 {
233 const QgsVector3D *bAxis = otherHalfAxis + b;
234 QgsVector3D lAxis = QgsVector3D::crossProduct( *aAxis, *bAxis );
235 if ( lAxis.isNull() )
236 continue;
237
238 lAxis.normalize();
239
240 const double tl = std::abs( QgsVector3D::dotProduct( lAxis, otherCenter ) - QgsVector3D::dotProduct( lAxis, thisCenter ) );
241 const double ra = std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[0] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[1] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[2] ) );
242 const double rb = std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[0] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[1] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[2] ) );
243 const double penetration = ( ra + rb ) - tl;
244 if ( penetration <= 0 )
245 return false;
246 }
247 }
248
249 for ( int a = 0; a < 3; ++a )
250 {
251 QgsVector3D lAxis = *( thisHalfAxis + a );
252 lAxis.normalize();
253
254 const double tl = std::abs( QgsVector3D::dotProduct( lAxis, otherCenter ) - QgsVector3D::dotProduct( lAxis, thisCenter ) );
255 const double ra = std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[0] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[1] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[2] ) );
256 const double rb = std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[0] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[1] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[2] ) );
257 const double penetration = ( ra + rb ) - tl;
258 if ( penetration <= 0 )
259 return false;
260 }
261
262 for ( int b = 0; b < 3; ++b )
263 {
264 QgsVector3D lAxis = *( otherHalfAxis + b );
265 lAxis.normalize();
266
267 const double tl = std::abs( QgsVector3D::dotProduct( lAxis, otherCenter ) - QgsVector3D::dotProduct( lAxis, thisCenter ) );
268 const double ra = std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[0] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[1] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, thisHalfAxis[2] ) );
269 const double rb = std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[0] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[1] ) ) + std::abs( QgsVector3D::dotProduct( lAxis, otherHalfAxis[2] ) );
270 const double penetration = ( ra + rb ) - tl;
271 if ( penetration <= 0 )
272 return false;
273 }
274
275 return true;
276}
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:42
QgsVector3D center() const
Returns the center of the box as a vector.
Definition qgsbox3d.cpp:120
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:30
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:49
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:51
void setZ(double z)
Sets Z coordinate.
Definition qgsvector3d.h:69
bool isNull() const
Returns true if all three coordinates are zero.
Definition qgsvector3d.h:44
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:47
void setX(double x)
Sets X coordinate.
Definition qgsvector3d.h:57
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:63
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