KiCad PCB EDA Suite
Loading...
Searching...
No Matches
seg.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2013 CERN
5 * @author Tomasz Wlostowski <[email protected]>
6 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <algorithm> // for min
23#include <geometry/seg.h>
24#include <math/util.h> // for rescale
25#include <math/vector2d.h> // for VECTOR2I, VECTOR2
26#include <math/wide_int.h>
27#include <trigo.h> // for RAD2DEG
28
29template <typename T>
30int sgn( T aVal )
31{
32 return ( T( 0 ) < aVal ) - ( aVal < T( 0 ) );
33}
34
36{
37 // Handle zero-length segments (points) specially.
38 // The Intersects() check below doesn't handle this case correctly because
39 // the cross product with a zero vector is always zero, causing false positives.
40 if( A == B )
41 return aSeg.SquaredDistance( A );
42
43 if( aSeg.A == aSeg.B )
44 return SquaredDistance( aSeg.A );
45
46 if( Intersects( aSeg ) )
47 return 0;
48
49 const VECTOR2I pts[4] =
50 {
51 aSeg.NearestPoint( A ) - A,
52 aSeg.NearestPoint( B ) - B,
53 NearestPoint( aSeg.A ) - aSeg.A,
54 NearestPoint( aSeg.B ) - aSeg.B
55 };
56
58
59 for( int i = 0; i < 4; i++ )
60 m = std::min( m, pts[i].SquaredEuclideanNorm() );
61
62 return m;
63}
64
65
66EDA_ANGLE SEG::Angle( const SEG& aOther ) const
67{
68 EDA_ANGLE thisAngle = EDA_ANGLE( A - B ).Normalize180();
69 EDA_ANGLE otherAngle = EDA_ANGLE( aOther.A - aOther.B ).Normalize180();
70
71 return std::abs( ( thisAngle - otherAngle ).Normalize180() );
72}
73
74
75const VECTOR2I SEG::NearestPoint( const SEG& aSeg ) const
76{
77 if( OPT_VECTOR2I p = Intersect( aSeg ) )
78 return *p;
79
80 const VECTOR2I pts_origin[4] =
81 {
82 aSeg.NearestPoint( A ),
83 aSeg.NearestPoint( B ),
84 NearestPoint( aSeg.A ),
85 NearestPoint( aSeg.B )
86 };
87
88
89 const VECTOR2I* pts_out[4] =
90 {
91 &A,
92 &B,
93 &pts_origin[2],
94 &pts_origin[3]
95 };
96
97 const ecoord pts_dist[4] =
98 {
99 ( pts_origin[0] - A ).SquaredEuclideanNorm(),
100 ( pts_origin[1] - B ).SquaredEuclideanNorm(),
101 ( pts_origin[2] - aSeg.A ).SquaredEuclideanNorm(),
102 ( pts_origin[3] - aSeg.B ).SquaredEuclideanNorm()
103 };
104
105 int min_i = 0;
106
107 for( int i = 0; i < 4; i++ )
108 {
109 if( pts_dist[i] < pts_dist[min_i] )
110 min_i = i;
111 }
112
113 return *pts_out[min_i];
114}
115
116
117bool SEG::NearestPoints( const SEG& aSeg, VECTOR2I& aPtA, VECTOR2I& aPtB, int64_t& aDistSq ) const
118{
119 if( OPT_VECTOR2I p = Intersect( aSeg ) )
120 {
121 aPtA = aPtB = *p;
122 aDistSq = 0;
123
124 return true;
125 }
126
127 const VECTOR2I pts_origin[4] =
128 {
129 aSeg.NearestPoint( A ),
130 aSeg.NearestPoint( B ),
131 NearestPoint( aSeg.A ),
132 NearestPoint( aSeg.B )
133 };
134
135 const VECTOR2I* pts_a_out[4] =
136 {
137 &A,
138 &B,
139 &pts_origin[2],
140 &pts_origin[3]
141 };
142
143 const VECTOR2I* pts_b_out[4] =
144 {
145 &pts_origin[0],
146 &pts_origin[1],
147 &aSeg.A,
148 &aSeg.B
149 };
150
151 const ecoord pts_dist[4] =
152 {
153 ( pts_origin[0] - A ).SquaredEuclideanNorm(),
154 ( pts_origin[1] - B ).SquaredEuclideanNorm(),
155 ( pts_origin[2] - aSeg.A ).SquaredEuclideanNorm(),
156 ( pts_origin[3] - aSeg.B ).SquaredEuclideanNorm()
157 };
158
159 int min_i = 0;
160
161 for( int i = 0; i < 4; i++ )
162 {
163 if( pts_dist[i] < pts_dist[min_i] )
164 min_i = i;
165 }
166
167 aPtA = *pts_a_out[min_i];
168 aPtB = *pts_b_out[min_i];
169 aDistSq = pts_dist[min_i];
170
171 return true;
172}
173
174
175bool SEG::checkCollinearOverlap( const SEG& aSeg, bool useXAxis, bool aIgnoreEndpoints, VECTOR2I* aPt ) const
176{
177 // Extract coordinates based on the chosen axis
178 int seg1_start, seg1_end, seg2_start, seg2_end;
179 int coord1_start, coord1_end; // For calculating other axis coordinate
180
181 if( useXAxis )
182 {
183 seg1_start = A.x; seg1_end = B.x;
184 seg2_start = aSeg.A.x; seg2_end = aSeg.B.x;
185 coord1_start = A.y; coord1_end = B.y;
186 }
187 else
188 {
189 seg1_start = A.y; seg1_end = B.y;
190 seg2_start = aSeg.A.y; seg2_end = aSeg.B.y;
191 coord1_start = A.x; coord1_end = B.x;
192 }
193
194 // Find segment ranges on the projection axis
195 const int seg1_min = std::min( seg1_start, seg1_end );
196 const int seg1_max = std::max( seg1_start, seg1_end );
197 const int seg2_min = std::min( seg2_start, seg2_end );
198 const int seg2_max = std::max( seg2_start, seg2_end );
199
200 // Check for overlap
201 const bool overlaps = seg1_max >= seg2_min && seg2_max >= seg1_min;
202 if( !overlaps )
203 return false;
204
205 // Check if intersection is only at endpoints when aIgnoreEndpoints is true
206 if( aIgnoreEndpoints )
207 {
208 // Calculate overlap region
209 const int overlap_start = std::max( seg1_min, seg2_min );
210 const int overlap_end = std::min( seg1_max, seg2_max );
211
212 // If overlap region has zero length, segments only touch at endpoint
213 if( overlap_start == overlap_end )
214 {
215 // Check if this endpoint touching involves actual segment endpoints
216 // (not just projected endpoints due to min/max calculation)
217 bool isEndpointTouch = false;
218
219 // Check if the touch point corresponds to actual segment endpoints
220 if( overlap_start == seg1_min || overlap_start == seg1_max )
221 {
222 // Touch point is at seg1's endpoint, check if it's also at seg2's endpoint
223 if( overlap_start == seg2_min || overlap_start == seg2_max )
224 {
225 isEndpointTouch = true;
226 }
227 }
228
229 if( isEndpointTouch )
230 return false; // Ignore endpoint-only intersection
231 }
232 }
233
234 // Calculate intersection point if requested
235 if( aPt )
236 {
237 // Find midpoint of overlap region
238 const int overlap_start = std::max( seg1_min, seg2_min );
239 const int overlap_end = std::min( seg1_max, seg2_max );
240 const int intersection_proj = ( overlap_start + overlap_end ) / 2;
241
242 // Calculate corresponding coordinate on the other axis
243 int intersection_other;
244 if( seg1_end != seg1_start )
245 {
246 // Use this segment's line equation to find other coordinate
247 intersection_other = coord1_start + static_cast<int>(
248 rescale( intersection_proj - seg1_start, coord1_end - coord1_start, seg1_end - seg1_start ) );
249 }
250 else
251 {
252 // Degenerate segment (point) or perpendicular to projection axis
253 intersection_other = coord1_start;
254 }
255
256 // Set result based on projection axis
257 if( useXAxis )
258 *aPt = VECTOR2I( intersection_proj, intersection_other );
259 else
260 *aPt = VECTOR2I( intersection_other, intersection_proj );
261 }
262
263 return true;
264}
265
266
267bool SEG::intersects( const SEG& aSeg, bool aIgnoreEndpoints, bool aLines, VECTOR2I* aPt ) const
268{
269 // Quick rejection: check if segment bounding boxes overlap
270 // (Skip for line mode since infinite lines can intersect anywhere)
271 if( !aLines )
272 {
273 const int this_min_x = std::min( A.x, B.x );
274 const int this_max_x = std::max( A.x, B.x );
275 const int this_min_y = std::min( A.y, B.y );
276 const int this_max_y = std::max( A.y, B.y );
277
278 const int other_min_x = std::min( aSeg.A.x, aSeg.B.x );
279 const int other_max_x = std::max( aSeg.A.x, aSeg.B.x );
280 const int other_min_y = std::min( aSeg.A.y, aSeg.B.y );
281 const int other_max_y = std::max( aSeg.A.y, aSeg.B.y );
282
283 if( this_max_x < other_min_x || other_max_x < this_min_x ||
284 this_max_y < other_min_y || other_max_y < this_min_y )
285 {
286 return false;
287 }
288 }
289
290 // Calculate direction vectors and offset vector using VECTOR2 operations
291 // Using parametric form: P₁ = A + t*dir1, P₂ = aSeg.A + s*dir2
292 const VECTOR2L dir1 = VECTOR2L( B ) - A; // direction vector e
293 const VECTOR2L dir2 = VECTOR2L( aSeg.B ) - aSeg.A; // direction vector f
294 const VECTOR2L offset = VECTOR2L( aSeg.A ) - A; // offset vector ac
295 const ecoord determinant = dir2.Cross( dir1 );
296
297 // Handle parallel/collinear case
298 if( determinant == 0 )
299 {
300 // Check if lines are collinear (not just parallel) using cross product
301 // Lines are collinear if offset vector is also parallel to direction vector
302 const ecoord collinear_test = dir1.Cross( offset );
303
304 if( collinear_test != 0 )
305 return false; // Parallel but not collinear
306
307 // Lines are collinear - for infinite lines, they always intersect
308 if( aLines )
309 {
310 // For infinite collinear lines, intersection point is ambiguous
311 // Use the midpoint between the two segment start points as a reasonable choice
312 if( aPt )
313 {
314 // If aSeg is degenerate (point), use its start point
315 if( aSeg.A == aSeg.B )
316 {
317 *aPt = aSeg.A;
318 }
319 else if( A == B )
320 { // If this segment is degenerate (point), use its start point
321 *aPt = A;
322 }
323 else
324 {
325 const VECTOR2I midpoint = ( A + aSeg.A ) / 2;
326 *aPt = midpoint;
327 }
328 }
329 return true;
330 }
331
332 // For segments, check overlap using the axis with larger coordinate range
333 const bool use_x_axis = std::abs( dir1.x ) >= std::abs( dir1.y );
334 return checkCollinearOverlap( aSeg, use_x_axis, aIgnoreEndpoints, aPt );
335 }
336
337 // param2_num = f × ac (parameter for second segment: s = p/d)
338 // param1_num = e × ac (parameter for first segment: t = q/d)
339 const ecoord param2_num = dir2.Cross( offset );
340 const ecoord param1_num = dir1.Cross( offset );
341
342 // For segments (not infinite lines), check if intersection is within both segments
343 if( !aLines )
344 {
345 // Parameters must be in [0,1] for intersection within segments
346 // Since we're comparing t = q/d and s = p/d to [0,1], we need to handle sign of d
347 if( determinant > 0 )
348 {
349 // d > 0: check 0 ≤ q ≤ d and 0 ≤ p ≤ d
350 if( param1_num < 0 || param1_num > determinant ||
351 param2_num < 0 || param2_num > determinant )
352 return false;
353 }
354 else
355 {
356 // d < 0: check d ≤ q ≤ 0 and d ≤ p ≤ 0
357 if( param1_num > 0 || param1_num < determinant ||
358 param2_num > 0 || param2_num < determinant )
359 return false;
360 }
361
362 // Optionally exclude endpoint intersections (when segments share vertices)
363 if( aIgnoreEndpoints &&
364 ( param1_num == 0 || param1_num == determinant ) &&
365 ( param2_num == 0 || param2_num == determinant ) )
366 {
367 return false;
368 }
369 }
370
371 if( aPt )
372 {
373 // Use parametric equation: intersection = aSeg.A + (q/d) * f
374 const VECTOR2L scaled_dir2( rescale( param1_num, dir2.x, determinant ),
375 rescale( param1_num, dir2.y, determinant ) );
376 const VECTOR2L result = VECTOR2L( aSeg.A ) + scaled_dir2;
377
378 // Verify result fits in coordinate type range
379 constexpr ecoord max_coord = std::numeric_limits<VECTOR2I::coord_type>::max();
380 constexpr ecoord min_coord = std::numeric_limits<VECTOR2I::coord_type>::min();
381
382 if( result.x > max_coord || result.x < min_coord ||
383 result.y > max_coord || result.y < min_coord )
384 {
385 return false; // Intersection exists but coordinates overflow
386 }
387
388 *aPt = VECTOR2I( static_cast<int>( result.x ), static_cast<int>( result.y ) );
389 }
390
391 return true;
392}
393
394
395bool SEG::Intersects( const SEG& aSeg ) const
396{
397 return intersects( aSeg );
398}
399
400
401OPT_VECTOR2I SEG::Intersect( const SEG& aSeg, bool aIgnoreEndpoints, bool aLines ) const
402{
403 VECTOR2I ip;
404
405 if( intersects( aSeg, aIgnoreEndpoints, aLines, &ip ) )
406 return ip;
407 else
408 return OPT_VECTOR2I();
409}
410
411
412bool SEG::IntersectsLine( double aSlope, double aOffset, VECTOR2I& aIntersection ) const
413{
414 const VECTOR2L segA( A );
415 const VECTOR2L segB( B );
416 const VECTOR2L segDir = segB - segA;
417
418 // Handle vertical segment case
419 if( segDir.x == 0 )
420 {
421 // Vertical segment: x = A.x, find y on the line
422 const double intersect_y = aSlope * A.x + aOffset;
423
424 // Compare before rounding. The line is unbounded, so a miss lands anywhere, and
425 // narrowing it first would fault on a value this is about to discard
426 const int seg_min_y = std::min( A.y, B.y );
427 const int seg_max_y = std::max( A.y, B.y );
428
429 if( intersect_y >= seg_min_y && intersect_y <= seg_max_y )
430 {
431 aIntersection = VECTOR2I( A.x, KiROUND( intersect_y ) );
432 return true;
433 }
434 return false;
435 }
436
437 const VECTOR2L lineDir( 1000, static_cast<ecoord>( aSlope * 1000 ) );
438 const ecoord cross_product = segDir.Cross( lineDir );
439
440 if( cross_product == 0 )
441 {
442 // Parallel lines - check if segment lies on the line
443 const double expected_y = aSlope * A.x + aOffset;
444 const double diff = std::abs( A.y - expected_y );
445
446 if( diff < 0.5 )
447 {
448 // Collinear: segment lies on the line, return midpoint
449 aIntersection = ( A + B ) / 2;
450 return true;
451 }
452
453 return false; // Parallel but not collinear
454 }
455
456 // Find intersection using parametric equations
457 // Segment: P = segA + t * segDir
458 // Line: y = aSlope * x + aOffset
459 //
460 // At intersection: segA.y + t * segDir.y = aSlope * (segA.x + t * segDir.x) + aOffset
461 // Solving for t: t = (aSlope * segA.x + aOffset - segA.y) / (segDir.y - aSlope * segDir.x)
462
463 const double numerator = aSlope * segA.x + aOffset - segA.y;
464 const double denominator = segDir.y - aSlope * segDir.x;
465
466 const double t = numerator / denominator;
467
468 // Check if intersection is within segment bounds
469 if( t >= 0.0 && t <= 1.0 )
470 {
471 aIntersection = KiROUND( segA.x + t * segDir.x, segA.y + t * segDir.y );
472 return true;
473 }
474
475 return false;
476}
477
478
480{
481 VECTOR2I slope( B - A );
482 VECTOR2I endPoint = slope.Perpendicular() + aP;
483
484 return SEG( aP, endPoint );
485}
486
487
488SEG SEG::ParallelSeg( const VECTOR2I& aP ) const
489{
490 VECTOR2I slope( B - A );
491 VECTOR2I endPoint = slope + aP;
492
493 return SEG( aP, endPoint );
494}
495
496
497bool SEG::Collide( const SEG& aSeg, int aClearance, int* aActual ) const
498{
499 // Handle negative clearance
500 if( aClearance < 0 )
501 {
502 if( aActual )
503 *aActual = 0;
504
505 return false;
506 }
507
508 // Handle zero-length segments (points) specially.
509 // The intersects() check below doesn't handle this case correctly because
510 // the cross product with a zero vector is always zero, causing false positives.
511 if( A == B )
512 {
513 int dist = aSeg.Distance( A );
514
515 if( aActual )
516 *aActual = dist;
517
518 return dist == 0 || dist < aClearance;
519 }
520
521 if( aSeg.A == aSeg.B )
522 {
523 int dist = Distance( aSeg.A );
524
525 if( aActual )
526 *aActual = dist;
527
528 return dist == 0 || dist < aClearance;
529 }
530
531 // Check for exact intersection first
532 if( intersects( aSeg, false, false ) )
533 {
534 if( aActual )
535 *aActual = 0;
536
537 return true;
538 }
539
540 const ecoord clearance_sq = static_cast<ecoord>( aClearance ) * aClearance;
541 ecoord min_dist_sq = VECTOR2I::ECOORD_MAX;
542
543 auto checkDistance = [&]( ecoord dist, ecoord& min_dist ) -> bool
544 {
545 if( dist == 0 )
546 {
547 if( aActual )
548 *aActual = 0;
549
550 return true;
551 }
552
553 min_dist = std::min( min_dist, dist );
554 return false; // Continue checking
555 };
556
557 // There are 4 points to check: start and end of this segment, and
558 // start and end of the other segment.
559 if( checkDistance( SquaredDistance( aSeg.A ), min_dist_sq ) ||
560 checkDistance( SquaredDistance( aSeg.B ), min_dist_sq ) ||
561 checkDistance( aSeg.SquaredDistance( A ), min_dist_sq ) ||
562 checkDistance( aSeg.SquaredDistance( B ), min_dist_sq ) )
563 {
564 return true;
565 }
566
567 if( min_dist_sq < clearance_sq )
568 {
569 if( aActual )
570 *aActual = static_cast<int>( isqrt( min_dist_sq ) );
571
572 return true;
573 }
574
575 if( aActual )
576 *aActual = static_cast<int>( isqrt( min_dist_sq ) );
577
578 return false;
579}
580
581
582bool SEG::Contains( const VECTOR2I& aP, int aSqDistanceThreshold ) const
583{
584 /* Warning. This code does work in most - but not all of the cases.
585 Take the segment (0,0) - (10,0) and the point (9, 1). With the distance threshold of 3,
586 the point is assumed as contained in by the segment:
587
588 (9,1)\
589 (0,0) ------ (10, 0)
590
591 I've made the distance threshold configurable (using the default of value of 3),
592 to not break any existing code that relies on the current rounding behaviour, but we need to
593 think of more accurate (and degeneracy-free) solution. */
594
595 return SquaredDistance( aP ) <= aSqDistanceThreshold;
596}
597
598
599const VECTOR2I SEG::NearestPoint( const VECTOR2I& aP ) const
600{
601 // Inlined for performance reasons
602 VECTOR2L d;
603 d.x = static_cast<int64_t>( B.x ) - A.x;
604 d.y = static_cast<int64_t>( B.y ) - A.y;
605
606 ecoord l_squared( d.x * d.x + d.y * d.y );
607
608 if( l_squared == 0 )
609 return A;
610
611 // Inlined for performance reasons
612 VECTOR2L pa;
613 pa.x = static_cast<int64_t>( aP.x ) - A.x;
614 pa.y = static_cast<int64_t>( aP.y ) - A.y;
615
616 ecoord t = d.Dot( pa );
617
618 if( t < 0 )
619 return A;
620 else if( t > l_squared )
621 return B;
622
623 ecoord xp = rescale( t, (ecoord) d.x, l_squared );
624 ecoord yp = rescale( t, (ecoord) d.y, l_squared );
625
626 return VECTOR2<ecoord>( A.x + xp, A.y + yp );
627}
628
629
630const VECTOR2I SEG::ReflectPoint( const VECTOR2I& aP ) const
631{
632 VECTOR2I d = B - A;
633 VECTOR2I::extended_type l_squared = d.Dot( d );
634 VECTOR2I::extended_type t = d.Dot( aP - A );
636
637 if( !l_squared )
638 {
639 c = aP;
640 }
641 else
642 {
643 c.x = A.x + rescale( t, static_cast<VECTOR2I::extended_type>( d.x ), l_squared );
644 c.y = A.y + rescale( t, static_cast<VECTOR2I::extended_type>( d.y ), l_squared );
645 }
646
647 return VECTOR2<ecoord>( 2 * c.x - aP.x, 2 * c.y - aP.y );
648}
649
650
652{
653 VECTOR2I d = B - A;
654 ecoord l_squared = d.Dot( d );
655
656 if( l_squared == 0 )
657 return A;
658
659 ecoord t = d.Dot( aP - A );
660
661 ecoord xp = rescale( t, ecoord{ d.x }, l_squared );
662 ecoord yp = rescale( t, ecoord{ d.y }, l_squared );
663
664 return VECTOR2<ecoord>( A.x + xp, A.y + yp );
665}
666
667
668int SEG::Distance( const SEG& aSeg ) const
669{
670 return int( isqrt( SquaredDistance( aSeg ) ) );
671}
672
673
674int SEG::Distance( const VECTOR2I& aP ) const
675{
676 return int( isqrt( SquaredDistance( aP ) ) );
677}
678
679
681{
682 VECTOR2<ecoord> ab( ecoord( B.x ) - A.x, ecoord( B.y ) - A.y );
683 VECTOR2<ecoord> ap( ecoord( aP.x ) - A.x, ecoord( aP.y ) - A.y );
684
685 ecoord e = ap.Dot( ab );
686
687 if( e <= 0 )
688 return ap.SquaredEuclideanNorm();
689
691
692 if( e >= f )
693 {
694 VECTOR2<ecoord> bp( ecoord( aP.x ) - B.x, ecoord( aP.y ) - B.y );
695
696 return bp.Dot( bp );
697 }
698
699 // Lagrange identity keeps the small result free of cancellation
700 const double cross = ToDouble( CrossWide( ab, ap ) );
701 const double g = cross * cross / double( f );
702
703 if( !( g >= 0 ) || g > static_cast<double>( std::numeric_limits<ecoord>::max() ) )
704 return 0;
705
706 return KiROUND<double, ecoord>( g );
707}
708
709
710int SEG::LineDistance( const VECTOR2I& aP, bool aDetermineSide ) const
711{
712 ecoord p = ecoord{ A.y } - B.y;
713 ecoord q = ecoord{ B.x } - A.x;
714 ecoord r = -p * A.x - q * A.y;
715 ecoord l = p * p + q * q;
716 ecoord det = p * aP.x + q * aP.y + r;
717 ecoord dist_sq = 0;
718
719 if( l > 0 )
720 {
721 dist_sq = rescale( det, det, l );
722 }
723
724 ecoord dist = isqrt( dist_sq );
725
726 return static_cast<int>( aDetermineSide ? sgn( det ) * dist : std::abs( dist ) );
727}
728
729
730bool SEG::mutualDistanceSquared( const SEG& aSeg, ecoord& aD1, ecoord& aD2 ) const
731{
732 SEG a( *this );
733 SEG b( aSeg );
734
735 if( a.SquaredLength() < b.SquaredLength() )
736 std::swap(a, b);
737
738 ecoord p = ecoord{ a.A.y } - a.B.y;
739 ecoord q = ecoord{ a.B.x } - a.A.x;
740 ecoord r = -p * a.A.x - q * a.A.y;
741
742 ecoord l = p * p + q * q;
743
744 if( l == 0 )
745 return false;
746
747 ecoord det1 = p * b.A.x + q * b.A.y + r;
748 ecoord det2 = p * b.B.x + q * b.B.y + r;
749
750 ecoord dsq1 = rescale( det1, det1, l );
751 ecoord dsq2 = rescale( det2, det2, l );
752
753 aD1 = sgn( det1 ) * dsq1;
754 aD2 = sgn( det2 ) * dsq2;
755
756 return true;
757}
758
759bool SEG::ApproxCollinear( const SEG& aSeg, int aDistanceThreshold ) const
760{
761 ecoord thresholdSquared = Square( aDistanceThreshold );
762 ecoord d1_sq, d2_sq;
763
764 if( !mutualDistanceSquared( aSeg, d1_sq, d2_sq ) )
765 return false;
766
767 return std::abs( d1_sq ) <= thresholdSquared && std::abs( d2_sq ) <= thresholdSquared;
768}
769
770
771bool SEG::ApproxParallel( const SEG& aSeg, int aDistanceThreshold ) const
772{
773 ecoord thresholdSquared = Square( aDistanceThreshold );
774 ecoord d1_sq, d2_sq;
775
776 if( ! mutualDistanceSquared( aSeg, d1_sq, d2_sq ) )
777 return false;
778
779 return std::abs( d1_sq - d2_sq ) <= thresholdSquared;
780}
781
782
783bool SEG::ApproxPerpendicular( const SEG& aSeg ) const
784{
785 SEG perp = PerpendicularSeg( A );
786
787 return aSeg.ApproxParallel( perp );
788}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
EDA_ANGLE Normalize180()
Definition eda_angle.h:273
const VECTOR2I ReflectPoint(const VECTOR2I &aP) const
Reflect a point using this segment as axis.
Definition seg.cpp:630
VECTOR2I A
Definition seg.h:45
int LineDistance(const VECTOR2I &aP, bool aDetermineSide=false) const
Return the closest Euclidean distance between point aP and the line defined by the ends of segment (t...
Definition seg.cpp:710
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:35
bool IntersectsLine(double aSlope, double aOffset, VECTOR2I &aIntersection) const
Check if this segment intersects a line defined by slope aSlope and offset aOffset.
Definition seg.cpp:412
bool intersects(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false, VECTOR2I *aPt=nullptr) const
Definition seg.cpp:267
VECTOR2I::extended_type ecoord
Definition seg.h:40
VECTOR2I B
Definition seg.h:46
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:599
bool Intersects(const SEG &aSeg) const
Definition seg.cpp:395
bool checkCollinearOverlap(const SEG &aSeg, bool useXAxis, bool aIgnoreEndpoints, VECTOR2I *aPt) const
Definition seg.cpp:175
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Definition seg.cpp:401
static SEG::ecoord Square(int a)
Definition seg.h:119
bool Collide(const SEG &aSeg, int aClearance, int *aActual=nullptr) const
Definition seg.cpp:497
bool ApproxParallel(const SEG &aSeg, int aDistanceThreshold=1) const
Definition seg.cpp:771
SEG()
Create an empty (0, 0) segment.
Definition seg.h:51
SEG ParallelSeg(const VECTOR2I &aP) const
Compute a segment parallel to this one, passing through point aP.
Definition seg.cpp:488
ecoord SquaredLength() const
Definition seg.h:345
bool ApproxPerpendicular(const SEG &aSeg) const
Definition seg.cpp:783
bool ApproxCollinear(const SEG &aSeg, int aDistanceThreshold=1) const
Definition seg.cpp:759
bool mutualDistanceSquared(const SEG &aSeg, ecoord &aD1, ecoord &aD2) const
Definition seg.cpp:730
bool NearestPoints(const SEG &aSeg, VECTOR2I &aPtA, VECTOR2I &aPtB, int64_t &aDistSq) const
Compute closest points between this segment and aSeg.
Definition seg.cpp:117
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:668
bool Contains(const SEG &aSeg) const
Definition seg.h:321
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition seg.cpp:651
SEG PerpendicularSeg(const VECTOR2I &aP) const
Compute a segment perpendicular to this one, passing through point aP.
Definition seg.cpp:479
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
Definition seg.cpp:66
Define a general 2D-vector/point.
Definition vector2d.h:67
constexpr extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
Definition vector2d.h:559
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition vector2d.h:312
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:72
VECTOR2_TRAITS< int32_t >::extended_type extended_type
Definition vector2d.h:69
constexpr VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
Definition vector2d.h:335
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
Definition vector2d.h:567
static coord2_t cross_product(const VECTOR2I &O, const VECTOR2I &A, const VECTOR2I &B)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
int sgn(T aVal)
Definition seg.cpp:30
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:35
wxString result
Test unit parsing edge cases and error handling.
T rescale(T aNumerator, T aValue, T aDenominator)
Scale a number (value) by rational (numerator/denominator).
Definition util.h:160
T isqrt(T aX)
Exact floor of the square root of an integer.
Definition util.h:206
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:709
128-bit integers for exact products of 64-bit coordinate deltas.
double ToDouble(KI_INT128 aValue)
Definition wide_int.h:94
constexpr KI_INT128 CrossWide(const VECTOR2L &aA, const VECTOR2L &aB)
Exact aA.x * aB.y - aA.y * aB.x.
Definition wide_int.h:104