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
465 // Compare before rounding. The line is unbounded, so a miss lands anywhere, and
466 // narrowing it first would fault on a value this is about to discard
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 >= seg_min_y && intersect_y <= seg_max_y )
471 {
472 aIntersection = VECTOR2I( A.x, KiROUND( intersect_y ) );
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, int aSqDistanceThreshold ) const
624{
625 /* Warning. This code does work in most - but not all of the cases.
626 Take the segment (0,0) - (10,0) and the point (9, 1). With the distance threshold of 3,
627 the point is assumed as contained in by the segment:
628
629 (9,1)\
630 (0,0) ------ (10, 0)
631
632 I've made the distance threshold configurable (using the default of value of 3),
633 to not break any existing code that relies on the current rounding behaviour, but we need to
634 think of more accurate (and degeneracy-free) solution. */
635
636 return SquaredDistance( aP ) <= aSqDistanceThreshold;
637}
638
639
640const VECTOR2I SEG::NearestPoint( const VECTOR2I& aP ) const
641{
642 // Inlined for performance reasons
643 VECTOR2L d;
644 d.x = static_cast<int64_t>( B.x ) - A.x;
645 d.y = static_cast<int64_t>( B.y ) - A.y;
646
647 ecoord l_squared( d.x * d.x + d.y * d.y );
648
649 if( l_squared == 0 )
650 return A;
651
652 // Inlined for performance reasons
653 VECTOR2L pa;
654 pa.x = static_cast<int64_t>( aP.x ) - A.x;
655 pa.y = static_cast<int64_t>( aP.y ) - A.y;
656
657 ecoord t = d.Dot( pa );
658
659 if( t < 0 )
660 return A;
661 else if( t > l_squared )
662 return B;
663
664 ecoord xp = rescale( t, (ecoord) d.x, l_squared );
665 ecoord yp = rescale( t, (ecoord) d.y, l_squared );
666
667 return VECTOR2<ecoord>( A.x + xp, A.y + yp );
668}
669
670
671const VECTOR2I SEG::ReflectPoint( const VECTOR2I& aP ) const
672{
673 VECTOR2I d = B - A;
674 VECTOR2I::extended_type l_squared = d.Dot( d );
675 VECTOR2I::extended_type t = d.Dot( aP - A );
677
678 if( !l_squared )
679 {
680 c = aP;
681 }
682 else
683 {
684 c.x = A.x + rescale( t, static_cast<VECTOR2I::extended_type>( d.x ), l_squared );
685 c.y = A.y + rescale( t, static_cast<VECTOR2I::extended_type>( d.y ), l_squared );
686 }
687
688 return VECTOR2<ecoord>( 2 * c.x - aP.x, 2 * c.y - aP.y );
689}
690
691
693{
694 VECTOR2I d = B - A;
695 ecoord l_squared = d.Dot( d );
696
697 if( l_squared == 0 )
698 return A;
699
700 ecoord t = d.Dot( aP - A );
701
702 ecoord xp = rescale( t, ecoord{ d.x }, l_squared );
703 ecoord yp = rescale( t, ecoord{ d.y }, l_squared );
704
705 return VECTOR2<ecoord>( A.x + xp, A.y + yp );
706}
707
708
709int SEG::Distance( const SEG& aSeg ) const
710{
711 return int( isqrt( SquaredDistance( aSeg ) ) );
712}
713
714
715int SEG::Distance( const VECTOR2I& aP ) const
716{
717 return int( isqrt( SquaredDistance( aP ) ) );
718}
719
720
722{
723 VECTOR2<ecoord> ab( ecoord( B.x ) - A.x, ecoord( B.y ) - A.y );
724 VECTOR2<ecoord> ap( ecoord( aP.x ) - A.x, ecoord( aP.y ) - A.y );
725
726 ecoord e = ap.Dot( ab );
727
728 if( e <= 0 )
729 return ap.SquaredEuclideanNorm();
730
732
733 if( e >= f )
734 {
735 VECTOR2<ecoord> bp( ecoord( aP.x ) - B.x, ecoord( aP.y ) - B.y );
736
737 return bp.Dot( bp );
738 }
739
740 const double g = ap.SquaredEuclideanNorm() - ( double( e ) * e ) / f;
741
742 // The only way g can be negative is if there was a rounding error since
743 // e is the projection of aP onto ab and therefore cannot be greater than
744 // the length of ap and f is guaranteed to be greater than e, meaning
745 // e * e / f cannot be greater than ap.SquaredEuclideanNorm()
746 if( g < 0 || g > static_cast<double>( std::numeric_limits<ecoord>::max() ) )
747 return 0;
748
749 return KiROUND<double, ecoord>( g );
750}
751
752
753int SEG::LineDistance( const VECTOR2I& aP, bool aDetermineSide ) const
754{
755 ecoord p = ecoord{ A.y } - B.y;
756 ecoord q = ecoord{ B.x } - A.x;
757 ecoord r = -p * A.x - q * A.y;
758 ecoord l = p * p + q * q;
759 ecoord det = p * aP.x + q * aP.y + r;
760 ecoord dist_sq = 0;
761
762 if( l > 0 )
763 {
764 dist_sq = rescale( det, det, l );
765 }
766
767 ecoord dist = isqrt( dist_sq );
768
769 return static_cast<int>( aDetermineSide ? sgn( det ) * dist : std::abs( dist ) );
770}
771
772
773bool SEG::mutualDistanceSquared( const SEG& aSeg, ecoord& aD1, ecoord& aD2 ) const
774{
775 SEG a( *this );
776 SEG b( aSeg );
777
778 if( a.SquaredLength() < b.SquaredLength() )
779 std::swap(a, b);
780
781 ecoord p = ecoord{ a.A.y } - a.B.y;
782 ecoord q = ecoord{ a.B.x } - a.A.x;
783 ecoord r = -p * a.A.x - q * a.A.y;
784
785 ecoord l = p * p + q * q;
786
787 if( l == 0 )
788 return false;
789
790 ecoord det1 = p * b.A.x + q * b.A.y + r;
791 ecoord det2 = p * b.B.x + q * b.B.y + r;
792
793 ecoord dsq1 = rescale( det1, det1, l );
794 ecoord dsq2 = rescale( det2, det2, l );
795
796 aD1 = sgn( det1 ) * dsq1;
797 aD2 = sgn( det2 ) * dsq2;
798
799 return true;
800}
801
802bool SEG::ApproxCollinear( const SEG& aSeg, int aDistanceThreshold ) const
803{
804 ecoord thresholdSquared = Square( aDistanceThreshold );
805 ecoord d1_sq, d2_sq;
806
807 if( !mutualDistanceSquared( aSeg, d1_sq, d2_sq ) )
808 return false;
809
810 return std::abs( d1_sq ) <= thresholdSquared && std::abs( d2_sq ) <= thresholdSquared;
811}
812
813
814bool SEG::ApproxParallel( const SEG& aSeg, int aDistanceThreshold ) const
815{
816 ecoord thresholdSquared = Square( aDistanceThreshold );
817 ecoord d1_sq, d2_sq;
818
819 if( ! mutualDistanceSquared( aSeg, d1_sq, d2_sq ) )
820 return false;
821
822 return std::abs( d1_sq - d2_sq ) <= thresholdSquared;
823}
824
825
826bool SEG::ApproxPerpendicular( const SEG& aSeg ) const
827{
828 SEG perp = PerpendicularSeg( A );
829
830 return aSeg.ApproxParallel( perp );
831}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:995
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:671
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:753
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:640
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:814
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:826
bool ApproxCollinear(const SEG &aSeg, int aDistanceThreshold=1) const
Definition seg.cpp:802
bool mutualDistanceSquared(const SEG &aSeg, ecoord &aD1, ecoord &aD2) const
Definition seg.cpp:773
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:709
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:692
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:411
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