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