QGIS API Documentation 4.1.0-Master (60fea48833c)
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, double x1, double y1, double x2, double y2, double x3, double y3, int fromVertex, int toVertex )
227{
228 if ( fromVertex == toVertex )
229 return 0.0;
230
231 if ( fromVertex < 0 || fromVertex > 2 || toVertex < 0 || toVertex > 2 )
232 return 0.0;
233
234 // Calculate angles for all three points (in degrees)
235 const double angle1 = QgsGeometryUtilsBase::ccwAngle( y1 - centerY, x1 - centerX );
236 const double angle2 = QgsGeometryUtilsBase::ccwAngle( y2 - centerY, x2 - centerX );
237 const double angle3 = QgsGeometryUtilsBase::ccwAngle( y3 - centerY, x3 - centerX );
238
239 // Determine the direction of the arc using the sweep angle
240 const double totalSweepAngle = QgsGeometryUtilsBase::sweepAngle( centerX, centerY, x1, y1, x2, y2, x3, y3 );
241 bool clockwise = totalSweepAngle < 0;
242
243 // Map vertex indices to angles
244 double fromAngle, toAngle;
245 if ( fromVertex == 0 )
246 fromAngle = angle1;
247 else if ( fromVertex == 1 )
248 fromAngle = angle2;
249 else
250 fromAngle = angle3;
251
252 if ( toVertex == 0 )
253 toAngle = angle1;
254 else if ( toVertex == 1 )
255 toAngle = angle2;
256 else
257 toAngle = angle3;
258
259 // Calculate the arc angle between the two points following the arc direction (in degrees)
260 double arcAngleDegrees;
261 if ( clockwise )
262 {
263 arcAngleDegrees = fromAngle - toAngle;
264 if ( arcAngleDegrees <= 0 )
265 {
266 arcAngleDegrees += 360.0;
267 }
268 }
269 else
270 {
271 arcAngleDegrees = toAngle - fromAngle;
272 if ( arcAngleDegrees <= 0 )
273 {
274 arcAngleDegrees += 360.0;
275 }
276 }
277
278 // Make sure we follow the arc in the right direction
279 // For a 3-point arc, we should never have an angle > the total arc
280 double totalArcAngleDegrees = std::abs( totalSweepAngle );
281 if ( arcAngleDegrees > totalArcAngleDegrees && ( fromVertex == 0 && toVertex == 2 ) == false )
282 {
283 // We went the wrong way around the circle, take the shorter arc
284 arcAngleDegrees = 360.0 - arcAngleDegrees;
285 }
286
287 // Convert to radians for arc length calculation
288 const double arcAngleRadians = arcAngleDegrees * M_PI / 180.0;
289 return radius * arcAngleRadians;
290}
291
292double QgsGeometryUtilsBase::sweepAngle( double centerX, double centerY, double x1, double y1, double x2, double y2, double x3, double y3 )
293{
294 const double p1Angle = QgsGeometryUtilsBase::ccwAngle( y1 - centerY, x1 - centerX );
295 const double p2Angle = QgsGeometryUtilsBase::ccwAngle( y2 - centerY, x2 - centerX );
296 const double p3Angle = QgsGeometryUtilsBase::ccwAngle( y3 - centerY, x3 - centerX );
297
298 if ( p3Angle >= p1Angle )
299 {
300 if ( p2Angle > p1Angle && p2Angle < p3Angle )
301 {
302 return ( p3Angle - p1Angle );
303 }
304 else
305 {
306 return ( -( p1Angle + ( 360 - p3Angle ) ) );
307 }
308 }
309 else
310 {
311 if ( p2Angle < p1Angle && p2Angle > p3Angle )
312 {
313 return ( -( p1Angle - p3Angle ) );
314 }
315 else
316 {
317 return ( p3Angle + ( 360 - p1Angle ) );
318 }
319 }
320}
321
322double QgsGeometryUtilsBase::interpolateArcValue( double angle, double a1, double a2, double a3, double zm1, double zm2, double zm3 )
323{
324 /* Counter-clockwise sweep */
325 if ( a1 < a2 )
326 {
327 if ( angle <= a2 )
328 return zm1 + ( zm2 - zm1 ) * ( angle - a1 ) / ( a2 - a1 );
329 else
330 return zm2 + ( zm3 - zm2 ) * ( angle - a2 ) / ( a3 - a2 );
331 }
332 /* Clockwise sweep */
333 else
334 {
335 if ( angle >= a2 )
336 return zm1 + ( zm2 - zm1 ) * ( a1 - angle ) / ( a1 - a2 );
337 else
338 return zm2 + ( zm3 - zm2 ) * ( a2 - angle ) / ( a2 - a3 );
339 }
340}
342{
343 double clippedAngle = angle;
344 if ( clippedAngle >= M_PI * 2 || clippedAngle <= -2 * M_PI )
345 {
346 clippedAngle = std::fmod( clippedAngle, 2 * M_PI );
347 }
348 if ( clippedAngle < 0.0 )
349 {
350 clippedAngle += 2 * M_PI;
351 }
352 return clippedAngle;
353}
354
355
356int QgsGeometryUtilsBase::closestSideOfRectangle( double right, double bottom, double left, double top, double x, double y )
357{
358 // point outside rectangle
359 if ( x <= left && y <= bottom )
360 {
361 const double dx = left - x;
362 const double dy = bottom - y;
363 if ( qgsDoubleNear( dx, dy ) )
364 return 6;
365 else if ( dx < dy )
366 return 5;
367 else
368 return 7;
369 }
370 else if ( x >= right && y >= top )
371 {
372 const double dx = x - right;
373 const double dy = y - top;
374 if ( qgsDoubleNear( dx, dy ) )
375 return 2;
376 else if ( dx < dy )
377 return 1;
378 else
379 return 3;
380 }
381 else if ( x >= right && y <= bottom )
382 {
383 const double dx = x - right;
384 const double dy = bottom - y;
385 if ( qgsDoubleNear( dx, dy ) )
386 return 4;
387 else if ( dx < dy )
388 return 5;
389 else
390 return 3;
391 }
392 else if ( x <= left && y >= top )
393 {
394 const double dx = left - x;
395 const double dy = y - top;
396 if ( qgsDoubleNear( dx, dy ) )
397 return 8;
398 else if ( dx < dy )
399 return 1;
400 else
401 return 7;
402 }
403 else if ( x <= left )
404 return 7;
405 else if ( x >= right )
406 return 3;
407 else if ( y <= bottom )
408 return 5;
409 else if ( y >= top )
410 return 1;
411
412 // point is inside rectangle
413 const double smallestX = std::min( right - x, x - left );
414 const double smallestY = std::min( top - y, y - bottom );
415 if ( smallestX < smallestY )
416 {
417 // closer to left/right side
418 if ( right - x < x - left )
419 return 3; // closest to right side
420 else
421 return 7;
422 }
423 else
424 {
425 // closer to top/bottom side
426 if ( top - y < y - bottom )
427 return 1; // closest to top side
428 else
429 return 5;
430 }
431}
432
434 double pointx,
435 double pointy,
436 double segmentPoint1x,
437 double segmentPoint1y,
438 double segmentPoint2x,
439 double segmentPoint2y,
440 double &perpendicularSegmentPoint1x,
441 double &perpendicularSegmentPoint1y,
442 double &perpendicularSegmentPoint2x,
443 double &perpendicularSegmentPoint2y,
444 double desiredSegmentLength
445)
446{
447 QgsVector segmentVector = QgsVector( segmentPoint2x - segmentPoint1x, segmentPoint2y - segmentPoint1y );
448 QgsVector perpendicularVector = segmentVector.perpVector();
449 if ( desiredSegmentLength != 0 )
450 {
451 perpendicularVector = perpendicularVector.normalized() * ( desiredSegmentLength ) / 2;
452 }
453
454 perpendicularSegmentPoint1x = pointx - perpendicularVector.x();
455 perpendicularSegmentPoint1y = pointy - perpendicularVector.y();
456 perpendicularSegmentPoint2x = pointx + perpendicularVector.x();
457 perpendicularSegmentPoint2y = pointy + perpendicularVector.y();
458}
459
460double QgsGeometryUtilsBase::lineAngle( double x1, double y1, double x2, double y2 )
461{
462 const double at = std::atan2( y2 - y1, x2 - x1 );
463 const double a = -at + M_PI_2;
464 return normalizedAngle( a );
465}
466
467double QgsGeometryUtilsBase::angleBetweenThreePoints( double x1, double y1, double x2, double y2, double x3, double y3 )
468{
469 const double angle1 = std::atan2( y1 - y2, x1 - x2 );
470 const double angle2 = std::atan2( y3 - y2, x3 - x2 );
471 return normalizedAngle( angle1 - angle2 );
472}
473
474double QgsGeometryUtilsBase::linePerpendicularAngle( double x1, double y1, double x2, double y2 )
475{
476 double a = lineAngle( x1, y1, x2, y2 );
477 a += M_PI_2;
478 return normalizedAngle( a );
479}
480
481double QgsGeometryUtilsBase::averageAngle( double x1, double y1, double x2, double y2, double x3, double y3 )
482{
483 // calc average angle between the previous and next point
484 const double a1 = lineAngle( x1, y1, x2, y2 );
485 const double a2 = lineAngle( x2, y2, x3, y3 );
486 return averageAngle( a1, a2 );
487}
488
489double QgsGeometryUtilsBase::averageAngle( double a1, double a2 )
490{
491 a1 = normalizedAngle( a1 );
492 a2 = normalizedAngle( a2 );
493 double clockwiseDiff = 0.0;
494 if ( a2 >= a1 )
495 {
496 clockwiseDiff = a2 - a1;
497 }
498 else
499 {
500 clockwiseDiff = a2 + ( 2 * M_PI - a1 );
501 }
502 const double counterClockwiseDiff = 2 * M_PI - clockwiseDiff;
503
504 double resultAngle = 0;
505 if ( clockwiseDiff <= counterClockwiseDiff )
506 {
507 resultAngle = a1 + clockwiseDiff / 2.0;
508 }
509 else
510 {
511 resultAngle = a1 - counterClockwiseDiff / 2.0;
512 }
513 return normalizedAngle( resultAngle );
514}
515
516double QgsGeometryUtilsBase::skewLinesDistance( const QgsVector3D &P1, const QgsVector3D &P12, const QgsVector3D &P2, const QgsVector3D &P22 )
517{
518 const QgsVector3D u1 = P12 - P1;
519 const QgsVector3D u2 = P22 - P2;
521 if ( u3.length() == 0 )
522 return 1;
523 u3.normalize();
524 const QgsVector3D dir = P1 - P2;
525 return std::fabs( ( QgsVector3D::dotProduct( dir, u3 ) ) ); // u3 is already normalized
526}
527
528bool QgsGeometryUtilsBase::skewLinesProjection( const QgsVector3D &P1, const QgsVector3D &P12, const QgsVector3D &P2, const QgsVector3D &P22, QgsVector3D &X1, double epsilon )
529{
530 const QgsVector3D d = P2 - P1;
531 QgsVector3D u1 = P12 - P1;
532 u1.normalize();
533 QgsVector3D u2 = P22 - P2;
534 u2.normalize();
535 const QgsVector3D u3 = QgsVector3D::crossProduct( u1, u2 );
536
537 if ( std::fabs( u3.x() ) <= epsilon && std::fabs( u3.y() ) <= epsilon && std::fabs( u3.z() ) <= epsilon )
538 {
539 // The rays are almost parallel.
540 return false;
541 }
542
543 // X1 and X2 are the closest points on lines
544 // we want to find X1 (lies on u1)
545 // solving the linear equation in r1 and r2: Xi = Pi + ri*ui
546 // we are only interested in X1 so we only solve for r1.
547 const double a1 = QgsVector3D::dotProduct( u1, u1 ), b1 = QgsVector3D::dotProduct( u1, u2 ), c1 = QgsVector3D::dotProduct( u1, d );
548 const double a2 = QgsVector3D::dotProduct( u1, u2 ), b2 = QgsVector3D::dotProduct( u2, u2 ), c2 = QgsVector3D::dotProduct( u2, d );
549 if ( !( std::fabs( b1 ) > epsilon ) )
550 {
551 // Denominator is close to zero.
552 return false;
553 }
554 if ( !( a2 != -1 && a2 != 1 ) )
555 {
556 // Lines are parallel
557 return false;
558 }
559
560 const double r1 = ( c2 - b2 * c1 / b1 ) / ( a2 - b2 * a1 / b1 );
561 X1 = P1 + u1 * r1;
562
563 return true;
564}
565
566bool QgsGeometryUtilsBase::lineIntersection( double p1x, double p1y, QgsVector v1, double p2x, double p2y, QgsVector v2, double &intersectionX, double &intersectionY )
567{
568 const double d = v1.y() * v2.x() - v1.x() * v2.y();
569
570 if ( qgsDoubleNear( d, 0 ) )
571 return false;
572
573 const double dx = p2x - p1x;
574 const double dy = p2y - p1y;
575 const double k = ( dy * v2.x() - dx * v2.y() ) / d;
576
577 intersectionX = p1x + v1.x() * k;
578 intersectionY = p1y + v1.y() * k;
579
580 return true;
581}
582
583bool QgsGeometryUtilsBase::intersectionPointOfLinesByBearing( double x1, double y1, double bearing1, double x2, double y2, double bearing2, double &intersectionX, double &intersectionY )
584{
585 // Convert bearings (clockwise from north) to direction vectors
586 // For bearing β: direction = (sin(β), cos(β))
587 const QgsVector v1( std::sin( bearing1 ), std::cos( bearing1 ) );
588 const QgsVector v2( std::sin( bearing2 ), std::cos( bearing2 ) );
589
590 return lineIntersection( x1, y1, v1, x2, y2, v2, intersectionX, intersectionY );
591}
592
593static bool equals( double p1x, double p1y, double p2x, double p2y, double epsilon = 1e-8 )
594{
595 return qgsDoubleNear( p1x, p2x, epsilon ) && qgsDoubleNear( p1y, p2y, epsilon );
596}
597
599 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
600)
601{
602 isIntersection = false;
603 intersectionPointX = intersectionPointY = std::numeric_limits<double>::quiet_NaN();
604
605 QgsVector v( p2x - p1x, p2y - p1y );
606 QgsVector w( q2x - q1x, q2y - q1y );
607 const double vl = v.length();
608 const double wl = w.length();
609
610 if ( qgsDoubleNear( vl, 0.0, tolerance ) || qgsDoubleNear( wl, 0.0, tolerance ) )
611 {
612 return false;
613 }
614 v = v / vl;
615 w = w / wl;
616
617 if ( !lineIntersection( p1x, p1y, v, q1x, q1y, w, intersectionPointX, intersectionPointY ) )
618 {
619 return false;
620 }
621
622 isIntersection = true;
623 if ( acceptImproperIntersection )
624 {
625 if ( ( equals( p1x, p1y, q1x, q1y ) ) || ( equals( p1x, p1y, q2x, q2y ) ) )
626 {
627 intersectionPointX = p1x;
628 intersectionPointY = p1y;
629 return true;
630 }
631 else if ( ( equals( p1x, p1y, q2x, q2y ) ) || ( equals( p2x, p2y, q2x, q2y ) ) )
632 {
633 intersectionPointX = p2x;
634 intersectionPointY = p2y;
635 return true;
636 }
637
638 double x, y;
639 if (
640 // intersectionPoint = p1
641 qgsDoubleNear( sqrDistToLine( p1x, p1y, q1x, q1y, q2x, q2y, x, y, tolerance ), 0.0, tolerance ) ||
642 // intersectionPoint = p2
643 qgsDoubleNear( sqrDistToLine( p2x, p2y, q1x, q1y, q2x, q2y, x, y, tolerance ), 0.0, tolerance ) ||
644 // intersectionPoint = q1
645 qgsDoubleNear( sqrDistToLine( q1x, q1y, p1x, p1y, p2x, p2y, x, y, tolerance ), 0.0, tolerance ) ||
646 // intersectionPoint = q2
647 qgsDoubleNear( sqrDistToLine( q2x, q2y, p1x, p1y, p2x, p2y, x, y, tolerance ), 0.0, tolerance )
648 )
649 {
650 return true;
651 }
652 }
653
654 const double lambdav = QgsVector( intersectionPointX - p1x, intersectionPointY - p1y ) * v;
655 if ( lambdav < 0. + tolerance || lambdav > vl - tolerance )
656 return false;
657
658 const double lambdaw = QgsVector( intersectionPointX - q1x, intersectionPointY - q1y ) * w;
659 return !( lambdaw < 0. + tolerance || lambdaw >= wl - tolerance );
660}
661
662
663bool QgsGeometryUtilsBase::linesIntersection3D( const QgsVector3D &La1, const QgsVector3D &La2, const QgsVector3D &Lb1, const QgsVector3D &Lb2, QgsVector3D &intersection )
664{
665 // if all Vector are on the same plane (have the same Z), use the 2D intersection
666 // else return a false result
667 if ( qgsDoubleNear( La1.z(), La2.z() ) && qgsDoubleNear( La1.z(), Lb1.z() ) && qgsDoubleNear( La1.z(), Lb2.z() ) )
668 {
669 double ptInterX = 0.0, ptInterY = 0.0;
670 bool isIntersection = false;
671 segmentIntersection( La1.x(), La1.y(), La2.x(), La2.y(), Lb1.x(), Lb1.y(), Lb2.x(), Lb2.y(), ptInterX, ptInterY, isIntersection, 1e-8, true );
672 intersection.set( ptInterX, ptInterY, La1.z() );
673 return true;
674 }
675
676 // first check if lines have an exact intersection point
677 // do it by checking if the shortest distance is exactly 0
678 const double distance = skewLinesDistance( La1, La2, Lb1, Lb2 );
679 if ( qgsDoubleNear( distance, 0.0 ) )
680 {
681 // 3d lines have exact intersection point.
682 const QgsVector3D C = La2;
683 const QgsVector3D D = Lb2;
684 const QgsVector3D e = La1 - La2;
685 const QgsVector3D f = Lb1 - Lb2;
686 const QgsVector3D g = D - C;
687 if ( qgsDoubleNear( ( QgsVector3D::crossProduct( f, g ) ).length(), 0.0 ) || qgsDoubleNear( ( QgsVector3D::crossProduct( f, e ) ).length(), 0.0 ) )
688 {
689 // Lines have no intersection, are they parallel?
690 return false;
691 }
692
694 fgn.normalize();
695
697 fen.normalize();
698
699 int di = -1;
700 if ( fgn == fen ) // same direction?
701 di *= -1;
702
703 intersection = C + e * di * ( QgsVector3D::crossProduct( f, g ).length() / QgsVector3D::crossProduct( f, e ).length() );
704 return true;
705 }
706
707 // try to calculate the approximate intersection point
708 QgsVector3D X1, X2;
709 const bool firstIsDone = skewLinesProjection( La1, La2, Lb1, Lb2, X1 );
710 const bool secondIsDone = skewLinesProjection( Lb1, Lb2, La1, La2, X2 );
711
712 if ( !firstIsDone || !secondIsDone )
713 {
714 // Could not obtain projection point.
715 return false;
716 }
717
718 intersection = ( X1 + X2 ) / 2.0;
719 return true;
720}
721
722double QgsGeometryUtilsBase::triangleArea( double aX, double aY, double bX, double bY, double cX, double cY )
723{
724 return 0.5 * std::abs( ( aX - cX ) * ( bY - aY ) - ( aX - bX ) * ( cY - aY ) );
725}
726
727double QgsGeometryUtilsBase::pointFractionAlongLine( double x1, double y1, double x2, double y2, double px, double py )
728{
729 const double dxp = px - x1;
730 const double dyp = py - y1;
731
732 const double dxl = x2 - x1;
733 const double dyl = y2 - y1;
734
735 return std::sqrt( ( dxp * dxp ) + ( dyp * dyp ) ) / std::sqrt( ( dxl * dxl ) + ( dyl * dyl ) );
736}
737
739 const double aX, const double aY, const double bX, const double bY, const double cX, const double cY, double weightB, double weightC, double &pointX, double &pointY
740)
741{
742 // if point will be outside of the triangle, invert weights
743 if ( weightB + weightC > 1 )
744 {
745 weightB = 1 - weightB;
746 weightC = 1 - weightC;
747 }
748
749 const double rBx = weightB * ( bX - aX );
750 const double rBy = weightB * ( bY - aY );
751 const double rCx = weightC * ( cX - aX );
752 const double rCy = weightC * ( cY - aY );
753
754 pointX = rBx + rCx + aX;
755 pointY = rBy + rCy + aY;
756}
757
758bool QgsGeometryUtilsBase::pointsAreCollinear( double x1, double y1, double x2, double y2, double x3, double y3, double epsilon )
759{
760 return qgsDoubleNear( x1 * ( y2 - y3 ) + x2 * ( y3 - y1 ) + x3 * ( y1 - y2 ), 0, epsilon );
761};
762
763bool QgsGeometryUtilsBase::points3DAreCollinear( double x1, double y1, double z1, double x2, double y2, double z2, double x3, double y3, double z3, double epsilon )
764{
765 // crossproduct
766 const double cx = ( y2 - y1 ) * ( z3 - z1 ) - ( z2 - z1 ) * ( y3 - y1 );
767 const double cy = ( z2 - z1 ) * ( x3 - x1 ) - ( x2 - x1 ) * ( z3 - z1 );
768 const double cz = ( x2 - x1 ) * ( y3 - y1 ) - ( y2 - y1 ) * ( x3 - x1 );
769
770 // The magnitude of the cross product must be close to 0
771 return qgsDoubleNear( cx * cx + cy * cy + cz * cz, 0.0, epsilon * epsilon );
772}
773
774double QgsGeometryUtilsBase::azimuth( double x1, double y1, double x2, double y2 )
775{
776 const double dx = x2 - x1;
777 const double dy = y2 - y1;
778 return ( std::atan2( dx, dy ) * 180.0 / M_PI );
779}
780
781bool QgsGeometryUtilsBase::angleBisector( double aX, double aY, double bX, double bY, double cX, double cY, double dX, double dY, double &pointX, double &pointY, double &angle )
782{
783 angle = ( azimuth( aX, aY, bX, bY ) + azimuth( cX, cY, dX, dY ) ) / 2.0;
784
785 bool intersection = false;
786 QgsGeometryUtilsBase::segmentIntersection( aX, aY, bX, bY, cX, cY, dX, dY, pointX, pointY, intersection );
787
788 return intersection;
789}
790
791void QgsGeometryUtilsBase::project( double aX, double aY, double aZ, double distance, double azimuth, double inclination, double &resultX, double &resultY, double &resultZ )
792{
793 const double radsXy = azimuth * M_PI / 180.0;
794 double dx = 0.0, dy = 0.0, dz = 0.0;
795
796 inclination = std::fmod( inclination, 360.0 );
797
798 if ( std::isnan( aZ ) && qgsDoubleNear( inclination, 90.0 ) )
799 {
800 dx = distance * std::sin( radsXy );
801 dy = distance * std::cos( radsXy );
802 }
803 else
804 {
805 const double radsZ = inclination * M_PI / 180.0;
806 dx = distance * std::sin( radsZ ) * std::sin( radsXy );
807 dy = distance * std::sin( radsZ ) * std::cos( radsXy );
808 dz = distance * std::cos( radsZ );
809 }
810
811 resultX = aX + dx;
812 resultY = aY + dy;
813 resultZ = aZ + dz;
814}
815
816bool QgsGeometryUtilsBase::bisector( double aX, double aY, double bX, double bY, double cX, double cY, double &pointX, double &pointY )
817{
818 const double angle = ( azimuth( aX, aY, bX, bY ) + azimuth( aX, aY, cX, cY ) ) / 2.0;
819
820 bool intersection = false;
821 double dX = 0.0, dY = 0.0, dZ = 0.0;
822 project( aX, aY, std::numeric_limits<double>::quiet_NaN(), 1.0, angle, 90.0, dX, dY, dZ );
823 segmentIntersection( bX, bY, cX, cY, aX, aY, dX, dY, pointX, pointY, intersection );
824
825 return intersection;
826}
827
828
830 const double segment1StartX,
831 const double segment1StartY,
832 const double segment1EndX,
833 const double segment1EndY,
834 const double segment2StartX,
835 const double segment2StartY,
836 const double segment2EndX,
837 const double segment2EndY,
838 double epsilon
839)
840{
841 double intersectionX, intersectionY;
842 bool isIntersection;
844 segmentIntersection( segment1StartX, segment1StartY, segment1EndX, segment1EndY, segment2StartX, segment2StartY, segment2EndX, segment2EndY, intersectionX, intersectionY, isIntersection, epsilon, true );
845
846 if ( !isIntersection )
847 {
848 return -1.0;
849 }
850
851 const double dist1ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1StartX, segment1StartY );
852 const double dist1ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1EndX, segment1EndY );
853 const double dist2ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2StartX, segment2StartY );
854 const double dist2ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2EndX, segment2EndY );
855
856 const double dir1X = dist1ToStart > epsilon ? segment1StartX : segment1EndX;
857 const double dir1Y = dist1ToStart > epsilon ? segment1StartY : segment1EndY;
858 const double dir2X = dist2ToStart > epsilon ? segment2StartX : segment2EndX;
859 const double dir2Y = dist2ToStart > epsilon ? segment2StartY : segment2EndY;
860
861 const double angle = QgsGeometryUtilsBase::angleBetweenThreePoints( dir1X, dir1Y, intersectionX, intersectionY, dir2X, dir2Y );
862
863 if ( std::abs( angle ) < epsilon || std::abs( angle - M_PI ) < epsilon )
864 {
865 return -1.0;
866 }
867
868 double workingAngle = angle;
869 if ( workingAngle > M_PI )
870 {
871 workingAngle = 2 * M_PI - workingAngle;
872 }
873
874 const double halfAngle = workingAngle / 2.0;
875 if ( std::abs( std::sin( halfAngle ) ) < epsilon )
876 {
877 return -1.0;
878 }
879
880 const double maxDist1 = ( dist1ToStart > epsilon ) ? dist1ToStart : dist1ToEnd;
881 const double maxDist2 = ( dist2ToStart > epsilon ) ? dist2ToStart : dist2ToEnd;
882
883 const double seg1Length = QgsGeometryUtilsBase::distance2D( segment1StartX, segment1StartY, segment1EndX, segment1EndY );
884 const double seg2Length = QgsGeometryUtilsBase::distance2D( segment2StartX, segment2StartY, segment2EndX, segment2EndY );
885
886 const bool intersectionOnSeg1 = std::abs( ( dist1ToStart + dist1ToEnd ) - seg1Length ) < epsilon;
887 const bool intersectionOnSeg2 = std::abs( ( dist2ToStart + dist2ToEnd ) - seg2Length ) < epsilon;
888
889 double maxDistanceToTangent = std::numeric_limits<double>::max();
890
891 if ( intersectionOnSeg1 )
892 {
893 maxDistanceToTangent = std::min( maxDistanceToTangent, maxDist1 - epsilon );
894 }
895
896 if ( intersectionOnSeg2 )
897 {
898 maxDistanceToTangent = std::min( maxDistanceToTangent, maxDist2 - epsilon );
899 }
900
901 if ( maxDistanceToTangent == std::numeric_limits<double>::max() )
902 {
903 maxDistanceToTangent = std::min( maxDist1, maxDist2 ) - epsilon;
904 }
905
906 if ( maxDistanceToTangent <= 0 )
907 {
908 return -1.0;
909 }
910
911 return maxDistanceToTangent * std::tan( halfAngle );
912}
913
915 const double segment1StartX,
916 const double segment1StartY,
917 const double segment1EndX,
918 const double segment1EndY,
919 const double segment2StartX,
920 const double segment2StartY,
921 const double segment2EndX,
922 const double segment2EndY,
923 const double radius,
924 double *filletPointsX,
925 double *filletPointsY,
926 double *trim1StartX,
927 double *trim1StartY,
928 double *trim1EndX,
929 double *trim1EndY,
930 double *trim2StartX,
931 double *trim2StartY,
932 double *trim2EndX,
933 double *trim2EndY,
934 const double epsilon
935)
936{
937 if ( radius <= 0 )
938 throw QgsInvalidArgumentException( "Radius must be greater than 0." );
939
940 // Find intersection point between segments (or their infinite line extensions)
941 double intersectionX, intersectionY;
942 bool isIntersection;
944 segmentIntersection( segment1StartX, segment1StartY, segment1EndX, segment1EndY, segment2StartX, segment2StartY, segment2EndX, segment2EndY, intersectionX, intersectionY, isIntersection, epsilon, true );
945
946 if ( !isIntersection )
947 {
948 throw QgsInvalidArgumentException( "Segments do not intersect." );
949 }
950
951 // Calculate distances from intersection to all segment endpoints
952 const double dist1ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1StartX, segment1StartY );
953 const double dist1ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1EndX, segment1EndY );
954 const double dist2ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2StartX, segment2StartY );
955 const double dist2ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2EndX, segment2EndY );
956
957 // Determine directional points for angle calculation
958 // These points define the rays extending from the intersection
959 const double dir1X = dist1ToStart > epsilon ? segment1StartX : segment1EndX;
960 const double dir1Y = dist1ToStart > epsilon ? segment1StartY : segment1EndY;
961 const double dir2X = dist2ToStart > epsilon ? segment2StartX : segment2EndX;
962 const double dir2Y = dist2ToStart > epsilon ? segment2StartY : segment2EndY;
963
964 // Calculate the angle between the two rays
965 const double angle = QgsGeometryUtilsBase::angleBetweenThreePoints( dir1X, dir1Y, intersectionX, intersectionY, dir2X, dir2Y );
966
967 // Validate angle - must be meaningful for fillet creation
968 if ( std::abs( angle ) < epsilon || std::abs( angle - M_PI ) < epsilon )
969 {
970 throw QgsInvalidArgumentException( "Parallel or anti-parallel segments." );
971 }
972
973 // Use the interior angle (always ≤ π) for fillet calculations
974 double workingAngle = angle;
975 if ( workingAngle > M_PI )
976 {
977 workingAngle = 2 * M_PI - workingAngle;
978 }
979
980 const double halfAngle = workingAngle / 2.0;
981 if ( std::abs( std::sin( halfAngle ) ) < epsilon )
982 {
983 // Avoid division by very small numbers.
984 throw QgsInvalidArgumentException( "Segment angle near 0 will generate wrong division" );
985 }
986
987 // Calculate distance from intersection to tangent points using trigonometry
988 const double distanceToTangent = radius / std::tan( halfAngle );
989
990 const double maxDist1 = ( dist1ToStart > epsilon ) ? dist1ToStart : dist1ToEnd;
991 const double maxDist2 = ( dist2ToStart > epsilon ) ? dist2ToStart : dist2ToEnd;
992
993 // Check if intersection is actually on the segments (not just on infinite lines)
994 const double seg1Length = QgsGeometryUtilsBase::distance2D( segment1StartX, segment1StartY, segment1EndX, segment1EndY );
995 const double seg2Length = QgsGeometryUtilsBase::distance2D( segment2StartX, segment2StartY, segment2EndX, segment2EndY );
996
997 const bool intersectionOnSeg1 = std::abs( ( dist1ToStart + dist1ToEnd ) - seg1Length ) < epsilon;
998 const bool intersectionOnSeg2 = std::abs( ( dist2ToStart + dist2ToEnd ) - seg2Length ) < epsilon;
999
1000 // Only enforce distance limits if intersection is actually on the segment
1001 // This allows fillets on non-touching segments (like chamfer behavior)
1002 if ( intersectionOnSeg1 && distanceToTangent > maxDist1 - epsilon )
1003 {
1004 throw QgsInvalidArgumentException( "Intersection 1 on segment but too far." );
1005 }
1006
1007 if ( intersectionOnSeg2 && distanceToTangent > maxDist2 - epsilon )
1008 {
1009 throw QgsInvalidArgumentException( "Intersection 2 on segment but too far." );
1010 }
1011
1012 // Calculate tangent points along the rays
1013 double T1x, T1y, T2x, T2y;
1014 QgsGeometryUtilsBase::pointOnLineWithDistance( intersectionX, intersectionY, dir1X, dir1Y, distanceToTangent, T1x, T1y );
1015 QgsGeometryUtilsBase::pointOnLineWithDistance( intersectionX, intersectionY, dir2X, dir2Y, distanceToTangent, T2x, T2y );
1016
1017 // Calculate circle center using angle bisector geometry
1018 const QgsVector v1( dir1X - intersectionX, dir1Y - intersectionY );
1019 const QgsVector v2( dir2X - intersectionX, dir2Y - intersectionY );
1020
1021 // The bisector direction is the normalized sum of the unit vectors
1022 const QgsVector bisectorDirection = ( v1.normalized() + v2.normalized() ).normalized();
1023
1024 // Distance from intersection to circle center
1025 const double centerDistance = radius / std::sin( halfAngle );
1026 const double centerX = intersectionX + bisectorDirection.x() * centerDistance;
1027 const double centerY = intersectionY + bisectorDirection.y() * centerDistance;
1028
1029 // Calculate arc midpoint - the point on the circle that bisects the arc
1030 const QgsVector centerToT1 = QgsVector( T1x - centerX, T1y - centerY ).normalized();
1031 const QgsVector centerToT2 = QgsVector( T2x - centerX, T2y - centerY ).normalized();
1032 const QgsVector midDirection = ( centerToT1 + centerToT2 ).normalized();
1033
1034 const double midX = centerX + midDirection.x() * radius;
1035 const double midY = centerY + midDirection.y() * radius;
1036
1037 // Return three-point arc representation: tangent1, midpoint, tangent2
1038 filletPointsX[0] = T1x;
1039 filletPointsY[0] = T1y;
1040 filletPointsX[1] = midX;
1041 filletPointsY[1] = midY;
1042 filletPointsX[2] = T2x;
1043 filletPointsY[2] = T2y;
1044
1045 // Generate trimmed segments if requested
1046 if ( trim1StartX )
1047 {
1048 if ( dist1ToStart > epsilon )
1049 {
1050 *trim1StartX = segment1StartX;
1051 *trim1StartY = segment1StartY;
1052 }
1053 else
1054 {
1055 *trim1StartX = segment1EndX;
1056 *trim1StartY = segment1EndY;
1057 }
1058 *trim1EndX = T1x;
1059 *trim1EndY = T1y;
1060 }
1061 if ( trim2StartX )
1062 {
1063 if ( dist2ToStart > epsilon )
1064 {
1065 *trim2StartX = segment2StartX;
1066 *trim2StartY = segment2StartY;
1067 }
1068 else
1069 {
1070 *trim2StartX = segment2EndX;
1071 *trim2StartY = segment2EndY;
1072 }
1073 *trim2EndX = T2x;
1074 *trim2EndY = T2y;
1075 }
1076
1077 return true;
1078}
1079
1081 const double segment1StartX,
1082 const double segment1StartY,
1083 const double segment1EndX,
1084 const double segment1EndY,
1085 const double segment2StartX,
1086 const double segment2StartY,
1087 const double segment2EndX,
1088 const double segment2EndY,
1089 double distance1,
1090 double distance2,
1091 double &chamferStartX,
1092 double &chamferStartY,
1093 double &chamferEndX,
1094 double &chamferEndY,
1095 double *trim1StartX,
1096 double *trim1StartY,
1097 double *trim1EndX,
1098 double *trim1EndY,
1099 double *trim2StartX,
1100 double *trim2StartY,
1101 double *trim2EndX,
1102 double *trim2EndY,
1103 const double epsilon
1104)
1105{
1106 // Apply symmetric distance if distance2 is negative
1107 if ( distance2 <= 0 )
1108 distance2 = distance1;
1109
1110 // Only for positive distance
1111 if ( distance1 <= 0 || distance2 <= 0 )
1112 throw QgsInvalidArgumentException( "Negative distances." );
1113
1114 // Find intersection point between segments (or their infinite line extensions)
1115 double intersectionX, intersectionY;
1116 bool isIntersection;
1118 segmentIntersection( segment1StartX, segment1StartY, segment1EndX, segment1EndY, segment2StartX, segment2StartY, segment2EndX, segment2EndY, intersectionX, intersectionY, isIntersection, epsilon, true );
1119
1120 if ( !isIntersection )
1121 {
1122 throw QgsInvalidArgumentException( "Segments do not intersect." );
1123 }
1124
1125 // Apply symmetric distance if distance2 is negative
1126 if ( distance2 < 0 )
1127 distance2 = distance1;
1128
1129 // Calculate distances from intersection to all segment endpoints
1130 const double dist1ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1StartX, segment1StartY );
1131 const double dist1ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment1EndX, segment1EndY );
1132 const double dist2ToStart = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2StartX, segment2StartY );
1133 const double dist2ToEnd = QgsGeometryUtilsBase::distance2D( intersectionX, intersectionY, segment2EndX, segment2EndY );
1134
1135 // Calculate chamfer points along each segment
1136 // Choose the endpoint farthest from intersection as direction
1137 double T1x, T1y;
1138 if ( dist1ToStart > epsilon )
1139 {
1140 QgsGeometryUtilsBase::pointOnLineWithDistance( intersectionX, intersectionY, segment1StartX, segment1StartY, distance1, T1x, T1y );
1141 }
1142 else
1143 {
1144 QgsGeometryUtilsBase::pointOnLineWithDistance( intersectionX, intersectionY, segment1EndX, segment1EndY, distance1, T1x, T1y );
1145 }
1146
1147 double T2x, T2y;
1148 if ( dist2ToStart > epsilon )
1149 {
1150 QgsGeometryUtilsBase::pointOnLineWithDistance( intersectionX, intersectionY, segment2StartX, segment2StartY, distance2, T2x, T2y );
1151 }
1152 else
1153 {
1154 QgsGeometryUtilsBase::pointOnLineWithDistance( intersectionX, intersectionY, segment2EndX, segment2EndY, distance2, T2x, T2y );
1155 }
1156
1157 // Clamp distances to available segment length if necessary
1158 const double distToSeg1Target = ( dist1ToStart > epsilon ) ? dist1ToStart : dist1ToEnd;
1159 const double distToSeg2Target = ( dist2ToStart > epsilon ) ? dist2ToStart : dist2ToEnd;
1160
1161 if ( distance1 > distToSeg1Target - epsilon )
1162 {
1163 if ( dist1ToStart > epsilon )
1164 {
1165 QgsGeometryUtilsBase::pointOnLineWithDistance( intersectionX, intersectionY, segment1StartX, segment1StartY, distToSeg1Target, T1x, T1y );
1166 }
1167 else
1168 {
1169 QgsGeometryUtilsBase::pointOnLineWithDistance( intersectionX, intersectionY, segment1EndX, segment1EndY, distToSeg1Target, T1x, T1y );
1170 }
1171 }
1172
1173 if ( distance2 > distToSeg2Target - epsilon )
1174 {
1175 if ( dist2ToStart > epsilon )
1176 {
1177 QgsGeometryUtilsBase::pointOnLineWithDistance( intersectionX, intersectionY, segment2StartX, segment2StartY, distToSeg2Target, T2x, T2y );
1178 }
1179 else
1180 {
1181 QgsGeometryUtilsBase::pointOnLineWithDistance( intersectionX, intersectionY, segment2EndX, segment2EndY, distToSeg2Target, T2x, T2y );
1182 }
1183 }
1184
1185 chamferStartX = T1x;
1186 chamferStartY = T1y;
1187 chamferEndX = T2x;
1188 chamferEndY = T2y;
1189
1190 // Generate trimmed segments if requested
1191 if ( trim1StartX )
1192 {
1193 if ( dist1ToStart > epsilon )
1194 {
1195 *trim1StartX = segment1StartX;
1196 *trim1StartY = segment1StartY;
1197 *trim1EndX = T1x;
1198 *trim1EndY = T1y;
1199 }
1200 else
1201 {
1202 *trim1StartX = segment1EndX;
1203 *trim1StartY = segment1EndY;
1204 *trim1EndX = T1x;
1205 *trim1EndY = T1y;
1206 }
1207 }
1208 if ( trim2StartX )
1209 {
1210 if ( dist2ToStart > epsilon )
1211 {
1212 *trim2StartX = segment2StartX;
1213 *trim2StartY = segment2StartY;
1214 *trim2EndX = T2x;
1215 *trim2EndY = T2y;
1216 }
1217 else
1218 {
1219 *trim2StartX = segment2EndX;
1220 *trim2StartY = segment2EndY;
1221 *trim2EndX = T2x;
1222 *trim2EndY = T2y;
1223 }
1224 }
1225
1226 return true;
1227}
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:60
double z() const
Returns Z coordinate.
Definition qgsvector3d.h:62
static double dotProduct(const QgsVector3D &v1, const QgsVector3D &v2)
Returns the dot product of two vectors.
double x() const
Returns X coordinate.
Definition qgsvector3d.h:58
void 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:83
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:155
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:163
double x() const
Returns the vector's x-component.
Definition qgsvector.h:146
double length() const
Returns the length of the vector.
Definition qgsvector.h:127
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975