QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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
577static bool equals( double p1x, double p1y, double p2x, double p2y, double epsilon = 1e-8 )
578{
579 return qgsDoubleNear( p1x, p2x, epsilon ) && qgsDoubleNear( p1y, p2y, epsilon );
580}
581
582bool 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 )
583{
584 isIntersection = false;
585 intersectionPointX = intersectionPointY = std::numeric_limits<double>::quiet_NaN();
586
587 QgsVector v( p2x - p1x, p2y - p1y );
588 QgsVector w( q2x - q1x, q2y - q1y );
589 const double vl = v.length();
590 const double wl = w.length();
591
592 if ( qgsDoubleNear( vl, 0.0, tolerance ) || qgsDoubleNear( wl, 0.0, tolerance ) )
593 {
594 return false;
595 }
596 v = v / vl;
597 w = w / wl;
598
599 if ( !lineIntersection( p1x, p1y, v, q1x, q1y, w, intersectionPointX, intersectionPointY ) )
600 {
601 return false;
602 }
603
604 isIntersection = true;
605 if ( acceptImproperIntersection )
606 {
607 if ( ( equals( p1x, p1y, q1x, q1y ) ) || ( equals( p1x, p1y, q2x, q2y ) ) )
608 {
609 intersectionPointX = p1x;
610 intersectionPointY = p1y;
611 return true;
612 }
613 else if ( ( equals( p1x, p1y, q2x, q2y ) ) || ( equals( p2x, p2y, q2x, q2y ) ) )
614 {
615 intersectionPointX = p2x;
616 intersectionPointY = p2y;
617 return true;
618 }
619
620 double x, y;
621 if (
622 // intersectionPoint = p1
623 qgsDoubleNear( sqrDistToLine( p1x, p1y, q1x, q1y, q2x, q2y, x, y, tolerance ), 0.0, tolerance ) ||
624 // intersectionPoint = p2
625 qgsDoubleNear( sqrDistToLine( p2x, p2y, q1x, q1y, q2x, q2y, x, y, tolerance ), 0.0, tolerance ) ||
626 // intersectionPoint = q1
627 qgsDoubleNear( sqrDistToLine( q1x, q1y, p1x, p1y, p2x, p2y, x, y, tolerance ), 0.0, tolerance ) ||
628 // intersectionPoint = q2
629 qgsDoubleNear( sqrDistToLine( q2x, q2y, p1x, p1y, p2x, p2y, x, y, tolerance ), 0.0, tolerance )
630 )
631 {
632 return true;
633 }
634 }
635
636 const double lambdav = QgsVector( intersectionPointX - p1x, intersectionPointY - p1y ) * v;
637 if ( lambdav < 0. + tolerance || lambdav > vl - tolerance )
638 return false;
639
640 const double lambdaw = QgsVector( intersectionPointX - q1x, intersectionPointY - q1y ) * w;
641 return !( lambdaw < 0. + tolerance || lambdaw >= wl - tolerance );
642
643}
644
645
647 const QgsVector3D &Lb1, const QgsVector3D &Lb2,
648 QgsVector3D &intersection )
649{
650
651 // if all Vector are on the same plane (have the same Z), use the 2D intersection
652 // else return a false result
653 if ( qgsDoubleNear( La1.z(), La2.z() ) && qgsDoubleNear( La1.z(), Lb1.z() ) && qgsDoubleNear( La1.z(), Lb2.z() ) )
654 {
655 double ptInterX = 0.0, ptInterY = 0.0;
656 bool isIntersection = false;
657 segmentIntersection( La1.x(), La1.y(),
658 La2.x(), La2.y(),
659 Lb1.x(), Lb1.y(),
660 Lb2.x(), Lb2.y(),
661 ptInterX, ptInterY,
662 isIntersection,
663 1e-8,
664 true );
665 intersection.set( ptInterX, ptInterY, La1.z() );
666 return true;
667 }
668
669 // first check if lines have an exact intersection point
670 // do it by checking if the shortest distance is exactly 0
671 const double distance = skewLinesDistance( La1, La2, Lb1, Lb2 );
672 if ( qgsDoubleNear( distance, 0.0 ) )
673 {
674 // 3d lines have exact intersection point.
675 const QgsVector3D C = La2;
676 const QgsVector3D D = Lb2;
677 const QgsVector3D e = La1 - La2;
678 const QgsVector3D f = Lb1 - Lb2;
679 const QgsVector3D g = D - C;
680 if ( qgsDoubleNear( ( QgsVector3D::crossProduct( f, g ) ).length(), 0.0 ) || qgsDoubleNear( ( QgsVector3D::crossProduct( f, e ) ).length(), 0.0 ) )
681 {
682 // Lines have no intersection, are they parallel?
683 return false;
684 }
685
687 fgn.normalize();
688
690 fen.normalize();
691
692 int di = -1;
693 if ( fgn == fen ) // same direction?
694 di *= -1;
695
696 intersection = C + e * di * ( QgsVector3D::crossProduct( f, g ).length() / QgsVector3D::crossProduct( f, e ).length() );
697 return true;
698 }
699
700 // try to calculate the approximate intersection point
701 QgsVector3D X1, X2;
702 const bool firstIsDone = skewLinesProjection( La1, La2, Lb1, Lb2, X1 );
703 const bool secondIsDone = skewLinesProjection( Lb1, Lb2, La1, La2, X2 );
704
705 if ( !firstIsDone || !secondIsDone )
706 {
707 // Could not obtain projection point.
708 return false;
709 }
710
711 intersection = ( X1 + X2 ) / 2.0;
712 return true;
713}
714
715double QgsGeometryUtilsBase::triangleArea( double aX, double aY, double bX, double bY, double cX, double cY )
716{
717 return 0.5 * std::abs( ( aX - cX ) * ( bY - aY ) - ( aX - bX ) * ( cY - aY ) );
718}
719
720double QgsGeometryUtilsBase::pointFractionAlongLine( double x1, double y1, double x2, double y2, double px, double py )
721{
722 const double dxp = px - x1;
723 const double dyp = py - y1;
724
725 const double dxl = x2 - x1;
726 const double dyl = y2 - y1;
727
728 return std::sqrt( ( dxp * dxp ) + ( dyp * dyp ) ) / std::sqrt( ( dxl * dxl ) + ( dyl * dyl ) );
729}
730
731void QgsGeometryUtilsBase::weightedPointInTriangle( const double aX, const double aY, const double bX, const double bY, const double cX, const double cY,
732 double weightB, double weightC, double &pointX, double &pointY )
733{
734 // if point will be outside of the triangle, invert weights
735 if ( weightB + weightC > 1 )
736 {
737 weightB = 1 - weightB;
738 weightC = 1 - weightC;
739 }
740
741 const double rBx = weightB * ( bX - aX );
742 const double rBy = weightB * ( bY - aY );
743 const double rCx = weightC * ( cX - aX );
744 const double rCy = weightC * ( cY - aY );
745
746 pointX = rBx + rCx + aX;
747 pointY = rBy + rCy + aY;
748}
749
750bool QgsGeometryUtilsBase::pointsAreCollinear( double x1, double y1, double x2, double y2, double x3, double y3, double epsilon )
751{
752 return qgsDoubleNear( x1 * ( y2 - y3 ) + x2 * ( y3 - y1 ) + x3 * ( y1 - y2 ), 0, epsilon );
753};
754
755bool QgsGeometryUtilsBase::points3DAreCollinear( double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, double z3, double epsilon )
756{
757 // crossproduct
758 const double cx = ( y2 - y1 ) * ( z3 - z1 ) - ( z2 - z1 ) * ( y3 - y1 );
759 const double cy = ( z2 - z1 ) * ( x3 - x1 ) - ( x2 - x1 ) * ( z3 - z1 );
760 const double cz = ( x2 - x1 ) * ( y3 - y1 ) - ( y2 - y1 ) * ( x3 - x1 );
761
762 // The magnitude of the cross product must be close to 0
763 return qgsDoubleNear( cx * cx + cy * cy + cz * cz, 0.0, epsilon * epsilon );
764}
765
766double QgsGeometryUtilsBase::azimuth( double x1, double y1, double x2, double y2 )
767{
768 const double dx = x2 - x1;
769 const double dy = y2 - y1;
770 return ( std::atan2( dx, dy ) * 180.0 / M_PI );
771}
772
773bool QgsGeometryUtilsBase::angleBisector( double aX, double aY, double bX, double bY, double cX, double cY, double dX, double dY,
774 double &pointX, double &pointY, double &angle )
775{
776 angle = ( azimuth( aX, aY, bX, bY ) + azimuth( cX, cY, dX, dY ) ) / 2.0;
777
778 bool intersection = false;
779 QgsGeometryUtilsBase::segmentIntersection( aX, aY, bX, bY, cX, cY, dX, dY, pointX, pointY, intersection );
780
781 return intersection;
782}
783
784void QgsGeometryUtilsBase::project( double aX, double aY, double aZ, double distance, double azimuth, double inclination, double &resultX, double &resultY, double &resultZ )
785{
786 const double radsXy = azimuth * M_PI / 180.0;
787 double dx = 0.0, dy = 0.0, dz = 0.0;
788
789 inclination = std::fmod( inclination, 360.0 );
790
791 if ( std::isnan( aZ ) && qgsDoubleNear( inclination, 90.0 ) )
792 {
793 dx = distance * std::sin( radsXy );
794 dy = distance * std::cos( radsXy );
795 }
796 else
797 {
798 const double radsZ = inclination * M_PI / 180.0;
799 dx = distance * std::sin( radsZ ) * std::sin( radsXy );
800 dy = distance * std::sin( radsZ ) * std::cos( radsXy );
801 dz = distance * std::cos( radsZ );
802 }
803
804 resultX = aX + dx;
805 resultY = aY + dy;
806 resultZ = aZ + dz;
807}
808
809bool QgsGeometryUtilsBase::bisector( double aX, double aY, double bX, double bY, double cX, double cY,
810 double &pointX, double &pointY )
811{
812 const double angle = ( azimuth( aX, aY, bX, bY ) + azimuth( aX, aY, cX, cY ) ) / 2.0;
813
814 bool intersection = false;
815 double dX = 0.0, dY = 0.0, dZ = 0.0;
816 project( aX, aY, std::numeric_limits<double>::quiet_NaN(), 1.0, angle, 90.0, dX, dY, dZ );
817 segmentIntersection( bX, bY, cX, cY, aX, aY, dX, dY, pointX, pointY, intersection );
818
819 return intersection;
820}
821
822
823double QgsGeometryUtilsBase::maxFilletRadius( const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY,
824 const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY,
825 double epsilon )
826{
827 double intersectionX, intersectionY;
828 bool isIntersection;
830 segment1StartX, segment1StartY, segment1EndX, segment1EndY,
831 segment2StartX, segment2StartY, segment2EndX, segment2EndY,
832 intersectionX, intersectionY, isIntersection, epsilon, true );
833
834 if ( !isIntersection )
835 {
836 return -1.0;
837 }
838
839 const double dist1ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1StartX, segment1StartY );
840 const double dist1ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1EndX, segment1EndY );
841 const double dist2ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2StartX, segment2StartY );
842 const double dist2ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2EndX, segment2EndY );
843
844 const double dir1X = dist1ToStart > epsilon ? segment1StartX : segment1EndX;
845 const double dir1Y = dist1ToStart > epsilon ? segment1StartY : segment1EndY;
846 const double dir2X = dist2ToStart > epsilon ? segment2StartX : segment2EndX;
847 const double dir2Y = dist2ToStart > epsilon ? segment2StartY : segment2EndY;
848
850 dir1X, dir1Y,
851 intersectionX, intersectionY,
852 dir2X, dir2Y
853 );
854
855 if ( std::abs( angle ) < epsilon || std::abs( angle - M_PI ) < epsilon )
856 {
857 return -1.0;
858 }
859
860 double workingAngle = angle;
861 if ( workingAngle > M_PI )
862 {
863 workingAngle = 2 * M_PI - workingAngle;
864 }
865
866 const double halfAngle = workingAngle / 2.0;
867 if ( std::abs( std::sin( halfAngle ) ) < epsilon )
868 {
869 return -1.0;
870 }
871
872 const double maxDist1 = ( dist1ToStart > epsilon ) ? dist1ToStart : dist1ToEnd;
873 const double maxDist2 = ( dist2ToStart > epsilon ) ? dist2ToStart : dist2ToEnd;
874
875 const double seg1Length = QgsGeometryUtilsBase::distance2D( segment1StartX, segment1StartY, segment1EndX, segment1EndY );
876 const double seg2Length = QgsGeometryUtilsBase::distance2D( segment2StartX, segment2StartY, segment2EndX, segment2EndY );
877
878 const bool intersectionOnSeg1 = std::abs( ( dist1ToStart + dist1ToEnd ) - seg1Length ) < epsilon;
879 const bool intersectionOnSeg2 = std::abs( ( dist2ToStart + dist2ToEnd ) - seg2Length ) < epsilon;
880
881 double maxDistanceToTangent = std::numeric_limits<double>::max();
882
883 if ( intersectionOnSeg1 )
884 {
885 maxDistanceToTangent = std::min( maxDistanceToTangent, maxDist1 - epsilon );
886 }
887
888 if ( intersectionOnSeg2 )
889 {
890 maxDistanceToTangent = std::min( maxDistanceToTangent, maxDist2 - epsilon );
891 }
892
893 if ( maxDistanceToTangent == std::numeric_limits<double>::max() )
894 {
895 maxDistanceToTangent = std::min( maxDist1, maxDist2 ) - epsilon;
896 }
897
898 if ( maxDistanceToTangent <= 0 )
899 {
900 return -1.0;
901 }
902
903 return maxDistanceToTangent * std::tan( halfAngle );
904}
905
907 const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY,
908 const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY,
909 const double radius,
910 double *filletPointsX, double *filletPointsY,
911 double *trim1StartX, double *trim1StartY,
912 double *trim1EndX, double *trim1EndY,
913 double *trim2StartX, double *trim2StartY,
914 double *trim2EndX, double *trim2EndY,
915 const double epsilon )
916{
917 if ( radius <= 0 )
918 throw QgsInvalidArgumentException( "Radius must be greater than 0." );
919
920 // Find intersection point between segments (or their infinite line extensions)
921 double intersectionX, intersectionY;
922 bool isIntersection;
924 segment1StartX, segment1StartY, segment1EndX, segment1EndY,
925 segment2StartX, segment2StartY, segment2EndX, segment2EndY,
926 intersectionX, intersectionY, isIntersection, epsilon, true );
927
928 if ( !isIntersection )
929 {
930 throw QgsInvalidArgumentException( "Segments do not intersect." );
931 }
932
933 // Calculate distances from intersection to all segment endpoints
934 const double dist1ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1StartX, segment1StartY );
935 const double dist1ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1EndX, segment1EndY );
936 const double dist2ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2StartX, segment2StartY );
937 const double dist2ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2EndX, segment2EndY );
938
939 // Determine directional points for angle calculation
940 // These points define the rays extending from the intersection
941 const double dir1X = dist1ToStart > epsilon ? segment1StartX : segment1EndX;
942 const double dir1Y = dist1ToStart > epsilon ? segment1StartY : segment1EndY;
943 const double dir2X = dist2ToStart > epsilon ? segment2StartX : segment2EndX;
944 const double dir2Y = dist2ToStart > epsilon ? segment2StartY : segment2EndY;
945
946 // Calculate the angle between the two rays
948 dir1X, dir1Y,
949 intersectionX, intersectionY,
950 dir2X, dir2Y
951 );
952
953 // Validate angle - must be meaningful for fillet creation
954 if ( std::abs( angle ) < epsilon || std::abs( angle - M_PI ) < epsilon )
955 {
956 throw QgsInvalidArgumentException( "Parallel or anti-parallel segments." );
957 }
958
959 // Use the interior angle (always ≤ π) for fillet calculations
960 double workingAngle = angle;
961 if ( workingAngle > M_PI )
962 {
963 workingAngle = 2 * M_PI - workingAngle;
964 }
965
966 const double halfAngle = workingAngle / 2.0;
967 if ( std::abs( std::sin( halfAngle ) ) < epsilon )
968 {
969 // Avoid division by very small numbers.
970 throw QgsInvalidArgumentException( "Segment angle near 0 will generate wrong division" );
971 }
972
973 // Calculate distance from intersection to tangent points using trigonometry
974 const double distanceToTangent = radius / std::tan( halfAngle );
975
976 const double maxDist1 = ( dist1ToStart > epsilon ) ? dist1ToStart : dist1ToEnd;
977 const double maxDist2 = ( dist2ToStart > epsilon ) ? dist2ToStart : dist2ToEnd;
978
979 // Check if intersection is actually on the segments (not just on infinite lines)
980 const double seg1Length = QgsGeometryUtilsBase::distance2D( segment1StartX, segment1StartY, segment1EndX, segment1EndY );
981 const double seg2Length = QgsGeometryUtilsBase::distance2D( segment2StartX, segment2StartY, segment2EndX, segment2EndY );
982
983 const bool intersectionOnSeg1 = std::abs( ( dist1ToStart + dist1ToEnd ) - seg1Length ) < epsilon;
984 const bool intersectionOnSeg2 = std::abs( ( dist2ToStart + dist2ToEnd ) - seg2Length ) < epsilon;
985
986 // Only enforce distance limits if intersection is actually on the segment
987 // This allows fillets on non-touching segments (like chamfer behavior)
988 if ( intersectionOnSeg1 && distanceToTangent > maxDist1 - epsilon )
989 {
990 throw QgsInvalidArgumentException( "Intersection 1 on segment but too far." );
991 }
992
993 if ( intersectionOnSeg2 && distanceToTangent > maxDist2 - epsilon )
994 {
995 throw QgsInvalidArgumentException( "Intersection 2 on segment but too far." );
996 }
997
998 // Calculate tangent points along the rays
999 double T1x, T1y, T2x, T2y;
1001 intersectionX, intersectionY,
1002 dir1X, dir1Y,
1003 distanceToTangent, T1x, T1y
1004 );
1006 intersectionX, intersectionY,
1007 dir2X, dir2Y,
1008 distanceToTangent, T2x, T2y
1009 );
1010
1011 // Calculate circle center using angle bisector geometry
1012 const QgsVector v1( dir1X - intersectionX, dir1Y - intersectionY );
1013 const QgsVector v2( dir2X - intersectionX, dir2Y - intersectionY );
1014
1015 // The bisector direction is the normalized sum of the unit vectors
1016 const QgsVector bisectorDirection = ( v1.normalized() + v2.normalized() ).normalized();
1017
1018 // Distance from intersection to circle center
1019 const double centerDistance = radius / std::sin( halfAngle );
1020 const double centerX = intersectionX + bisectorDirection.x() * centerDistance;
1021 const double centerY = intersectionY + bisectorDirection.y() * centerDistance;
1022
1023 // Calculate arc midpoint - the point on the circle that bisects the arc
1024 const QgsVector centerToT1 = QgsVector( T1x - centerX, T1y - centerY ).normalized();
1025 const QgsVector centerToT2 = QgsVector( T2x - centerX, T2y - centerY ).normalized();
1026 const QgsVector midDirection = ( centerToT1 + centerToT2 ).normalized();
1027
1028 const double midX = centerX + midDirection.x() * radius;
1029 const double midY = centerY + midDirection.y() * radius;
1030
1031 // Return three-point arc representation: tangent1, midpoint, tangent2
1032 filletPointsX[0] = T1x;
1033 filletPointsY[0] = T1y;
1034 filletPointsX[1] = midX;
1035 filletPointsY[1] = midY;
1036 filletPointsX[2] = T2x;
1037 filletPointsY[2] = T2y;
1038
1039 // Generate trimmed segments if requested
1040 if ( trim1StartX )
1041 {
1042 if ( dist1ToStart > epsilon )
1043 {
1044 *trim1StartX = segment1StartX;
1045 *trim1StartY = segment1StartY;
1046 }
1047 else
1048 {
1049 *trim1StartX = segment1EndX;
1050 *trim1StartY = segment1EndY;
1051 }
1052 *trim1EndX = T1x;
1053 *trim1EndY = T1y;
1054 }
1055 if ( trim2StartX )
1056 {
1057 if ( dist2ToStart > epsilon )
1058 {
1059 *trim2StartX = segment2StartX;
1060 *trim2StartY = segment2StartY;
1061 }
1062 else
1063 {
1064 *trim2StartX = segment2EndX;
1065 *trim2StartY = segment2EndY;
1066 }
1067 *trim2EndX = T2x;
1068 *trim2EndY = T2y;
1069 }
1070
1071 return true;
1072}
1073
1075 const double segment1StartX, const double segment1StartY, const double segment1EndX, const double segment1EndY,
1076 const double segment2StartX, const double segment2StartY, const double segment2EndX, const double segment2EndY,
1077 double distance1, double distance2,
1078 double &chamferStartX, double &chamferStartY,
1079 double &chamferEndX, double &chamferEndY,
1080 double *trim1StartX, double *trim1StartY,
1081 double *trim1EndX, double *trim1EndY,
1082 double *trim2StartX, double *trim2StartY,
1083 double *trim2EndX, double *trim2EndY,
1084 const double epsilon )
1085{
1086 // Apply symmetric distance if distance2 is negative
1087 if ( distance2 <= 0 )
1088 distance2 = distance1;
1089
1090 // Only for positive distance
1091 if ( distance1 <= 0 || distance2 <= 0 )
1092 throw QgsInvalidArgumentException( "Negative distances." );
1093
1094 // Find intersection point between segments (or their infinite line extensions)
1095 double intersectionX, intersectionY;
1096 bool isIntersection;
1098 segment1StartX, segment1StartY, segment1EndX, segment1EndY,
1099 segment2StartX, segment2StartY, segment2EndX, segment2EndY,
1100 intersectionX, intersectionY, isIntersection, epsilon, true );
1101
1102 if ( !isIntersection )
1103 {
1104 throw QgsInvalidArgumentException( "Segments do not intersect." );
1105 }
1106
1107 // Apply symmetric distance if distance2 is negative
1108 if ( distance2 < 0 )
1109 distance2 = distance1;
1110
1111 // Calculate distances from intersection to all segment endpoints
1112 const double dist1ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1StartX, segment1StartY );
1113 const double dist1ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1EndX, segment1EndY );
1114 const double dist2ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2StartX, segment2StartY );
1115 const double dist2ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2EndX, segment2EndY );
1116
1117 // Calculate chamfer points along each segment
1118 // Choose the endpoint farthest from intersection as direction
1119 double T1x, T1y;
1120 if ( dist1ToStart > epsilon )
1121 {
1123 intersectionX, intersectionY,
1124 segment1StartX, segment1StartY,
1125 distance1, T1x, T1y
1126 );
1127 }
1128 else
1129 {
1131 intersectionX, intersectionY,
1132 segment1EndX, segment1EndY,
1133 distance1, T1x, T1y
1134 );
1135 }
1136
1137 double T2x, T2y;
1138 if ( dist2ToStart > epsilon )
1139 {
1141 intersectionX, intersectionY,
1142 segment2StartX, segment2StartY,
1143 distance2, T2x, T2y
1144 );
1145 }
1146 else
1147 {
1149 intersectionX, intersectionY,
1150 segment2EndX, segment2EndY,
1151 distance2, T2x, T2y
1152 );
1153 }
1154
1155 // Clamp distances to available segment length if necessary
1156 const double distToSeg1Target = ( dist1ToStart > epsilon ) ? dist1ToStart : dist1ToEnd;
1157 const double distToSeg2Target = ( dist2ToStart > epsilon ) ? dist2ToStart : dist2ToEnd;
1158
1159 if ( distance1 > distToSeg1Target - epsilon )
1160 {
1161 if ( dist1ToStart > epsilon )
1162 {
1164 intersectionX, intersectionY,
1165 segment1StartX, segment1StartY,
1166 distToSeg1Target, T1x, T1y
1167 );
1168 }
1169 else
1170 {
1172 intersectionX, intersectionY,
1173 segment1EndX, segment1EndY,
1174 distToSeg1Target, T1x, T1y
1175 );
1176 }
1177 }
1178
1179 if ( distance2 > distToSeg2Target - epsilon )
1180 {
1181 if ( dist2ToStart > epsilon )
1182 {
1184 intersectionX, intersectionY,
1185 segment2StartX, segment2StartY,
1186 distToSeg2Target, T2x, T2y
1187 );
1188 }
1189 else
1190 {
1192 intersectionX, intersectionY,
1193 segment2EndX, segment2EndY,
1194 distToSeg2Target, T2x, T2y
1195 );
1196 }
1197 }
1198
1199 chamferStartX = T1x;
1200 chamferStartY = T1y;
1201 chamferEndX = T2x;
1202 chamferEndY = T2y;
1203
1204 // Generate trimmed segments if requested
1205 if ( trim1StartX )
1206 {
1207 if ( dist1ToStart > epsilon )
1208 {
1209 *trim1StartX = segment1StartX; *trim1StartY = segment1StartY;
1210 *trim1EndX = T1x; *trim1EndY = T1y;
1211 }
1212 else
1213 {
1214 *trim1StartX = segment1EndX; *trim1StartY = segment1EndY;
1215 *trim1EndX = T1x; *trim1EndY = T1y;
1216 }
1217 }
1218 if ( trim2StartX )
1219 {
1220 if ( dist2ToStart > epsilon )
1221 {
1222 *trim2StartX = segment2StartX; *trim2StartY = segment2StartY;
1223 *trim2EndX = T2x; *trim2EndY = T2y;
1224 }
1225 else
1226 {
1227 *trim2StartX = segment2EndX; *trim2StartY = segment2EndY;
1228 *trim2EndX = T2x; *trim2EndY = T2y;
1229 }
1230 }
1231
1232 return true;
1233}
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 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 double maxFilletRadius(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 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:30
double y() const
Returns Y coordinate.
Definition qgsvector3d.h:49
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:51
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 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:72
double length() const
Returns the length of the vector.
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
double length() const
Returns the length of the vector.
Definition qgsvector.h:125
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607