QGIS API Documentation 3.99.0-Master (d31f8633d82)
Loading...
Searching...
No Matches
qgsgeometryutils_base.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsgeometryutils_base.cpp
3 -------------------------------------------------------------------
4Date : 14 september 2023
5Copyright : (C) 2023 by Loïc Bartoletti
6email : loic dot bartoletti at oslandia 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
18#include "qgsexception.h"
19#include "qgsvector.h"
20#include "qgsvector3d.h"
21
22double QgsGeometryUtilsBase::sqrDistToLine( double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon )
23{
24 minDistX = x1;
25 minDistY = y1;
26
27 double dx = x2 - x1;
28 double dy = y2 - y1;
29
30 if ( !qgsDoubleNear( dx, 0.0 ) || !qgsDoubleNear( dy, 0.0 ) )
31 {
32 const double t = ( ( ptX - x1 ) * dx + ( ptY - y1 ) * dy ) / ( dx * dx + dy * dy );
33 if ( t > 1 )
34 {
35 minDistX = x2;
36 minDistY = y2;
37 }
38 else if ( t > 0 )
39 {
40 minDistX += dx * t;
41 minDistY += dy * t;
42 }
43 }
44
45 dx = ptX - minDistX;
46 dy = ptY - minDistY;
47
48 const double dist = dx * dx + dy * dy;
49
50 //prevent rounding errors if the point is directly on the segment
51 if ( qgsDoubleNear( dist, 0.0, epsilon ) )
52 {
53 minDistX = ptX;
54 minDistY = ptY;
55 return 0.0;
56 }
57
58 return dist;
59}
60
61int QgsGeometryUtilsBase::leftOfLine( const double x, const double y, const double x1, const double y1, const double x2, const double y2 )
62{
63 const double f1 = x - x1;
64 const double f2 = y2 - y1;
65 const double f3 = y - y1;
66 const double f4 = x2 - x1;
67 const double test = ( f1 * f2 - f3 * f4 );
68 // return -1, 0, or 1
69 return qgsDoubleNear( test, 0.0 ) ? 0 : ( test < 0 ? -1 : 1 );
70}
71
72void QgsGeometryUtilsBase::pointOnLineWithDistance( double x1, double y1, double x2, double y2, double distance, double &x, double &y, double *z1, double *z2, double *z, double *m1, double *m2, double *m )
73{
74 const double dx = x2 - x1;
75 const double dy = y2 - y1;
76 const double length = std::sqrt( dx * dx + dy * dy );
77
78 if ( qgsDoubleNear( length, 0.0 ) )
79 {
80 x = x1;
81 y = y1;
82 if ( z && z1 )
83 *z = *z1;
84 if ( m && m1 )
85 *m = *m1;
86 }
87 else
88 {
89 const double scaleFactor = distance / length;
90 x = x1 + dx * scaleFactor;
91 y = y1 + dy * scaleFactor;
92 if ( z && z1 && z2 )
93 *z = *z1 + ( *z2 - *z1 ) * scaleFactor;
94 if ( m && m1 && m2 )
95 *m = *m1 + ( *m2 - *m1 ) * scaleFactor;
96 }
97}
98
99void QgsGeometryUtilsBase::perpendicularOffsetPointAlongSegment( double x1, double y1, double x2, double y2, double proportion, double offset, double *x, double *y )
100{
101 // calculate point along segment
102 const double mX = x1 + ( x2 - x1 ) * proportion;
103 const double mY = y1 + ( y2 - y1 ) * proportion;
104 const double pX = x1 - x2;
105 const double pY = y1 - y2;
106 double normalX = -pY;
107 double normalY = pX; //#spellok
108 const double normalLength = sqrt( ( normalX * normalX ) + ( normalY * normalY ) ); //#spellok
109 normalX /= normalLength;
110 normalY /= normalLength; //#spellok
111
112 *x = mX + offset * normalX;
113 *y = mY + offset * normalY; //#spellok
114}
115
116double QgsGeometryUtilsBase::ccwAngle( double dy, double dx )
117{
118 const double angle = std::atan2( dy, dx ) * 180 / M_PI;
119 if ( angle < 0 )
120 {
121 return 360 + angle;
122 }
123 else if ( angle > 360 )
124 {
125 return 360 - angle;
126 }
127 return angle;
128}
129
130bool QgsGeometryUtilsBase::circleClockwise( double angle1, double angle2, double angle3 )
131{
132 if ( angle3 >= angle1 )
133 {
134 return !( angle2 > angle1 && angle2 < angle3 );
135 }
136 else
137 {
138 return !( angle2 > angle1 || angle2 < angle3 );
139 }
140}
141
142bool QgsGeometryUtilsBase::circleAngleBetween( double angle, double angle1, double angle2, bool clockwise )
143{
144 if ( clockwise )
145 {
146 if ( angle2 < angle1 )
147 {
148 return ( angle <= angle1 && angle >= angle2 );
149 }
150 else
151 {
152 return ( angle <= angle1 || angle >= angle2 );
153 }
154 }
155 else
156 {
157 if ( angle2 > angle1 )
158 {
159 return ( angle >= angle1 && angle <= angle2 );
160 }
161 else
162 {
163 return ( angle >= angle1 || angle <= angle2 );
164 }
165 }
166}
167
168bool QgsGeometryUtilsBase::angleOnCircle( double angle, double angle1, double angle2, double angle3 )
169{
170 const bool clockwise = circleClockwise( angle1, angle2, angle3 );
171 return circleAngleBetween( angle, angle1, angle3, clockwise );
172}
173
174void QgsGeometryUtilsBase::circleCenterRadius( double x1, double y1, double x2, double y2, double x3, double y3, double &radius, double &centerX, double &centerY )
175{
176 double dx21, dy21, dx31, dy31, h21, h31, d;
177
178 //closed circle
179 if ( qgsDoubleNear( x1, x3 ) && qgsDoubleNear( y1, y3 ) )
180 {
181 centerX = ( x1 + x2 ) / 2.0;
182 centerY = ( y1 + y2 ) / 2.0;
183 radius = std::sqrt( std::pow( centerX - x1, 2.0 ) + std::pow( centerY - y1, 2.0 ) );
184 return;
185 }
186
187 // Using Cartesian circumcenter eguations from page https://en.wikipedia.org/wiki/Circumscribed_circle
188 dx21 = x2 - x1;
189 dy21 = y2 - y1;
190 dx31 = x3 - x1;
191 dy31 = y3 - y1;
192
193 h21 = std::pow( dx21, 2.0 ) + std::pow( dy21, 2.0 );
194 h31 = std::pow( dx31, 2.0 ) + std::pow( dy31, 2.0 );
195
196 // 2*Cross product, d<0 means clockwise and d>0 counterclockwise sweeping angle
197 d = 2 * ( dx21 * dy31 - dx31 * dy21 );
198
199 // Check colinearity, Cross product = 0
200 if ( qgsDoubleNear( std::fabs( d ), 0.0, 0.00000000001 ) )
201 {
202 radius = -1.0;
203 return;
204 }
205
206 // Calculate centroid coordinates and radius
207 centerX = x1 + ( h21 * dy31 - h31 * dy21 ) / d;
208 centerY = y1 - ( h21 * dx31 - h31 * dx21 ) / d;
209 radius = std::sqrt( std::pow( centerX - x1, 2.0 ) + std::pow( centerY - y1, 2.0 ) );
210}
211
212double QgsGeometryUtilsBase::circleLength( double x1, double y1, double x2, double y2, double x3, double y3 )
213{
214 double centerX{0.0};
215 double centerY{0.0};
216 double radius{0.0};
217 circleCenterRadius( x1, y1, x2, y2, x3, y3, radius, centerX, centerY );
218 double length = M_PI / 180.0 * radius * sweepAngle( centerX, centerY, x1, y1, x2, y2, x3, y3 );
219 if ( length < 0 )
220 {
221 length = -length;
222 }
223 return length;
224}
225
226double QgsGeometryUtilsBase::calculateArcLength( double centerX, double centerY, double radius,
227 double x1, double y1, double x2, double y2,
228 double x3, double y3, int fromVertex, int toVertex )
229{
230 if ( fromVertex == toVertex )
231 return 0.0;
232
233 if ( fromVertex < 0 || fromVertex > 2 || toVertex < 0 || toVertex > 2 )
234 return 0.0;
235
236 // Calculate angles for all three points (in degrees)
237 const double angle1 = QgsGeometryUtilsBase::ccwAngle( y1 - centerY, x1 - centerX );
238 const double angle2 = QgsGeometryUtilsBase::ccwAngle( y2 - centerY, x2 - centerX );
239 const double angle3 = QgsGeometryUtilsBase::ccwAngle( y3 - centerY, x3 - centerX );
240
241 // Determine the direction of the arc using the sweep angle
242 const double totalSweepAngle = QgsGeometryUtilsBase::sweepAngle( centerX, centerY, x1, y1, x2, y2, x3, y3 );
243 bool clockwise = totalSweepAngle < 0;
244
245 // Map vertex indices to angles
246 double fromAngle, toAngle;
247 if ( fromVertex == 0 )
248 fromAngle = angle1;
249 else if ( fromVertex == 1 )
250 fromAngle = angle2;
251 else
252 fromAngle = angle3;
253
254 if ( toVertex == 0 )
255 toAngle = angle1;
256 else if ( toVertex == 1 )
257 toAngle = angle2;
258 else
259 toAngle = angle3;
260
261 // Calculate the arc angle between the two points following the arc direction (in degrees)
262 double arcAngleDegrees;
263 if ( clockwise )
264 {
265 arcAngleDegrees = fromAngle - toAngle;
266 if ( arcAngleDegrees <= 0 )
267 {
268 arcAngleDegrees += 360.0;
269 }
270 }
271 else
272 {
273 arcAngleDegrees = toAngle - fromAngle;
274 if ( arcAngleDegrees <= 0 )
275 {
276 arcAngleDegrees += 360.0;
277 }
278 }
279
280 // Make sure we follow the arc in the right direction
281 // For a 3-point arc, we should never have an angle > the total arc
282 double totalArcAngleDegrees = std::abs( totalSweepAngle );
283 if ( arcAngleDegrees > totalArcAngleDegrees && ( fromVertex == 0 && toVertex == 2 ) == false )
284 {
285 // We went the wrong way around the circle, take the shorter arc
286 arcAngleDegrees = 360.0 - arcAngleDegrees;
287 }
288
289 // Convert to radians for arc length calculation
290 const double arcAngleRadians = arcAngleDegrees * M_PI / 180.0;
291 return radius * arcAngleRadians;
292}
293
294double QgsGeometryUtilsBase::sweepAngle( double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3 )
295{
296 const double p1Angle = QgsGeometryUtilsBase::ccwAngle( y1 - centerY, x1 - centerX );
297 const double p2Angle = QgsGeometryUtilsBase::ccwAngle( y2 - centerY, x2 - centerX );
298 const double p3Angle = QgsGeometryUtilsBase::ccwAngle( y3 - centerY, x3 - centerX );
299
300 if ( p3Angle >= p1Angle )
301 {
302 if ( p2Angle > p1Angle && p2Angle < p3Angle )
303 {
304 return ( p3Angle - p1Angle );
305 }
306 else
307 {
308 return ( - ( p1Angle + ( 360 - p3Angle ) ) );
309 }
310 }
311 else
312 {
313 if ( p2Angle < p1Angle && p2Angle > p3Angle )
314 {
315 return ( -( p1Angle - p3Angle ) );
316 }
317 else
318 {
319 return ( p3Angle + ( 360 - p1Angle ) );
320 }
321 }
322}
323
324double QgsGeometryUtilsBase::interpolateArcValue( double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3 )
325{
326 /* Counter-clockwise sweep */
327 if ( a1 < a2 )
328 {
329 if ( angle <= a2 )
330 return zm1 + ( zm2 - zm1 ) * ( angle - a1 ) / ( a2 - a1 );
331 else
332 return zm2 + ( zm3 - zm2 ) * ( angle - a2 ) / ( a3 - a2 );
333 }
334 /* Clockwise sweep */
335 else
336 {
337 if ( angle >= a2 )
338 return zm1 + ( zm2 - zm1 ) * ( a1 - angle ) / ( a1 - a2 );
339 else
340 return zm2 + ( zm3 - zm2 ) * ( a2 - angle ) / ( a2 - a3 );
341 }
342}
344{
345 double clippedAngle = angle;
346 if ( clippedAngle >= M_PI * 2 || clippedAngle <= -2 * M_PI )
347 {
348 clippedAngle = std::fmod( clippedAngle, 2 * M_PI );
349 }
350 if ( clippedAngle < 0.0 )
351 {
352 clippedAngle += 2 * M_PI;
353 }
354 return clippedAngle;
355}
356
357
358int QgsGeometryUtilsBase::closestSideOfRectangle( double right, double bottom, double left, double top, double x, double y )
359{
360 // point outside rectangle
361 if ( x <= left && y <= bottom )
362 {
363 const double dx = left - x;
364 const double dy = bottom - y;
365 if ( qgsDoubleNear( dx, dy ) )
366 return 6;
367 else if ( dx < dy )
368 return 5;
369 else
370 return 7;
371 }
372 else if ( x >= right && y >= top )
373 {
374 const double dx = x - right;
375 const double dy = y - top;
376 if ( qgsDoubleNear( dx, dy ) )
377 return 2;
378 else if ( dx < dy )
379 return 1;
380 else
381 return 3;
382 }
383 else if ( x >= right && y <= bottom )
384 {
385 const double dx = x - right;
386 const double dy = bottom - y;
387 if ( qgsDoubleNear( dx, dy ) )
388 return 4;
389 else if ( dx < dy )
390 return 5;
391 else
392 return 3;
393 }
394 else if ( x <= left && y >= top )
395 {
396 const double dx = left - x;
397 const double dy = y - top;
398 if ( qgsDoubleNear( dx, dy ) )
399 return 8;
400 else if ( dx < dy )
401 return 1;
402 else
403 return 7;
404 }
405 else if ( x <= left )
406 return 7;
407 else if ( x >= right )
408 return 3;
409 else if ( y <= bottom )
410 return 5;
411 else if ( y >= top )
412 return 1;
413
414 // point is inside rectangle
415 const double smallestX = std::min( right - x, x - left );
416 const double smallestY = std::min( top - y, y - bottom );
417 if ( smallestX < smallestY )
418 {
419 // closer to left/right side
420 if ( right - x < x - left )
421 return 3; // closest to right side
422 else
423 return 7;
424 }
425 else
426 {
427 // closer to top/bottom side
428 if ( top - y < y - bottom )
429 return 1; // closest to top side
430 else
431 return 5;
432 }
433}
434
435void QgsGeometryUtilsBase::perpendicularCenterSegment( double pointx, double pointy, double segmentPoint1x, double segmentPoint1y, double segmentPoint2x, double segmentPoint2y, double &perpendicularSegmentPoint1x, double &perpendicularSegmentPoint1y, double &perpendicularSegmentPoint2x, double &perpendicularSegmentPoint2y, double desiredSegmentLength )
436{
437 QgsVector segmentVector = QgsVector( segmentPoint2x - segmentPoint1x, segmentPoint2y - segmentPoint1y );
438 QgsVector perpendicularVector = segmentVector.perpVector();
439 if ( desiredSegmentLength != 0 )
440 {
441 perpendicularVector = perpendicularVector.normalized() * ( desiredSegmentLength ) / 2;
442 }
443
444 perpendicularSegmentPoint1x = pointx - perpendicularVector.x();
445 perpendicularSegmentPoint1y = pointy - perpendicularVector.y();
446 perpendicularSegmentPoint2x = pointx + perpendicularVector.x();
447 perpendicularSegmentPoint2y = pointy + perpendicularVector.y();
448}
449
450double QgsGeometryUtilsBase::lineAngle( double x1, double y1, double x2, double y2 )
451{
452 const double at = std::atan2( y2 - y1, x2 - x1 );
453 const double a = -at + M_PI_2;
454 return normalizedAngle( a );
455}
456
457double QgsGeometryUtilsBase::angleBetweenThreePoints( double x1, double y1, double x2, double y2, double x3, double y3 )
458{
459 const double angle1 = std::atan2( y1 - y2, x1 - x2 );
460 const double angle2 = std::atan2( y3 - y2, x3 - x2 );
461 return normalizedAngle( angle1 - angle2 );
462}
463
464double QgsGeometryUtilsBase::linePerpendicularAngle( double x1, double y1, double x2, double y2 )
465{
466 double a = lineAngle( x1, y1, x2, y2 );
467 a += M_PI_2;
468 return normalizedAngle( a );
469}
470
471double QgsGeometryUtilsBase::averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 )
472{
473 // calc average angle between the previous and next point
474 const double a1 = lineAngle( x1, y1, x2, y2 );
475 const double a2 = lineAngle( x2, y2, x3, y3 );
476 return averageAngle( a1, a2 );
477}
478
479double QgsGeometryUtilsBase::averageAngle( double a1, double a2 )
480{
481 a1 = normalizedAngle( a1 );
482 a2 = normalizedAngle( a2 );
483 double clockwiseDiff = 0.0;
484 if ( a2 >= a1 )
485 {
486 clockwiseDiff = a2 - a1;
487 }
488 else
489 {
490 clockwiseDiff = a2 + ( 2 * M_PI - a1 );
491 }
492 const double counterClockwiseDiff = 2 * M_PI - clockwiseDiff;
493
494 double resultAngle = 0;
495 if ( clockwiseDiff <= counterClockwiseDiff )
496 {
497 resultAngle = a1 + clockwiseDiff / 2.0;
498 }
499 else
500 {
501 resultAngle = a1 - counterClockwiseDiff / 2.0;
502 }
503 return normalizedAngle( resultAngle );
504}
505
507 const QgsVector3D &P2, const QgsVector3D &P22 )
508{
509 const QgsVector3D u1 = P12 - P1;
510 const QgsVector3D u2 = P22 - P2;
512 if ( u3.length() == 0 ) return 1;
513 u3.normalize();
514 const QgsVector3D dir = P1 - P2;
515 return std::fabs( ( QgsVector3D::dotProduct( dir, u3 ) ) ); // u3 is already normalized
516}
517
519 const QgsVector3D &P2, const QgsVector3D &P22,
520 QgsVector3D &X1, double epsilon )
521{
522 const QgsVector3D d = P2 - P1;
523 QgsVector3D u1 = P12 - P1;
524 u1.normalize();
525 QgsVector3D u2 = P22 - P2;
526 u2.normalize();
527 const QgsVector3D u3 = QgsVector3D::crossProduct( u1, u2 );
528
529 if ( std::fabs( u3.x() ) <= epsilon &&
530 std::fabs( u3.y() ) <= epsilon &&
531 std::fabs( u3.z() ) <= epsilon )
532 {
533 // The rays are almost parallel.
534 return false;
535 }
536
537 // X1 and X2 are the closest points on lines
538 // we want to find X1 (lies on u1)
539 // solving the linear equation in r1 and r2: Xi = Pi + ri*ui
540 // we are only interested in X1 so we only solve for r1.
541 const double a1 = QgsVector3D::dotProduct( u1, u1 ), b1 = QgsVector3D::dotProduct( u1, u2 ), c1 = QgsVector3D::dotProduct( u1, d );
542 const double a2 = QgsVector3D::dotProduct( u1, u2 ), b2 = QgsVector3D::dotProduct( u2, u2 ), c2 = QgsVector3D::dotProduct( u2, d );
543 if ( !( std::fabs( b1 ) > epsilon ) )
544 {
545 // Denominator is close to zero.
546 return false;
547 }
548 if ( !( a2 != -1 && a2 != 1 ) )
549 {
550 // Lines are parallel
551 return false;
552 }
553
554 const double r1 = ( c2 - b2 * c1 / b1 ) / ( a2 - b2 * a1 / b1 );
555 X1 = P1 + u1 * r1;
556
557 return true;
558}
559
560bool QgsGeometryUtilsBase::lineIntersection( double p1x, double p1y, QgsVector v1, double p2x, double p2y, QgsVector v2, double &intersectionX, double &intersectionY )
561{
562 const double d = v1.y() * v2.x() - v1.x() * v2.y();
563
564 if ( qgsDoubleNear( d, 0 ) )
565 return false;
566
567 const double dx = p2x - p1x;
568 const double dy = p2y - p1y;
569 const double k = ( dy * v2.x() - dx * v2.y() ) / d;
570
571 intersectionX = p1x + v1.x() * k;
572 intersectionY = p1y + v1.y() * k;
573
574 return true;
575}
576
577bool QgsGeometryUtilsBase::intersectionPointOfLinesByBearing( double x1, double y1, double bearing1, double x2, double y2, double bearing2, double &intersectionX, double &intersectionY )
578{
579 // Convert bearings (clockwise from north) to direction vectors
580 // For bearing β: direction = (sin(β), cos(β))
581 const QgsVector v1( std::sin( bearing1 ), std::cos( bearing1 ) );
582 const QgsVector v2( std::sin( bearing2 ), std::cos( bearing2 ) );
583
584 return lineIntersection( x1, y1, v1, x2, y2, v2, intersectionX, intersectionY );
585}
586
587static bool equals( double p1x, double p1y, double p2x, double p2y, double epsilon = 1e-8 )
588{
589 return qgsDoubleNear( p1x, p2x, epsilon ) && qgsDoubleNear( p1y, p2y, epsilon );
590}
591
592bool QgsGeometryUtilsBase::segmentIntersection( double p1x, double p1y, double p2x, double p2y, double q1x, double q1y, double q2x, double q2y, double &intersectionPointX, double &intersectionPointY, bool &isIntersection, double tolerance, bool acceptImproperIntersection )
593{
594 isIntersection = false;
595 intersectionPointX = intersectionPointY = std::numeric_limits<double>::quiet_NaN();
596
597 QgsVector v( p2x - p1x, p2y - p1y );
598 QgsVector w( q2x - q1x, q2y - q1y );
599 const double vl = v.length();
600 const double wl = w.length();
601
602 if ( qgsDoubleNear( vl, 0.0, tolerance ) || qgsDoubleNear( wl, 0.0, tolerance ) )
603 {
604 return false;
605 }
606 v = v / vl;
607 w = w / wl;
608
609 if ( !lineIntersection( p1x, p1y, v, q1x, q1y, w, intersectionPointX, intersectionPointY ) )
610 {
611 return false;
612 }
613
614 isIntersection = true;
615 if ( acceptImproperIntersection )
616 {
617 if ( ( equals( p1x, p1y, q1x, q1y ) ) || ( equals( p1x, p1y, q2x, q2y ) ) )
618 {
619 intersectionPointX = p1x;
620 intersectionPointY = p1y;
621 return true;
622 }
623 else if ( ( equals( p1x, p1y, q2x, q2y ) ) || ( equals( p2x, p2y, q2x, q2y ) ) )
624 {
625 intersectionPointX = p2x;
626 intersectionPointY = p2y;
627 return true;
628 }
629
630 double x, y;
631 if (
632 // intersectionPoint = p1
633 qgsDoubleNear( sqrDistToLine( p1x, p1y, q1x, q1y, q2x, q2y, x, y, tolerance ), 0.0, tolerance ) ||
634 // intersectionPoint = p2
635 qgsDoubleNear( sqrDistToLine( p2x, p2y, q1x, q1y, q2x, q2y, x, y, tolerance ), 0.0, tolerance ) ||
636 // intersectionPoint = q1
637 qgsDoubleNear( sqrDistToLine( q1x, q1y, p1x, p1y, p2x, p2y, x, y, tolerance ), 0.0, tolerance ) ||
638 // intersectionPoint = q2
639 qgsDoubleNear( sqrDistToLine( q2x, q2y, p1x, p1y, p2x, p2y, x, y, tolerance ), 0.0, tolerance )
640 )
641 {
642 return true;
643 }
644 }
645
646 const double lambdav = QgsVector( intersectionPointX - p1x, intersectionPointY - p1y ) * v;
647 if ( lambdav < 0. + tolerance || lambdav > vl - tolerance )
648 return false;
649
650 const double lambdaw = QgsVector( intersectionPointX - q1x, intersectionPointY - q1y ) * w;
651 return !( lambdaw < 0. + tolerance || lambdaw >= wl - tolerance );
652
653}
654
655
657 const QgsVector3D &Lb1, const QgsVector3D &Lb2,
658 QgsVector3D &intersection )
659{
660
661 // if all Vector are on the same plane (have the same Z), use the 2D intersection
662 // else return a false result
663 if ( qgsDoubleNear( La1.z(), La2.z() ) && qgsDoubleNear( La1.z(), Lb1.z() ) && qgsDoubleNear( La1.z(), Lb2.z() ) )
664 {
665 double ptInterX = 0.0, ptInterY = 0.0;
666 bool isIntersection = false;
667 segmentIntersection( La1.x(), La1.y(),
668 La2.x(), La2.y(),
669 Lb1.x(), Lb1.y(),
670 Lb2.x(), Lb2.y(),
671 ptInterX, ptInterY,
672 isIntersection,
673 1e-8,
674 true );
675 intersection.set( ptInterX, ptInterY, La1.z() );
676 return true;
677 }
678
679 // first check if lines have an exact intersection point
680 // do it by checking if the shortest distance is exactly 0
681 const double distance = skewLinesDistance( La1, La2, Lb1, Lb2 );
682 if ( qgsDoubleNear( distance, 0.0 ) )
683 {
684 // 3d lines have exact intersection point.
685 const QgsVector3D C = La2;
686 const QgsVector3D D = Lb2;
687 const QgsVector3D e = La1 - La2;
688 const QgsVector3D f = Lb1 - Lb2;
689 const QgsVector3D g = D - C;
690 if ( qgsDoubleNear( ( QgsVector3D::crossProduct( f, g ) ).length(), 0.0 ) || qgsDoubleNear( ( QgsVector3D::crossProduct( f, e ) ).length(), 0.0 ) )
691 {
692 // Lines have no intersection, are they parallel?
693 return false;
694 }
695
697 fgn.normalize();
698
700 fen.normalize();
701
702 int di = -1;
703 if ( fgn == fen ) // same direction?
704 di *= -1;
705
706 intersection = C + e * di * ( QgsVector3D::crossProduct( f, g ).length() / QgsVector3D::crossProduct( f, e ).length() );
707 return true;
708 }
709
710 // try to calculate the approximate intersection point
711 QgsVector3D X1, X2;
712 const bool firstIsDone = skewLinesProjection( La1, La2, Lb1, Lb2, X1 );
713 const bool secondIsDone = skewLinesProjection( Lb1, Lb2, La1, La2, X2 );
714
715 if ( !firstIsDone || !secondIsDone )
716 {
717 // Could not obtain projection point.
718 return false;
719 }
720
721 intersection = ( X1 + X2 ) / 2.0;
722 return true;
723}
724
725double QgsGeometryUtilsBase::triangleArea( double aX, double aY, double bX, double bY, double cX, double cY )
726{
727 return 0.5 * std::abs( ( aX - cX ) * ( bY - aY ) - ( aX - bX ) * ( cY - aY ) );
728}
729
730double QgsGeometryUtilsBase::pointFractionAlongLine( double x1, double y1, double x2, double y2, double px, double py )
731{
732 const double dxp = px - x1;
733 const double dyp = py - y1;
734
735 const double dxl = x2 - x1;
736 const double dyl = y2 - y1;
737
738 return std::sqrt( ( dxp * dxp ) + ( dyp * dyp ) ) / std::sqrt( ( dxl * dxl ) + ( dyl * dyl ) );
739}
740
741void QgsGeometryUtilsBase::weightedPointInTriangle( const double aX, const double aY, const double bX, const double bY, const double cX, const double cY,
742 double weightB, double weightC, double &pointX, double &pointY )
743{
744 // if point will be outside of the triangle, invert weights
745 if ( weightB + weightC > 1 )
746 {
747 weightB = 1 - weightB;
748 weightC = 1 - weightC;
749 }
750
751 const double rBx = weightB * ( bX - aX );
752 const double rBy = weightB * ( bY - aY );
753 const double rCx = weightC * ( cX - aX );
754 const double rCy = weightC * ( cY - aY );
755
756 pointX = rBx + rCx + aX;
757 pointY = rBy + rCy + aY;
758}
759
760bool QgsGeometryUtilsBase::pointsAreCollinear( double x1, double y1, double x2, double y2, double x3, double y3, double epsilon )
761{
762 return qgsDoubleNear( x1 * ( y2 - y3 ) + x2 * ( y3 - y1 ) + x3 * ( y1 - y2 ), 0, epsilon );
763};
764
765bool QgsGeometryUtilsBase::points3DAreCollinear( double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, double z3, double epsilon )
766{
767 // crossproduct
768 const double cx = ( y2 - y1 ) * ( z3 - z1 ) - ( z2 - z1 ) * ( y3 - y1 );
769 const double cy = ( z2 - z1 ) * ( x3 - x1 ) - ( x2 - x1 ) * ( z3 - z1 );
770 const double cz = ( x2 - x1 ) * ( y3 - y1 ) - ( y2 - y1 ) * ( x3 - x1 );
771
772 // The magnitude of the cross product must be close to 0
773 return qgsDoubleNear( cx * cx + cy * cy + cz * cz, 0.0, epsilon * epsilon );
774}
775
776double QgsGeometryUtilsBase::azimuth( double x1, double y1, double x2, double y2 )
777{
778 const double dx = x2 - x1;
779 const double dy = y2 - y1;
780 return ( std::atan2( dx, dy ) * 180.0 / M_PI );
781}
782
783bool QgsGeometryUtilsBase::angleBisector( double aX, double aY, double bX, double bY, double cX, double cY, double dX, double dY,
784 double &pointX, double &pointY, double &angle )
785{
786 angle = ( azimuth( aX, aY, bX, bY ) + azimuth( cX, cY, dX, dY ) ) / 2.0;
787
788 bool intersection = false;
789 QgsGeometryUtilsBase::segmentIntersection( aX, aY, bX, bY, cX, cY, dX, dY, pointX, pointY, intersection );
790
791 return intersection;
792}
793
794void QgsGeometryUtilsBase::project( double aX, double aY, double aZ, double distance, double azimuth, double inclination, double &resultX, double &resultY, double &resultZ )
795{
796 const double radsXy = azimuth * M_PI / 180.0;
797 double dx = 0.0, dy = 0.0, dz = 0.0;
798
799 inclination = std::fmod( inclination, 360.0 );
800
801 if ( std::isnan( aZ ) && qgsDoubleNear( inclination, 90.0 ) )
802 {
803 dx = distance * std::sin( radsXy );
804 dy = distance * std::cos( radsXy );
805 }
806 else
807 {
808 const double radsZ = inclination * M_PI / 180.0;
809 dx = distance * std::sin( radsZ ) * std::sin( radsXy );
810 dy = distance * std::sin( radsZ ) * std::cos( radsXy );
811 dz = distance * std::cos( radsZ );
812 }
813
814 resultX = aX + dx;
815 resultY = aY + dy;
816 resultZ = aZ + dz;
817}
818
819bool QgsGeometryUtilsBase::bisector( double aX, double aY, double bX, double bY, double cX, double cY,
820 double &pointX, double &pointY )
821{
822 const double angle = ( azimuth( aX, aY, bX, bY ) + azimuth( aX, aY, cX, cY ) ) / 2.0;
823
824 bool intersection = false;
825 double dX = 0.0, dY = 0.0, dZ = 0.0;
826 project( aX, aY, std::numeric_limits<double>::quiet_NaN(), 1.0, angle, 90.0, dX, dY, dZ );
827 segmentIntersection( bX, bY, cX, cY, aX, aY, dX, dY, pointX, pointY, intersection );
828
829 return intersection;
830}
831
832
833double QgsGeometryUtilsBase::maximumFilletRadius( const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY,
834 const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY,
835 double epsilon )
836{
837 double intersectionX, intersectionY;
838 bool isIntersection;
840 segment1StartX, segment1StartY, segment1EndX, segment1EndY,
841 segment2StartX, segment2StartY, segment2EndX, segment2EndY,
842 intersectionX, intersectionY, isIntersection, epsilon, true );
843
844 if ( !isIntersection )
845 {
846 return -1.0;
847 }
848
849 const double dist1ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1StartX, segment1StartY );
850 const double dist1ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1EndX, segment1EndY );
851 const double dist2ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2StartX, segment2StartY );
852 const double dist2ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2EndX, segment2EndY );
853
854 const double dir1X = dist1ToStart > epsilon ? segment1StartX : segment1EndX;
855 const double dir1Y = dist1ToStart > epsilon ? segment1StartY : segment1EndY;
856 const double dir2X = dist2ToStart > epsilon ? segment2StartX : segment2EndX;
857 const double dir2Y = dist2ToStart > epsilon ? segment2StartY : segment2EndY;
858
860 dir1X, dir1Y,
861 intersectionX, intersectionY,
862 dir2X, dir2Y
863 );
864
865 if ( std::abs( angle ) < epsilon || std::abs( angle - M_PI ) < epsilon )
866 {
867 return -1.0;
868 }
869
870 double workingAngle = angle;
871 if ( workingAngle > M_PI )
872 {
873 workingAngle = 2 * M_PI - workingAngle;
874 }
875
876 const double halfAngle = workingAngle / 2.0;
877 if ( std::abs( std::sin( halfAngle ) ) < epsilon )
878 {
879 return -1.0;
880 }
881
882 const double maxDist1 = ( dist1ToStart > epsilon ) ? dist1ToStart : dist1ToEnd;
883 const double maxDist2 = ( dist2ToStart > epsilon ) ? dist2ToStart : dist2ToEnd;
884
885 const double seg1Length = QgsGeometryUtilsBase::distance2D( segment1StartX, segment1StartY, segment1EndX, segment1EndY );
886 const double seg2Length = QgsGeometryUtilsBase::distance2D( segment2StartX, segment2StartY, segment2EndX, segment2EndY );
887
888 const bool intersectionOnSeg1 = std::abs( ( dist1ToStart + dist1ToEnd ) - seg1Length ) < epsilon;
889 const bool intersectionOnSeg2 = std::abs( ( dist2ToStart + dist2ToEnd ) - seg2Length ) < epsilon;
890
891 double maxDistanceToTangent = std::numeric_limits<double>::max();
892
893 if ( intersectionOnSeg1 )
894 {
895 maxDistanceToTangent = std::min( maxDistanceToTangent, maxDist1 - epsilon );
896 }
897
898 if ( intersectionOnSeg2 )
899 {
900 maxDistanceToTangent = std::min( maxDistanceToTangent, maxDist2 - epsilon );
901 }
902
903 if ( maxDistanceToTangent == std::numeric_limits<double>::max() )
904 {
905 maxDistanceToTangent = std::min( maxDist1, maxDist2 ) - epsilon;
906 }
907
908 if ( maxDistanceToTangent <= 0 )
909 {
910 return -1.0;
911 }
912
913 return maxDistanceToTangent * std::tan( halfAngle );
914}
915
917 const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY,
918 const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY,
919 const double radius,
920 double *filletPointsX, double *filletPointsY,
921 double *trim1StartX, double *trim1StartY,
922 double *trim1EndX, double *trim1EndY,
923 double *trim2StartX, double *trim2StartY,
924 double *trim2EndX, double *trim2EndY,
925 const double epsilon )
926{
927 if ( radius <= 0 )
928 throw QgsInvalidArgumentException( "Radius must be greater than 0." );
929
930 // Find intersection point between segments (or their infinite line extensions)
931 double intersectionX, intersectionY;
932 bool isIntersection;
934 segment1StartX, segment1StartY, segment1EndX, segment1EndY,
935 segment2StartX, segment2StartY, segment2EndX, segment2EndY,
936 intersectionX, intersectionY, isIntersection, epsilon, true );
937
938 if ( !isIntersection )
939 {
940 throw QgsInvalidArgumentException( "Segments do not intersect." );
941 }
942
943 // Calculate distances from intersection to all segment endpoints
944 const double dist1ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1StartX, segment1StartY );
945 const double dist1ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1EndX, segment1EndY );
946 const double dist2ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2StartX, segment2StartY );
947 const double dist2ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2EndX, segment2EndY );
948
949 // Determine directional points for angle calculation
950 // These points define the rays extending from the intersection
951 const double dir1X = dist1ToStart > epsilon ? segment1StartX : segment1EndX;
952 const double dir1Y = dist1ToStart > epsilon ? segment1StartY : segment1EndY;
953 const double dir2X = dist2ToStart > epsilon ? segment2StartX : segment2EndX;
954 const double dir2Y = dist2ToStart > epsilon ? segment2StartY : segment2EndY;
955
956 // Calculate the angle between the two rays
958 dir1X, dir1Y,
959 intersectionX, intersectionY,
960 dir2X, dir2Y
961 );
962
963 // Validate angle - must be meaningful for fillet creation
964 if ( std::abs( angle ) < epsilon || std::abs( angle - M_PI ) < epsilon )
965 {
966 throw QgsInvalidArgumentException( "Parallel or anti-parallel segments." );
967 }
968
969 // Use the interior angle (always ≤ π) for fillet calculations
970 double workingAngle = angle;
971 if ( workingAngle > M_PI )
972 {
973 workingAngle = 2 * M_PI - workingAngle;
974 }
975
976 const double halfAngle = workingAngle / 2.0;
977 if ( std::abs( std::sin( halfAngle ) ) < epsilon )
978 {
979 // Avoid division by very small numbers.
980 throw QgsInvalidArgumentException( "Segment angle near 0 will generate wrong division" );
981 }
982
983 // Calculate distance from intersection to tangent points using trigonometry
984 const double distanceToTangent = radius / std::tan( halfAngle );
985
986 const double maxDist1 = ( dist1ToStart > epsilon ) ? dist1ToStart : dist1ToEnd;
987 const double maxDist2 = ( dist2ToStart > epsilon ) ? dist2ToStart : dist2ToEnd;
988
989 // Check if intersection is actually on the segments (not just on infinite lines)
990 const double seg1Length = QgsGeometryUtilsBase::distance2D( segment1StartX, segment1StartY, segment1EndX, segment1EndY );
991 const double seg2Length = QgsGeometryUtilsBase::distance2D( segment2StartX, segment2StartY, segment2EndX, segment2EndY );
992
993 const bool intersectionOnSeg1 = std::abs( ( dist1ToStart + dist1ToEnd ) - seg1Length ) < epsilon;
994 const bool intersectionOnSeg2 = std::abs( ( dist2ToStart + dist2ToEnd ) - seg2Length ) < epsilon;
995
996 // Only enforce distance limits if intersection is actually on the segment
997 // This allows fillets on non-touching segments (like chamfer behavior)
998 if ( intersectionOnSeg1 && distanceToTangent > maxDist1 - epsilon )
999 {
1000 throw QgsInvalidArgumentException( "Intersection 1 on segment but too far." );
1001 }
1002
1003 if ( intersectionOnSeg2 && distanceToTangent > maxDist2 - epsilon )
1004 {
1005 throw QgsInvalidArgumentException( "Intersection 2 on segment but too far." );
1006 }
1007
1008 // Calculate tangent points along the rays
1009 double T1x, T1y, T2x, T2y;
1011 intersectionX, intersectionY,
1012 dir1X, dir1Y,
1013 distanceToTangent, T1x, T1y
1014 );
1016 intersectionX, intersectionY,
1017 dir2X, dir2Y,
1018 distanceToTangent, T2x, T2y
1019 );
1020
1021 // Calculate circle center using angle bisector geometry
1022 const QgsVector v1( dir1X - intersectionX, dir1Y - intersectionY );
1023 const QgsVector v2( dir2X - intersectionX, dir2Y - intersectionY );
1024
1025 // The bisector direction is the normalized sum of the unit vectors
1026 const QgsVector bisectorDirection = ( v1.normalized() + v2.normalized() ).normalized();
1027
1028 // Distance from intersection to circle center
1029 const double centerDistance = radius / std::sin( halfAngle );
1030 const double centerX = intersectionX + bisectorDirection.x() * centerDistance;
1031 const double centerY = intersectionY + bisectorDirection.y() * centerDistance;
1032
1033 // Calculate arc midpoint - the point on the circle that bisects the arc
1034 const QgsVector centerToT1 = QgsVector( T1x - centerX, T1y - centerY ).normalized();
1035 const QgsVector centerToT2 = QgsVector( T2x - centerX, T2y - centerY ).normalized();
1036 const QgsVector midDirection = ( centerToT1 + centerToT2 ).normalized();
1037
1038 const double midX = centerX + midDirection.x() * radius;
1039 const double midY = centerY + midDirection.y() * radius;
1040
1041 // Return three-point arc representation: tangent1, midpoint, tangent2
1042 filletPointsX[0] = T1x;
1043 filletPointsY[0] = T1y;
1044 filletPointsX[1] = midX;
1045 filletPointsY[1] = midY;
1046 filletPointsX[2] = T2x;
1047 filletPointsY[2] = T2y;
1048
1049 // Generate trimmed segments if requested
1050 if ( trim1StartX )
1051 {
1052 if ( dist1ToStart > epsilon )
1053 {
1054 *trim1StartX = segment1StartX;
1055 *trim1StartY = segment1StartY;
1056 }
1057 else
1058 {
1059 *trim1StartX = segment1EndX;
1060 *trim1StartY = segment1EndY;
1061 }
1062 *trim1EndX = T1x;
1063 *trim1EndY = T1y;
1064 }
1065 if ( trim2StartX )
1066 {
1067 if ( dist2ToStart > epsilon )
1068 {
1069 *trim2StartX = segment2StartX;
1070 *trim2StartY = segment2StartY;
1071 }
1072 else
1073 {
1074 *trim2StartX = segment2EndX;
1075 *trim2StartY = segment2EndY;
1076 }
1077 *trim2EndX = T2x;
1078 *trim2EndY = T2y;
1079 }
1080
1081 return true;
1082}
1083
1085 const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY,
1086 const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY,
1087 double distance1, double distance2,
1088 double &chamferStartX, double &chamferStartY,
1089 double &chamferEndX, double &chamferEndY,
1090 double *trim1StartX, double *trim1StartY,
1091 double *trim1EndX, double *trim1EndY,
1092 double *trim2StartX, double *trim2StartY,
1093 double *trim2EndX, double *trim2EndY,
1094 const double epsilon )
1095{
1096 // Apply symmetric distance if distance2 is negative
1097 if ( distance2 <= 0 )
1098 distance2 = distance1;
1099
1100 // Only for positive distance
1101 if ( distance1 <= 0 || distance2 <= 0 )
1102 throw QgsInvalidArgumentException( "Negative distances." );
1103
1104 // Find intersection point between segments (or their infinite line extensions)
1105 double intersectionX, intersectionY;
1106 bool isIntersection;
1108 segment1StartX, segment1StartY, segment1EndX, segment1EndY,
1109 segment2StartX, segment2StartY, segment2EndX, segment2EndY,
1110 intersectionX, intersectionY, isIntersection, epsilon, true );
1111
1112 if ( !isIntersection )
1113 {
1114 throw QgsInvalidArgumentException( "Segments do not intersect." );
1115 }
1116
1117 // Apply symmetric distance if distance2 is negative
1118 if ( distance2 < 0 )
1119 distance2 = distance1;
1120
1121 // Calculate distances from intersection to all segment endpoints
1122 const double dist1ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1StartX, segment1StartY );
1123 const double dist1ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1EndX, segment1EndY );
1124 const double dist2ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2StartX, segment2StartY );
1125 const double dist2ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2EndX, segment2EndY );
1126
1127 // Calculate chamfer points along each segment
1128 // Choose the endpoint farthest from intersection as direction
1129 double T1x, T1y;
1130 if ( dist1ToStart > epsilon )
1131 {
1133 intersectionX, intersectionY,
1134 segment1StartX, segment1StartY,
1135 distance1, T1x, T1y
1136 );
1137 }
1138 else
1139 {
1141 intersectionX, intersectionY,
1142 segment1EndX, segment1EndY,
1143 distance1, T1x, T1y
1144 );
1145 }
1146
1147 double T2x, T2y;
1148 if ( dist2ToStart > epsilon )
1149 {
1151 intersectionX, intersectionY,
1152 segment2StartX, segment2StartY,
1153 distance2, T2x, T2y
1154 );
1155 }
1156 else
1157 {
1159 intersectionX, intersectionY,
1160 segment2EndX, segment2EndY,
1161 distance2, T2x, T2y
1162 );
1163 }
1164
1165 // Clamp distances to available segment length if necessary
1166 const double distToSeg1Target = ( dist1ToStart > epsilon ) ? dist1ToStart : dist1ToEnd;
1167 const double distToSeg2Target = ( dist2ToStart > epsilon ) ? dist2ToStart : dist2ToEnd;
1168
1169 if ( distance1 > distToSeg1Target - epsilon )
1170 {
1171 if ( dist1ToStart > epsilon )
1172 {
1174 intersectionX, intersectionY,
1175 segment1StartX, segment1StartY,
1176 distToSeg1Target, T1x, T1y
1177 );
1178 }
1179 else
1180 {
1182 intersectionX, intersectionY,
1183 segment1EndX, segment1EndY,
1184 distToSeg1Target, T1x, T1y
1185 );
1186 }
1187 }
1188
1189 if ( distance2 > distToSeg2Target - epsilon )
1190 {
1191 if ( dist2ToStart > epsilon )
1192 {
1194 intersectionX, intersectionY,
1195 segment2StartX, segment2StartY,
1196 distToSeg2Target, T2x, T2y
1197 );
1198 }
1199 else
1200 {
1202 intersectionX, intersectionY,
1203 segment2EndX, segment2EndY,
1204 distToSeg2Target, T2x, T2y
1205 );
1206 }
1207 }
1208
1209 chamferStartX = T1x;
1210 chamferStartY = T1y;
1211 chamferEndX = T2x;
1212 chamferEndY = T2y;
1213
1214 // Generate trimmed segments if requested
1215 if ( trim1StartX )
1216 {
1217 if ( dist1ToStart > epsilon )
1218 {
1219 *trim1StartX = segment1StartX; *trim1StartY = segment1StartY;
1220 *trim1EndX = T1x; *trim1EndY = T1y;
1221 }
1222 else
1223 {
1224 *trim1StartX = segment1EndX; *trim1StartY = segment1EndY;
1225 *trim1EndX = T1x; *trim1EndY = T1y;
1226 }
1227 }
1228 if ( trim2StartX )
1229 {
1230 if ( dist2ToStart > epsilon )
1231 {
1232 *trim2StartX = segment2StartX; *trim2StartY = segment2StartY;
1233 *trim2EndX = T2x; *trim2EndY = T2y;
1234 }
1235 else
1236 {
1237 *trim2StartX = segment2EndX; *trim2StartY = segment2EndY;
1238 *trim2EndX = T2x; *trim2EndY = T2y;
1239 }
1240 }
1241
1242 return true;
1243}
static double circleLength(double x1, double y1, double x2, double y2, double x3, double y3)
Length of a circular string segment defined by pt1, pt2, pt3.
static double calculateArcLength(double centerX, double centerY, double radius, double x1, double y1, double x2, double y2, double x3, double y3, int fromVertex, int toVertex)
Calculates the precise arc length between two vertices on a circular arc.
static void pointOnLineWithDistance(double x1, double y1, double x2, double y2, double distance, double &x, double &y, double *z1=nullptr, double *z2=nullptr, double *z=nullptr, double *m1=nullptr, double *m2=nullptr, double *m=nullptr)
Calculates the point a specified distance from (x1, y1) toward a second point (x2,...
static double angleBetweenThreePoints(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the angle between the lines AB and BC, where AB and BC described by points a,...
static double maximumFilletRadius(const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY, const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY, double epsilon=1e-8)
Calculates the maximum allowed fillet radius for the given segment configuration.
static double ccwAngle(double dy, double dx)
Returns the counter clockwise angle between a line with components dx, dy and the line with dx > 0 an...
static bool circleClockwise(double angle1, double angle2, double angle3)
Returns true if the circle defined by three angles is ordered clockwise.
static double distance2D(double x1, double y1, double x2, double y2)
Returns the 2D distance between (x1, y1) and (x2, y2).
static double sweepAngle(double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3)
Calculates angle of a circular string part defined by pt1, pt2, pt3.
static bool createChamfer(const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY, const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY, const double distance1, const double distance2, double &chamferStartX, double &chamferStartY, double &chamferEndX, double &chamferEndY, double *trim1StartX=nullptr, double *trim1StartY=nullptr, double *trim1EndX=nullptr, double *trim1EndY=nullptr, double *trim2StartX=nullptr, double *trim2StartY=nullptr, double *trim2EndX=nullptr, double *trim2EndY=nullptr, const double epsilon=1e-8)
Creates a chamfer (angled corner) between two line segments.
static double linePerpendicularAngle(double x1, double y1, double x2, double y2)
Calculates the perpendicular angle to a line joining two points.
static bool bisector(double aX, double aY, double bX, double bY, double cX, double cY, double &pointX, double &pointY)
Returns the point (pointX, pointY) forming the bisector from point (aX, aY) to the segment (bX,...
static double lineAngle(double x1, double y1, double x2, double y2)
Calculates the direction of line joining two points in radians, clockwise from the north direction.
static double averageAngle(double x1, double y1, double x2, double y2, double x3, double y3)
Calculates the average angle (in radians) between the two linear segments from (x1,...
static bool skewLinesProjection(const QgsVector3D &P1, const QgsVector3D &P12, const QgsVector3D &P2, const QgsVector3D &P22, QgsVector3D &X1, double epsilon=0.0001)
A method to project one skew line onto another.
static bool points3DAreCollinear(double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, double z3, double epsilon)
Given the points (x1, y1, z1), (x2, y2, z2) and (x3, y3, z3) returns true if these points can be cons...
static int closestSideOfRectangle(double right, double bottom, double left, double top, double x, double y)
Returns a number representing the closest side of a rectangle defined by /a right,...
static void project(double aX, double aY, double aZ, double distance, double azimuth, double inclination, double &resultX, double &resultY, double &resultZ)
Returns coordinates of a point which corresponds to this point projected by a specified distance with...
static double interpolateArcValue(double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3)
Interpolate a value at given angle on circular arc given values (zm1, zm2, zm3) at three different an...
static void perpendicularOffsetPointAlongSegment(double x1, double y1, double x2, double y2, double proportion, double offset, double *x, double *y)
Calculates a point a certain proportion of the way along the segment from (x1, y1) to (x2,...
static double skewLinesDistance(const QgsVector3D &P1, const QgsVector3D &P12, const QgsVector3D &P2, const QgsVector3D &P22)
An algorithm to calculate the shortest distance between two skew lines.
static double pointFractionAlongLine(double x1, double y1, double x2, double y2, double px, double py)
Given the line (x1, y1) to (x2, y2) and a point (px, py) returns the fraction of the line length at w...
static double normalizedAngle(double angle)
Ensures that an angle is in the range 0 <= angle < 2 pi.
static void weightedPointInTriangle(double aX, double aY, double bX, double bY, double cX, double cY, double weightB, double weightC, double &pointX, double &pointY)
Returns a weighted point inside the triangle denoted by the points (aX, aY), (bX, bY) and (cX,...
static void perpendicularCenterSegment(double centerPointX, double centerPointY, double segmentPoint1x, double segmentPoint1y, double segmentPoint2x, double segmentPoint2y, double &perpendicularSegmentPoint1x, double &perpendicularSegmentPoint1y, double &perpendicularSegmentPoint2x, double &perpendicularSegmentPoint2y, double segmentLength=0)
Create a perpendicular line segment to a given segment [segmentPoint1,segmentPoint2] with its center ...
static bool circleAngleBetween(double angle, double angle1, double angle2, bool clockwise)
Returns true if, in a circle, angle is between angle1 and angle2.
static double triangleArea(double aX, double aY, double bX, double bY, double cX, double cY)
Returns the area of the triangle denoted by the points (aX, aY), (bX, bY) and (cX,...
static bool angleBisector(double aX, double aY, double bX, double bY, double cX, double cY, double dX, double dY, double &pointX, double &pointY, double &angle)
Returns the point (pointX, pointY) forming the bisector from segment (aX aY) (bX bY) and segment (bX,...
static double sqrDistToLine(double ptX, double ptY, double x1, double y1, double x2, double y2, double &minDistX, double &minDistY, double epsilon)
Returns the squared distance between a point and a line.
static bool intersectionPointOfLinesByBearing(double x1, double y1, double bearing1, double x2, double y2, double bearing2, double &intersectionX, double &intersectionY)
Calculates the intersection point of two lines defined by point and bearing.
static bool angleOnCircle(double angle, double angle1, double angle2, double angle3)
Returns true if an angle is between angle1 and angle3 on a circle described by angle1,...
static bool segmentIntersection(double p1x, double p1y, double p2x, double p2y, double q1x, double q1y, double q2x, double q2y, double &intersectionPointX, double &intersectionPointY, bool &isIntersection, double tolerance=1e-8, bool acceptImproperIntersection=false)
Compute the intersection between two segments.
static int leftOfLine(const double x, const double y, const double x1, const double y1, const double x2, const double y2)
Returns a value < 0 if the point (x, y) is left of the line from (x1, y1) -> (x2, y2).
static bool linesIntersection3D(const QgsVector3D &La1, const QgsVector3D &La2, const QgsVector3D &Lb1, const QgsVector3D &Lb2, QgsVector3D &intersection)
An algorithm to calculate an (approximate) intersection of two lines in 3D.
static bool pointsAreCollinear(double x1, double y1, double x2, double y2, double x3, double y3, double epsilon)
Given the points (x1, y1), (x2, y2) and (x3, y3) returns true if these points can be considered colli...
static void circleCenterRadius(double x1, double y1, double x2, double y2, double x3, double y3, double &radius, double &centerX, double &centerY)
Returns radius and center of the circle through (x1 y1), (x2 y2), (x3 y3).
static bool createFillet(const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY, const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY, const double radius, double *filletPointsX, double *filletPointsY, double *trim1StartX=nullptr, double *trim1StartY=nullptr, double *trim1EndX=nullptr, double *trim1EndY=nullptr, double *trim2StartX=nullptr, double *trim2StartY=nullptr, double *trim2EndX=nullptr, double *trim2EndY=nullptr, const double epsilon=1e-8)
Creates a fillet (rounded corner) between two line segments.
static bool lineIntersection(double p1x, double p1y, QgsVector v1, double p2x, double p2y, QgsVector v2, double &intersectionX, double &intersectionY)
Computes the intersection between two lines.
static double azimuth(double x1, double y1, double x2, double y2)
Calculates Cartesian azimuth between points (x1, y1) and (x2, y2) (clockwise in degree,...
Custom exception class when argument are invalid.
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:52
double z() const
Returns Z coordinate.
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:50
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 set(double x, double y, double z)
Sets vector coordinates.
Definition qgsvector3d.h:75
double length() const
Returns the length of the vector.
Represent a 2-dimensional vector.
Definition qgsvector.h:34
double y() const
Returns the vector's y-component.
Definition qgsvector.h:156
QgsVector normalized() const
Returns the vector's normalized (or "unit") vector (ie same angle but length of 1....
Definition qgsvector.cpp:33
QgsVector perpVector() const
Returns the perpendicular vector to this vector (rotated 90 degrees counter-clockwise).
Definition qgsvector.h:164
double x() const
Returns the vector's x-component.
Definition qgsvector.h:147
double length() const
Returns the length of the vector.
Definition qgsvector.h:128
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6935