KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_shape_arc.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 The KiCad Developers, see AUTHORS.TXT for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21#include <boost/test/data/test_case.hpp>
22
23#include <limits>
24
26#include <geometry/shape_arc.h>
29
31#include <qa_utils/numeric.h>
32
33#include "geom_test_utils.h"
34
35BOOST_AUTO_TEST_SUITE( ShapeArc )
36
37
52
59static void CheckArcGeom( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps, const int aSynErrIU = 1 )
60{
61 // Angular error - note this can get quite large for very small arcs,
62 // as the integral position rounding has a relatively greater effect
63 const double angle_tol_deg = 2.0;
64
65 // Position error - rounding to nearest integer
66 const int pos_tol = 1;
67
69 ( aProps.m_start_point )( aProps.m_start_point )( pos_tol ) );
70
72 ( aArc.GetP1() )( aProps.m_end_point )( pos_tol ) );
73
75 ( aArc.GetCenter() )( aProps.m_center_point )( aSynErrIU ) );
76
78 ( aArc.GetCentralAngle().AsDegrees() )( aProps.m_center_angle )( 360.0 )( angle_tol_deg ) );
79
81 ( aArc.GetStartAngle().AsDegrees() )( aProps.m_start_angle )( 360.0 )( angle_tol_deg ) );
82
84 ( aArc.GetEndAngle().AsDegrees() )( aProps.m_end_angle )( 360.0 )( angle_tol_deg ) );
85
87 ( aArc.GetRadius() )( aProps.m_radius )( aSynErrIU ) );
88
89 // Angle normalization contracts
90 BOOST_TEST( aArc.GetStartAngle().AsDegrees() >= 0.0 );
91 BOOST_TEST( aArc.GetStartAngle().AsDegrees() <= 360.0 );
92
93 BOOST_TEST( aArc.GetEndAngle().AsDegrees() >= 0.0 );
94 BOOST_TEST( aArc.GetEndAngle().AsDegrees() <= 360.0 );
95
96 BOOST_TEST( aArc.GetCentralAngle().AsDegrees() >= -360.0 );
97 BOOST_TEST( aArc.GetCentralAngle().AsDegrees() <= 360.0 );
98
100 const SEG chord = aArc.GetChord();
101
103 ( chord.A )( aProps.m_start_point )( pos_tol ) );
104
106 ( chord.B )( aProps.m_end_point )( pos_tol ) );
107
109 BOOST_CHECK_EQUAL( aArc.IsSolid(), true );
110
112 ( aArc.BBox() )( aProps.m_bbox )( pos_tol ) );
113
115}
116
117
124static void CheckArc( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps, const int aSynErrIU = 1 )
125{
126 // Check the original arc
127 CheckArcGeom( aArc, aProps, aSynErrIU );
128
129 // Test the Clone function (also tests copy-ctor)
130 std::unique_ptr<SHAPE> new_shape{ aArc.Clone() };
131
132 BOOST_REQUIRE_EQUAL( new_shape->Type(), SH_ARC );
133
134 SHAPE_ARC* new_arc = dynamic_cast<SHAPE_ARC*>( new_shape.get() );
135
136 BOOST_REQUIRE( new_arc != nullptr );
137
139 CheckArcGeom( *new_arc, aProps, aSynErrIU );
140}
141
146{
147 auto arc = SHAPE_ARC();
148
149 BOOST_CHECK_EQUAL( arc.GetWidth(), 0 );
150
151 static ARC_PROPERTIES null_props{
152 { 0, 0 },
153 { 0, 0 },
154 { 0, 0 },
155 0,
156 0,
157 0,
158 0,
159 };
160
161 CheckArc( arc, null_props );
162}
163
164
174
186
187
188static const std::vector<ARC_SME_CASE> arc_sme_cases = {
189 {
190 "S(-100,0), M(0,100), E(100,0)",
191 {
192 { -100, 0 },
193 { 0, 100 },
194 { 100, 0 },
195 },
196 0,
197 {
198 { 0, 0 },
199 { -100, 0 },
200 { 100, 0 },
201 180,
202 180,
203 0,
204 100,
205 { { -100, 0 }, { 200, 100 } },
206 },
207 },
208 {
209 "S(100,0), M(0,100), E(-100,0) (reversed)",
210 {
211 { 100, 0 },
212 { 0, 100 },
213 { -100, 0 },
214 },
215 0,
216 {
217 { 0, 0 },
218 { 100, 0 },
219 { -100, 0 },
220 -180,
221 0,
222 180,
223 100,
224 { { -100, 0 }, { 200, 100 } },
225 },
226 },
227 {
228 // This data has a midpoint not exactly at the midway point of the arc.
229 // This should be corrected by the constructor.
230 // The mid point should be at about (-71, -71) for a 270 degree arc, with the
231 // bottom right quadrant open.
232 "S(100,0), M(-100,0), E(0,100) (bad midpoint)",
233 {
234 { 100, 0 },
235 { -100, 0 },
236 { 0, 100 },
237 },
238 0,
239 {
240 { 0, 0 },
241 { 100, 0 },
242 { 0, 100 },
243 -270,
244 0,
245 90,
246 100,
247 { { -100, -100 }, { 200, 200 } },
248 },
249 }
250};
251
252
253BOOST_DATA_TEST_CASE( BasicSMEGeom, boost::unit_test::data::make( arc_sme_cases ), c )
254{
255 const SHAPE_ARC this_arc{
256 c.m_geom.m_start_point,
257 c.m_geom.m_mid_point,
258 c.m_geom.m_end_point,
259 c.m_width,
260 };
261
262 CheckArc( this_arc, c.m_properties );
263}
264
265
275
276
288
289
290static const std::vector<ARC_CPA_CASE> arc_cases = {
291 {
292 "C(0,0) 114 + 360 degree",
293 {
294 { 0, 0 },
295 { -306451, 687368 },
296 360,
297 },
298 0,
299 {
300 { 0, 0 },
301 { -306451, 687368 },
302 { -306451, 687368 },
303 360,
304 113.95929,
305 113.95929,
306 752587,
307 { { -752587, -752587 }, { 1505174, 1505174 } },
308 },
309 },
310 {
311 "C(0,0) 180 + 360 degree",
312 {
313 { 0, 0 },
314 { -100, 0 },
315 360,
316 },
317 0,
318 {
319 { 0, 0 },
320 { -100, 0 },
321 { -100, 0 },
322 360,
323 180,
324 180,
325 100,
326 { { -100, -100 }, { 200, 200 } },
327 },
328 },
329 {
330 "C(0,0) 180 + 90 degree",
331 {
332 { 0, 0 },
333 { -100, 0 },
334 90,
335 },
336 0,
337 {
338 { 0, 0 },
339 { -100, 0 },
340 { 0, -100 },
341 90,
342 180,
343 270,
344 100,
345 { { -100, -100 }, { 100, 100 } },
346 },
347 },
348 {
349 "C(100,200) 0 - 30 degree",
350 {
351 { 100, 200 },
352 { 300, 200 },
353 -30,
354 },
355 0,
356 {
357 { 100, 200 },
358 { 300, 200 },
359 { 273, 100 }, // 200 * sin(30) = 100, 200* cos(30) = 173
360 -30,
361 0,
362 330,
363 200,
364 { { 273, 100 }, { 27, 100 } },
365 },
366 },
367 {
368 // This is a "fan shape" which includes the top quadrant point,
369 // so it exercises the bounding box code (centre and end points
370 // do not contain the top quadrant)
371 "C(0,0) 30 + 120 degree",
372 {
373 { 0, 0 },
374 { 17320, 10000 },
375 120,
376 },
377 0,
378 {
379 { 0, 0 },
380 { 17320, 10000 },
381 { -17320, 10000 }, // 200 * sin(30) = 100, 200* cos(30) = 173
382 120,
383 30,
384 150,
385 20000,
386 // bbox defined by: centre, top quadrant point, two endpoints
387 { { -17320, 10000 }, { 17320 * 2, 10000 } },
388 },
389 },
390 {
391 // An arc that covers three quadrant points (L/R, bottom)
392 "C(0,0) 150 + 240 degree",
393 {
394 { 0, 0 },
395 { -17320, 10000 },
396 240,
397 },
398 0,
399 {
400 { 0, 0 },
401 { -17320, 10000 },
402 { 17320, 10000 },
403 240,
404 150,
405 30,
406 20000,
407 // bbox defined by: L/R quads, bottom quad and start/end
408 { { -20000, -20000 }, { 40000, 30000 } },
409 },
410 },
411 {
412 // Same as above but reverse direction
413 "C(0,0) 30 - 300 degree",
414 {
415 { 0, 0 },
416 { 17320, 10000 },
417 -240,
418 },
419 0,
420 {
421 { 0, 0 },
422 { 17320, 10000 },
423 { -17320, 10000 },
424 -240,
425 30,
426 150,
427 20000,
428 // bbox defined by: L/R quads, bottom quad and start/end
429 { { -20000, -20000 }, { 40000, 30000 } },
430 },
431 },
432};
433
434
435BOOST_DATA_TEST_CASE( BasicCPAGeom, boost::unit_test::data::make( arc_cases ), c )
436{
437 const SHAPE_ARC this_arc{
438 c.m_geom.m_center_point,
439 c.m_geom.m_start_point,
440 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ),
441 c.m_width,
442 };
443
444 CheckArc( this_arc, c.m_properties );
445}
446
447
448
458
459
471
472
473static const std::vector<ARC_TTR_CASE> arc_ttr_cases = {
474 {
475 "90 degree segments intersecting",
476 {
477 { 0, 0, 0, 1000 },
478 { 0, 0, 1000, 0 },
479 1000,
480 },
481 0,
482 {
483 { 1000, 1000 },
484 { 0, 1000 }, //start on first segment
485 { 1000, 0 }, //end on second segment
486 90, //positive angle due to start/end
487 180,
488 270,
489 1000,
490 { { 0, 0 }, { 1000, 1000 } },
491 }
492 },
493 {
494 "45 degree segments intersecting",
495 {
496 { 0, 0, 0, 1000 },
497 { 0, 0, 1000, 1000 },
498 1000,
499 },
500 0,
501 {
502 { 1000, 2414 },
503 { 0, 2414 }, //start on first segment
504 { 1707, 1707 }, //end on second segment
505 135, //positive angle due to start/end
506 180,
507 315,
508 1000,
509 { { 0, 1414 }, { 1707, 1000 } },
510 }
511 },
512 {
513 "135 degree segments intersecting",
514 {
515 { 0, 0, 0, 1000 },
516 { 0, 0, 1000, -1000 },
517 1000,
518 },
519 0,
520 {
521 { 1000, 414 },
522 { 0, 414 }, //start on first segment ( radius * tan(45 /2) )
523 { 293, -293 }, //end on second segment (radius * 1-cos(45)) )
524 45, //positive angle due to start/end
525 180,
526 225,
527 1000,
528 { { 0, -293 }, { 293, 707 } },
529 }
530 }
531
532
533};
534
535
536BOOST_DATA_TEST_CASE( BasicTTRGeom, boost::unit_test::data::make( arc_ttr_cases ), c )
537{
538 for( int testCase = 0; testCase < 8; ++testCase )
539 {
540 SEG seg1 = c.m_geom.m_segment_1;
541 SEG seg2 = c.m_geom.m_segment_2;
542 ARC_PROPERTIES props = c.m_properties;
543
544 if( testCase > 3 )
545 {
546 //Swap input segments.
547 seg1 = c.m_geom.m_segment_2;
548 seg2 = c.m_geom.m_segment_1;
549
550 //The result should swap start and end points and invert the angles:
551 props.m_end_point = c.m_properties.m_start_point;
552 props.m_start_point = c.m_properties.m_end_point;
553 props.m_start_angle = c.m_properties.m_end_angle;
554 props.m_end_angle = c.m_properties.m_start_angle;
555 props.m_center_angle = -c.m_properties.m_center_angle;
556 }
557
558 //Test all combinations of start and end points for the segments
559 if( ( testCase % 4 ) == 1 || ( testCase % 4 ) == 3 )
560 {
561 //Swap start and end points for seg1
562 VECTOR2I temp = seg1.A;
563 seg1.A = seg1.B;
564 seg1.B = temp;
565 }
566
567 if( ( testCase % 4 ) == 2 || ( testCase % 4 ) == 3 )
568 {
569 //Swap start and end points for seg2
570 VECTOR2I temp = seg2.A;
571 seg2.A = seg2.B;
572 seg2.B = temp;
573 }
574
575 const auto this_arc = SHAPE_ARC{ seg1, seg2,
576 c.m_geom.m_radius, c.m_width };
577
578 // Error of 4 IU permitted for the center and radius calculation
580 }
581}
582
583
587struct ARC_START_END_CENTER
588{
589 VECTOR2I m_start;
590 VECTOR2I m_end;
591 VECTOR2I m_center;
592};
593
594
596{
598 ARC_START_END_CENTER m_geom;
599
602
605};
606
607
608
609static const std::vector<ARC_SEC_CASE> arc_sec_cases = {
610 { "180 deg, clockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } }, true, { 50, -50 } },
611 { "180 deg, anticlockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } }, false, { 50, 50 } },
612 { "180 deg flipped, clockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } }, true, { 50, 50 } },
613 { "180 deg flipped, anticlockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } }, false, { 50, -50 } },
614 { "90 deg, clockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } }, true, { -71, 71 } },
615 { "90 deg, anticlockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } }, false, { 71, -71 } },
616};
617
618
619BOOST_DATA_TEST_CASE( BasicSECGeom, boost::unit_test::data::make( arc_sec_cases ), c )
620{
621 VECTOR2I start = c.m_geom.m_start;
622 VECTOR2I end = c.m_geom.m_end;
623 VECTOR2I center = c.m_geom.m_center;
624 bool cw = c.m_clockwise;
625
627 this_arc.ConstructFromStartEndCenter( start, end, center, cw );
628
629 BOOST_CHECK_EQUAL( this_arc.GetArcMid(), c.m_expected_mid );
630}
631
632
642
643static const std::vector<ARC_CICLE_COLLIDE_CASE> arc_circle_collide_cases = {
644 { " Issue 20336, large arc", { { 183000000, 65710001}, {150496913, 147587363},{116291153, 66406583}}, 2000000 / 2, {116300000, 133100000}, 300000, true, 53319 }
645};
646
647
648BOOST_DATA_TEST_CASE( CollideCircle, boost::unit_test::data::make( arc_circle_collide_cases ), c )
649{
650 SHAPE_ARC arc( c.m_geom.m_start_point, c.m_geom.m_mid_point, c.m_geom.m_end_point, 0 );
651 SHAPE_CIRCLE circle( c.m_circle_center, c.m_circle_radius );
652
653 // Test a zero width arc (distance should equal the clearance)
654 BOOST_TEST_CONTEXT( "Test Clearance" )
655 {
656 int dist = -1;
657 BOOST_CHECK_EQUAL( arc.Collide( &circle, c.m_arc_clearance, &dist ), c.m_exp_result );
658 BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
659 }
660
661 // Test by changing the width of the arc (distance should equal zero)
662 BOOST_TEST_CONTEXT( "Test Width" )
663 {
664 int dist = -1;
665 arc.SetWidth( c.m_arc_clearance * 2 );
666 BOOST_CHECK_EQUAL( arc.Collide( &circle, 0, &dist ), c.m_exp_result );
667
668 if( c.m_exp_result )
669 BOOST_CHECK_EQUAL( dist, 0 );
670 else
671 BOOST_CHECK_EQUAL( dist, -1 );
672 }
673}
674
675
684
685
686static const std::vector<ARC_PT_COLLIDE_CASE> arc_pt_collide_cases = {
687 { " 270deg, 0 cl, 0 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 100, 0 }, true, 0 },
688 { " 270deg, 0 cl, 90 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 0, 100 }, true, 0 },
689 { " 270deg, 0 cl, 180 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { -100, 0 }, true, 0 },
690 { " 270deg, 0 cl, 270 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 0, -100 }, true, 0 },
691 { " 270deg, 0 cl, 45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 71, 71 }, true, 0 },
692 { " 270deg, 0 cl, -45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 71, -71 }, false, -1 },
693 { "-270deg, 0 cl, 0 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 100, 0 }, true, 0 },
694 { "-270deg, 0 cl, 90 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 0, 100 }, true, 0 },
695 { "-270deg, 0 cl, 180 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { -100, 0 }, true, 0 },
696 { "-270deg, 0 cl, 270 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 0, -100 }, true, 0 },
697 { "-270deg, 0 cl, 45 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 71, 71 }, false, -1 },
698 { "-270deg, 0 cl, -45 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 71, -71 }, true, 0 },
699 { " 270deg, 5 cl, 0 deg, 5 pos X", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 105, 0 }, true, 5 },
700 { " 270deg, 5 cl, 0 deg, 5 pos Y", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 100, -5 }, true, 5 },
701 { " 270deg, 5 cl, 90 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, 105 }, true, 5 },
702 { " 270deg, 5 cl, 180 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { -105, 0 }, true, 5 },
703 { " 270deg, 5 cl, 270 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, -105 }, true, 5 },
704 { " 270deg, 5 cl, 0 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 105, 0 }, true, 5 },
705 { " 270deg, 5 cl, 90 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, 105 }, true, 5 },
706 { " 270deg, 5 cl, 180 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { -105, 0 }, true, 5 },
707 { " 270deg, 5 cl, 270 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, -105 }, true, 5 },
708 { " 270deg, 5 cl, 45 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 74, 75 }, true, 5 }, // 74.246, -74.246
709 { " 270deg, 5 cl, -45 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 74, -75 }, false, -1 }, //74.246, -74.246
710 { " 270deg, 5 cl, 45 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 67, 67 }, true, 5 }, // 67.17, 67.17
711 { " 270deg, 5 cl, -45 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 67, -67 }, false, -1 }, // 67.17, -67.17
712 { " 270deg, 4 cl, 0 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 105, 0 }, false, -1 },
713 { " 270deg, 4 cl, 90 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 0, 105 }, false, -1 },
714 { " 270deg, 4 cl, 180 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { -105, 0 }, false, -1 },
715 { " 270deg, 4 cl, 270 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 0, -105 }, false, -1 },
716 { " 90deg, 0 cl, 0 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 71, -71 }, true, 0 },
717 { " 90deg, 0 cl, 45 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 100, 0 }, true, 0 },
718 { " 90deg, 0 cl, 90 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 71, 71 }, true, 0 },
719 { " 90deg, 0 cl, 135 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 0, -100 }, false, -1 },
720 { " 90deg, 0 cl, -45 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 0, 100 }, false, -1 },
721 { " -90deg, 0 cl, 0 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 71, -71 }, true, 0 },
722 { " -90deg, 0 cl, 45 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 100, 0 }, true, 0 },
723 { " -90deg, 0 cl, 90 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 71, 71 }, true, 0 },
724 { " -90deg, 0 cl, 135 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 0, -100 }, false, -1 },
725 { " -90deg, 0 cl, -45 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 0, 100 }, false, -1 },
726 { "issue 11358 collide",
727 { { 119888000, 60452000 }, { 120904000, 60452000 }, 360.0 },
728 0,
729 { 120395500, 59571830 },
730 true,
731 0 },
732 { "issue 11358 dist",
733 { { 119888000, 60452000 }, { 120904000, 60452000 }, 360.0 },
734 100,
735 { 118872050, 60452000 },
736 true,
737 50 },
738};
739
740
741BOOST_DATA_TEST_CASE( CollidePt, boost::unit_test::data::make( arc_pt_collide_cases ), c )
742{
743 SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
744 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
745
746 // Test a zero width arc (distance should equal the clearance)
747 BOOST_TEST_CONTEXT( "Test Clearance" )
748 {
749 int dist = -1;
750 BOOST_CHECK_EQUAL( arc.Collide( c.m_point, c.m_arc_clearance, &dist ),
751 c.m_exp_result );
752 BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
753 }
754
755 // Test by changing the width of the arc (distance should equal zero)
756 BOOST_TEST_CONTEXT( "Test Width" )
757 {
758 int dist = -1;
759 arc.SetWidth( c.m_arc_clearance * 2 );
760 BOOST_CHECK_EQUAL( arc.Collide( c.m_point, 0, &dist ), c.m_exp_result );
761
762 if( c.m_exp_result )
763 BOOST_CHECK_EQUAL( dist, 0 );
764 else
765 BOOST_CHECK_EQUAL( dist, -1 );
766 }
767}
768
778
779
780static const std::vector<ARC_SEG_COLLIDE_CASE> arc_seg_collide_cases = {
781 { "0 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 100, 0 }, { 50, 0 } }, true, 0, { 100, 0 } },
782 { "90 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 0, 100 }, { 0, 50 } }, true, 0, { 0, 100 } },
783 { "180 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { -100, 0 }, { -50, 0 } }, true, 0, { -100, 0 } },
784 { "270 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 0, -100 }, { 0, -50 } }, true, 0, { 0, -100 } },
785 { "45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 71, 71 }, { 35, 35 } }, true, 0, { 70, 70 } },
786 { "-45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 71, -71 }, { 35, -35 } }, false, -1, { 0, 0 } },
787 { "seg inside arc start", { { 0, 0 }, { 71, -71 }, 90.0 },
788 10, { { 90, 0 }, { -35, 0 } }, true, 10, { 100, 0 } },
789 { "seg inside arc end", { { 0, 0 }, { 71, -71 }, 90.0 },
790 10, { { -35, 0 }, { 90, 0 } }, true, 10, { 100, 0 } },
791 { "large diameter arc", { { 172367922, 82282076 }, { 162530000, 92120000 }, -45.0 },
792 433300, { { 162096732, 92331236 }, { 162096732, 78253268 } }, true, 433268, { 162530000, 92120000 } },
793 { "upside down collide", { { 26250000, 16520000 }, { 28360000, 16520000 }, 90.0 },
794 0, { { 27545249, 18303444 }, { 27545249, 18114500 } }, true, 0, { 27545249, 18185662 } }
795};
796
797
798BOOST_DATA_TEST_CASE( CollideSeg, boost::unit_test::data::make( arc_seg_collide_cases ), c )
799{
800 SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
801 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
802
803 // Test a zero width arc (distance should equal the clearance)
804 BOOST_TEST_CONTEXT( "Test Clearance" )
805 {
806 int dist = -1;
807 BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, c.m_arc_clearance, &dist ),
808 c.m_exp_result );
809 BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
810 }
811
812 // Test by changing the width of the arc (distance should equal zero)
813 BOOST_TEST_CONTEXT( "Test Width" )
814 {
815 int dist = -1;
816 arc.SetWidth( c.m_arc_clearance * 2 );
817 BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, 0, &dist ), c.m_exp_result );
818
819 if( c.m_exp_result )
820 BOOST_CHECK_EQUAL( dist, 0 );
821 else
822 BOOST_CHECK_EQUAL( dist, -1 );
823 }
824
825 BOOST_TEST_CONTEXT( "Test Collide Point" )
826 {
827 VECTOR2I collide_point;
828 int dist = -1;
829
830 if( c.m_exp_result )
831 {
832 arc.Collide( c.m_seg, c.m_arc_clearance, &dist, &collide_point );
833 BOOST_CHECK_EQUAL( collide_point, c.m_collide_point );
834 }
835 }
836}
837
839{
840 // Coordinates and dimensions in millimeters
843 double m_start_x;
844 double m_start_y;
846 double m_width;
847
849 {
850 SHAPE_ARC arc( VECTOR2D( pcbIUScale.mmToIU( m_center_x ), pcbIUScale.mmToIU( m_center_y ) ),
851 VECTOR2D( pcbIUScale.mmToIU( m_start_x ), pcbIUScale.mmToIU( m_start_y ) ),
853
854 return arc;
855 }
856};
857
858
866
867
868static const std::vector<ARC_ARC_COLLIDE_CASE> arc_arc_collide_cases = {
869 { "case 1: No intersection",
870 { 73.843527, 74.355869, 71.713528, 72.965869, -76.36664803, 0.2 },
871 { 71.236473, 74.704131, 73.366472, 76.094131, -76.36664803, 0.2 },
872 0,
873 false },
874 { "case 2: No intersection",
875 { 82.542335, 74.825975, 80.413528, 73.435869, -76.4, 0.2 },
876 { 76.491192, 73.839894, 78.619999, 75.23, -76.4, 0.2 },
877 0,
878 false },
879 { "case 3: No intersection",
880 { 89.318807, 74.810106, 87.19, 73.42, -76.4, 0.2 },
881 { 87.045667, 74.632941, 88.826472, 75.794131, -267.9, 0.2 },
882 0,
883 false },
884 { "case 4: Co-centered not intersecting",
885 { 94.665667, 73.772941, 96.446472, 74.934131, -267.9, 0.2 },
886 { 94.665667, 73.772941, 93.6551, 73.025482, -255.5, 0.2 },
887 0,
888 false },
889 { "case 5: Not intersecting, but end points very close",
890 { 72.915251, 80.493054, 73.570159, 81.257692, -260.5, 0.2 },
891 { 73.063537, 82.295989, 71.968628, 81.581351, -255.5, 0.2 },
892 0,
893 false },
894 { "case 6: Coincident centers, colliding due to arc thickness",
895 { 79.279991, 80.67988, 80.3749, 81.394518, -255.5, 0.3 },
896 { 79.279991, 80.67988, 80.3749, 81.694518, -255.5, 0.3 },
897 0,
898 true },
899 { "case 7: Single intersection",
900 { 88.495265, 81.766089, 90.090174, 82.867869, -255.5, 0.2 },
901 { 86.995265, 81.387966, 89.090174, 82.876887, -255.5, 0.2 },
902 0,
903 true },
904 { "case 8: Double intersection",
905 { 96.149734, 81.792126, 94.99, 83.37, -347.2, 0.2 },
906 { 94.857156, 81.240589, 95.91, 83.9, -288.5, 0.2 },
907 0,
908 true },
909 { "case 9: Endpoints within arc width",
910 { 72.915251, 86.493054, 73.970159, 87.257692, -260.5, 0.2 },
911 { 73.063537, 88.295989, 71.968628, 87.581351, -255.5, 0.2 },
912 0,
913 true },
914 { "case 10: Endpoints close, outside, no collision",
915 { 78.915251, 86.393054, 79.970159, 87.157692, 99.5, 0.2 },
916 { 79.063537, 88.295989, 77.968628, 87.581351, -255.5, 0.2 },
917 0,
918 false },
919 { "case 11: Endpoints close, inside, collision due to arc width",
920 { 85.915251, 86.993054, 86.970159, 87.757692, 99.5, 0.2 },
921 { 86.063537, 88.295989, 84.968628, 87.581351, -255.5, 0.2 },
922 0,
923 true },
924 { "case 12: Simulated differential pair length-tuning",
925 { 94.6551, 88.296, 95.6551, 88.296, 90.0, 0.1 },
926 { 94.6551, 88.296, 95.8551, 88.296, 90.0, 0.1 },
927 0.1,
928 false },
929 { "case 13: One arc fully enclosed in other, non-concentric",
930 { 73.77532, 93.413654, 75.70532, 93.883054, 60.0, 0.1 },
931 { 73.86532, 93.393054, 75.86532, 93.393054, 90.0, 0.3 },
932 0,
933 true },
934 { "case 14: One arc fully enclosed in other, concentric",
935 { 79.87532, 93.413654, 81.64532, 94.113054, 60.0, 0.1 },
936 { 79.87532, 93.413654, 81.86532, 93.393054, 90.0, 0.3 },
937 0,
938 true },
939 { "case 15: Arcs separated by clearance",
940 { 303.7615, 149.9252, 303.695968, 149.925237, 90.0262, 0.065 },
941 { 303.6345, 149.2637, 303.634523, 148.85619, 89.9957, 0.065 },
942 0.15,
943 false },
944};
945
946
947BOOST_DATA_TEST_CASE( CollideArc, boost::unit_test::data::make( arc_arc_collide_cases ), c )
948{
949 SHAPE_ARC arc1( c.m_arc1.GenerateArc() );
950 SHAPE_ARC arc2( c.m_arc2.GenerateArc() );
951
952
953 SHAPE_LINE_CHAIN arc1_slc( c.m_arc1.GenerateArc() );
954 arc1_slc.SetWidth( 0 );
955
956 SHAPE_LINE_CHAIN arc2_slc( c.m_arc2.GenerateArc() );
957 arc2_slc.SetWidth( 0 );
958
959 int actual = 0;
961
962 SHAPE* arc1_sh = &arc1;
966
967 bool result_arc_to_arc = arc1_sh->Collide( arc2_sh, pcbIUScale.mmToIU( c.m_clearance ),
968 &actual, &location );
969
970 // For arc to chain collisions, we need to re-calculate the clearances because the
971 // SHAPE_LINE_CHAIN is zero width
972 int clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc2.GetWidth() / 2 );
973
975
976 clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc1.GetWidth() / 2 );
978
979 clearance = ( arc1.GetWidth() / 2 ) + ( arc2.GetWidth() / 2 );
981
986}
987
988
989BOOST_AUTO_TEST_CASE( CollideArcToShapeLineChain )
990{
991 SHAPE_ARC arc( VECTOR2I( 206000000, 140110000 ), VECTOR2I( 201574617, 139229737 ),
992 VECTOR2I( 197822958, 136722959 ), 250000 );
993
994 SHAPE_LINE_CHAIN lc( { VECTOR2I( 159600000, 142500000 ), VECTOR2I( 159600000, 142600000 ),
995 VECTOR2I( 166400000, 135800000 ), VECTOR2I( 166400000, 111600000 ),
996 VECTOR2I( 190576804, 111600000 ), VECTOR2I( 192242284, 113265480 ),
997 VECTOR2I( 192255720, 113265480 ), VECTOR2I( 203682188, 124691948 ),
998 VECTOR2I( 203682188, 140332188 ), VECTOR2I( 206000000, 142650000 ) },
999 false );
1000
1001
1002
1003 SHAPE* arc_sh = &arc;
1004 SHAPE* lc_sh = &lc;
1005
1006 BOOST_CHECK_EQUAL( arc_sh->Collide( &lc, 100000 ), true );
1007 BOOST_CHECK_EQUAL( lc_sh->Collide( &arc, 100000 ), true );
1008
1009 SEG seg( VECTOR2I( 203682188, 124691948 ), VECTOR2I( 203682188, 140332188 ) );
1010 BOOST_CHECK_EQUAL( arc.Collide( seg, 0 ), true );
1011}
1012
1013
1014BOOST_AUTO_TEST_CASE( CollideArcToPolygonApproximation )
1015{
1016 SHAPE_ARC arc( VECTOR2I( 73843527, 74355869 ), VECTOR2I( 71713528, 72965869 ),
1017 EDA_ANGLE( -76.36664803, DEGREES_T ), 1000000 );
1018
1019 // Create a polyset approximation from the arc - error outside (simulating the zone filler)
1020 SHAPE_POLY_SET arcBuffer;
1021 int clearance = ( arc.GetWidth() * 3 ) / 2;
1022 int polygonApproximationError = SHAPE_ARC::DefaultAccuracyForPCB();
1023
1024 TransformArcToPolygon( arcBuffer, arc.GetP0(), arc.GetArcMid(), arc.GetP1(),
1025 arc.GetWidth() + 2 * clearance,
1026 polygonApproximationError, ERROR_OUTSIDE );
1027
1028 BOOST_REQUIRE_EQUAL( arcBuffer.OutlineCount(), 1 );
1029 BOOST_CHECK_EQUAL( arcBuffer.HoleCount( 0 ), 0 );
1030
1031 // Make a reasonably large rectangular outline around the arc shape
1032 BOX2I arcbbox = arc.BBox( clearance * 4 );
1033
1034 SHAPE_LINE_CHAIN zoneOutline( { arcbbox.GetPosition(),
1035 arcbbox.GetPosition() + VECTOR2I( arcbbox.GetWidth(), 0 ),
1036 arcbbox.GetEnd(),
1037 arcbbox.GetEnd() - VECTOR2I( arcbbox.GetWidth(), 0 )
1038 },
1039 true );
1040
1041 // Create a synthetic "zone fill" polygon
1042 SHAPE_POLY_SET zoneFill;
1043 zoneFill.AddOutline( zoneOutline );
1044 zoneFill.AddHole( arcBuffer.Outline( 0 ) );
1045 zoneFill.CacheTriangulation();
1046
1047 int actual = 0;
1049 int epsilon = polygonApproximationError / 10;
1050
1051 BOOST_CHECK_EQUAL( zoneFill.Collide( &arc, clearance + epsilon, &actual, &location ), true );
1052
1053 BOOST_CHECK_EQUAL( zoneFill.Collide( &arc, clearance - epsilon, &actual, &location ), false );
1054}
1055
1056
1061
1062
1072bool ArePolylineEndPointsNearCircle( const SHAPE_LINE_CHAIN& aPolyline, const VECTOR2I& aCentre,
1073 int aRad, int aTolerance )
1074{
1075 std::vector<VECTOR2I> points;
1076
1077 for( int i = 0; i < aPolyline.PointCount(); ++i )
1078 {
1079 points.push_back( aPolyline.CPoint( i ) );
1080 }
1081
1082 return GEOM_TEST::ArePointsNearCircle( points, aCentre, aRad, aTolerance );
1083}
1084
1085
1095bool ArePolylineMidPointsNearCircle( const SHAPE_LINE_CHAIN& aPolyline, const VECTOR2I& aCentre,
1096 int aRad, int aTolerance )
1097{
1098 std::vector<VECTOR2I> points;
1099
1100 for( int i = 0; i < aPolyline.PointCount() - 1; ++i )
1101 {
1102 const VECTOR2I mid_pt = ( aPolyline.CPoint( i ) + aPolyline.CPoint( i + 1 ) ) / 2;
1103 points.push_back( mid_pt );
1104 }
1105
1106 return GEOM_TEST::ArePointsNearCircle( points, aCentre, aRad, aTolerance );
1107}
1108
1109
1110const std::vector<ARC_TO_POLYLINE_CASE> ArcToPolyline_cases{
1111 {
1112 "Zero rad",
1113 {
1114 { 0, 0 },
1115 { 0, 0 },
1116 180,
1117 },
1118 },
1119 {
1120 "Semicircle",
1121 {
1122 { 0, 0 },
1123 { -1000000, 0 },
1124 180,
1125 },
1126 },
1127 {
1128 // check that very small circles don't fall apart and that reverse angles
1129 // work too
1130 "Extremely small semicircle",
1131 {
1132 { 0, 0 },
1133 { -1000, 0 },
1134 -180,
1135 },
1136 },
1137 {
1138 // Make sure it doesn't only work for "easy" angles
1139 "Non-round geometry",
1140 {
1141 { 0, 0 },
1142 { 1234567, 0 },
1143 42.22,
1144 },
1145 },
1146};
1147
1148
1149BOOST_DATA_TEST_CASE( ArcToPolyline, boost::unit_test::data::make( ArcToPolyline_cases ), c )
1150{
1151 const int width = 0;
1152
1153 // Note: do not expect accuracies around 1 to work. We use integers internally so we're
1154 // liable to rounding errors. In PCBNew accuracy defaults to 5000 and we don't recommend
1155 // anything lower than 1000 (for performance reasons).
1156 const int accuracy = 100;
1157 const int epsilon = 1;
1158
1159 const SHAPE_ARC this_arc{ c.m_geom.m_center_point, c.m_geom.m_start_point,
1160 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ), width };
1161
1162 const SHAPE_LINE_CHAIN chain = this_arc.ConvertToPolyline( accuracy );
1163
1164 BOOST_TEST_MESSAGE( "Polyline has " << chain.PointCount() << " points" );
1165
1166 // Start point (exactly) where expected
1167 BOOST_CHECK_EQUAL( chain.CPoint( 0 ), c.m_geom.m_start_point );
1168
1169 // End point (exactly) where expected
1170 BOOST_CHECK_EQUAL( chain.CLastPoint(), this_arc.GetP1() );
1171
1172 int radius = ( c.m_geom.m_center_point - c.m_geom.m_start_point ).EuclideanNorm();
1173
1174 // Other points within accuracy + epsilon (for rounding) of where they should be
1176 ( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
1177
1179 ( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
1180}
1181
1182
1191BOOST_AUTO_TEST_CASE( TransformShallowArcToPolygon )
1192{
1193 SHAPE_POLY_SET buffer;
1194
1195 // Create an arc where the mid-point is only slightly off the start-end line.
1196 // This creates a very large radius arc that previously caused integer overflow.
1197 const VECTOR2I start( 0, 0 );
1198 const VECTOR2I end( 10000000, 0 ); // 10mm chord length
1199 const VECTOR2I mid( 5000000, 5 ); // Mid-point only 5nm off the line
1200
1201 const int width = 250000; // 0.25mm track width
1202 const int aError = 5000; // Default error tolerance
1203
1204 // This should not crash or produce invalid geometry
1205 TransformArcToPolygon( buffer, start, mid, end, width, aError, ERROR_INSIDE );
1206
1207 // Should produce at least one outline
1208 BOOST_CHECK( buffer.OutlineCount() >= 1 );
1209
1210 // The outline should be valid (closed, has points)
1211 if( buffer.OutlineCount() > 0 )
1212 {
1213 const SHAPE_LINE_CHAIN& outline = buffer.COutline( 0 );
1214 BOOST_CHECK( outline.IsClosed() );
1215 BOOST_CHECK( outline.PointCount() >= 3 );
1216
1217 // The bounding box should be reasonable (roughly the track width around the chord)
1218 BOX2I bbox = outline.BBox();
1219 BOOST_CHECK( bbox.GetWidth() <= end.x + width * 2 );
1220 BOOST_CHECK( bbox.GetHeight() <= width * 2 + 100 ); // Allow some tolerance
1221 }
1222}
1223
1224
1229BOOST_AUTO_TEST_CASE( TransformVeryShallowArcToPolygon )
1230{
1231 SHAPE_POLY_SET buffer;
1232
1233 // Create an arc that is effectively a straight line - mid-point essentially on the line.
1234 // This should be detected by IsEffectiveLine() and handled as a line segment.
1235 const VECTOR2I start( 0, 0 );
1236 const VECTOR2I end( 50000000, 0 ); // 50mm chord length
1237 const VECTOR2I mid( 25000000, 1 ); // Mid-point only 1nm off the line
1238
1239 const int width = 250000; // 0.25mm track width
1240 const int aError = 5000;
1241
1242 // This should not crash and should produce valid geometry
1243 TransformArcToPolygon( buffer, start, mid, end, width, aError, ERROR_INSIDE );
1244
1245 BOOST_CHECK( buffer.OutlineCount() >= 1 );
1246
1247 if( buffer.OutlineCount() > 0 )
1248 {
1249 const SHAPE_LINE_CHAIN& outline = buffer.COutline( 0 );
1250 BOOST_CHECK( outline.IsClosed() );
1251 BOOST_CHECK( outline.PointCount() >= 3 );
1252 }
1253}
1254
1255
1260BOOST_AUTO_TEST_CASE( TransformIssue22475ArcToPolygon )
1261{
1262 SHAPE_POLY_SET buffer;
1263
1264 // Values approximating one of the problematic arcs from issue #22475:
1265 // radius=24.35 mm, distToMid=12960 nm, chord=1.62 mm, angle=-3.8 deg
1266 // Compute start, mid, end points for such an arc
1267 const VECTOR2I start( 0, 0 );
1268 const VECTOR2I end( 1620000, 0 ); // 1.62mm chord length
1269 const VECTOR2I mid( 810000, 12960 ); // Mid-point 12960nm (12.96µm) off the line
1270
1271 const int width = 200000; // 0.2mm track width
1272 const int aError = 5000;
1273
1274 // Before the fix, this should create a polygon with very large extent
1275 // After the fix, it should create a reasonable oval-shaped polygon
1276 TransformArcToPolygon( buffer, start, mid, end, width, aError, ERROR_INSIDE );
1277
1278 BOOST_REQUIRE( buffer.OutlineCount() >= 1 );
1279
1280 const SHAPE_LINE_CHAIN& outline = buffer.COutline( 0 );
1281 BOOST_CHECK( outline.IsClosed() );
1282 BOOST_CHECK( outline.PointCount() >= 3 );
1283
1284 BOX2I bbox = outline.BBox();
1285
1286 // The bounding box should be reasonable - roughly chord + 2*width wide, 2*width high
1287 // With the fix (treating as oval), width should be ~1620000 + 2*200000 = 2020000
1288 // Height should be ~2*200000 = 400000
1289 // Without the fix, the height could be enormous due to the large arc radius
1290
1291 // Check that the polygon isn't ridiculously large
1292 BOOST_CHECK_MESSAGE( bbox.GetWidth() <= 3000000,
1293 wxString::Format( "Polygon width %lld is too large (expected ~2020000)", (long long)bbox.GetWidth() ) );
1294 BOOST_CHECK_MESSAGE( bbox.GetHeight() <= 1000000,
1295 wxString::Format( "Polygon height %lld is too large (expected ~400000)", (long long)bbox.GetHeight() ) );
1296}
1297
1298
1304BOOST_AUTO_TEST_CASE( CollideNearlyFlatArcDoesNotOverflow )
1305{
1306 // Values from the core dump: a nearly-flat arc with enormous radius
1307 const VECTOR2I start( 68208364, -8000 );
1308 const VECTOR2I mid( 771364, 500000 );
1309 const VECTOR2I end( 35224335, -7999 );
1310 const int width = 1270000;
1311
1312 SHAPE_ARC arc( start, mid, end, width );
1313
1314 // Radius should be near or above INT_MAX/2, triggering the segment fallback
1315 BOOST_CHECK( arc.GetRadius() >= (double) std::numeric_limits<int>::max() / 2.0 );
1316
1317 // Point near the arc endpoints. Must not crash.
1318 const VECTOR2I testPt( 35224298, -5381 );
1319 int actual = 0;
1321
1322 BOOST_CHECK_NO_THROW( arc.Collide( testPt, 635000, &actual, &location ) );
1323
1324 // Segment near the arc. Must not crash.
1325 const SEG testSeg( VECTOR2I( 35224298, -5381 ), VECTOR2I( 35696364, -32988651 ) );
1326
1327 BOOST_CHECK_NO_THROW( arc.Collide( testSeg, 635000, &actual, &location ) );
1328}
1329
1330
1342BOOST_AUTO_TEST_CASE( DegenerateArcCoincidentPoints )
1343{
1344 SHAPE_ARC arc( VECTOR2I( 135674000, 84576744 ),
1345 VECTOR2I( 135673999, 84576744 ),
1346 VECTOR2I( 135673998, 84576744 ),
1347 100000 );
1348
1349 BOOST_CHECK_LT( arc.GetRadius(), 10.0 );
1350
1352 BOOST_CHECK_LT( poly.BBox().GetWidth(), 1000 ); // < 1 µm
1353 BOOST_CHECK_LT( poly.BBox().GetHeight(), 1000 );
1354 BOOST_CHECK_LT( arc.GetLength(), 1000.0 );
1355}
1356
1357
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr const Vec & GetPosition() const
Definition box2.h:207
constexpr const Vec GetEnd() const
Definition box2.h:208
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr size_type GetHeight() const
Definition box2.h:211
double AsDegrees() const
Definition eda_angle.h:116
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
EDA_ANGLE GetCentralAngle() const
Get the "central angle" of the arc - this is the angle at the point of the "pie slice".
const VECTOR2I & GetArcMid() const
Definition shape_arc.h:116
SEG GetChord() const
Definition shape_arc.h:243
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
int GetWidth() const override
Definition shape_arc.h:211
EDA_ANGLE GetEndAngle() const
double GetLength() const
const SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError=DefaultAccuracyForPCB(), int *aActualError=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
bool Collide(const SEG &aSeg, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the segment aSeg than aClearance,...
static int DefaultAccuracyForPCB()
Definition shape_arc.h:279
double GetRadius() const
EDA_ANGLE GetStartAngle() const
SHAPE * Clone() const override
Return a dynamically allocated copy of the shape.
Definition shape_arc.h:84
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
bool IsSolid() const override
Definition shape_arc.h:216
const VECTOR2I & GetCenter() const
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int OutlineCount() const
Return the number of outlines in the set.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
An abstract shape on 2D plane.
Definition shape.h:124
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition shape.h:179
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
Definition shape.h:129
void TransformArcToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arc to multiple straight segments.
@ DEGREES_T
Definition eda_angle.h:31
bool ArePointsNearCircle(const std::vector< VECTOR2< T > > &aPoints, const VECTOR2< T > &aCentre, T aRad, T aTol)
Predicate for checking a set of points is within a certain tolerance of a circle.
bool IsWithinWrapped(T aValue, T aNominal, T aWrap, T aError)
Check if a value is within a tolerance of a nominal value, wrapping to a given val.
Definition numeric.h:39
bool IsBoxWithinTol(const BOX &aBox, const BOX &aExp, typename BOX::coord_type aTol)
Check that a box is close enough to another box.
Definition geometry.h:61
bool IsWithin(T aValue, T aNominal, T aError)
Check if a value is within a tolerance of a nominal value.
Definition numeric.h:57
bool IsVecWithinTol(const VEC &aVec, const VEC &aExp, typename VEC::coord_type aTol)
Check that both x and y of a vector are within expected error.
Definition geometry.h:51
Numerical test predicates.
const double epsilon
@ SH_ARC
circular arc
Definition shape.h:50
Info to set up an arc by centre, start point and angle.
ARC_START_MID_END m_geom
ARC_CENTRE_PT_ANGLE m_geom
Geom of the arc.
ARC_PROPERTIES m_properties
Expected properties.
int m_width
Arc line width.
SHAPE_ARC GenerateArc() const
All properties of an arc (depending on how it's constructed, some of these might be the same as the c...
VECTOR2I m_center_point
ARC_CENTRE_PT_ANGLE m_geom
bool m_clockwise
clockwise or anti-clockwise?
ARC_START_END_CENTER m_geom
Geom of the arc.
VECTOR2I m_expected_mid
Expected mid-point of the arc.
ARC_CENTRE_PT_ANGLE m_geom
ARC_START_MID_END m_geom
Geom of the arc.
int m_width
Arc line width.
ARC_PROPERTIES m_properties
Expected properties.
Info to set up an arc by start, mid and end points.
Info to set up an arc by tangent to two segments and a radius.
ARC_CENTRE_PT_ANGLE m_geom
ARC_PROPERTIES m_properties
Expected properties.
ARC_TAN_TAN_RADIUS m_geom
Geom of the arc.
int m_width
Arc line width.
A named data-driven test case.
BOOST_DATA_TEST_CASE(ConvertToKicadUnit, boost::unit_test::data::make(altium_to_kicad_unit), input_value, expected_result)
Test conversation from Altium internal units into KiCad internal units.
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_TEST(netlist.find("R_G1 ARM_OUT1 DIE_B R='0.001 / ((SW_STATE)") !=std::string::npos)
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
bool ArePolylineMidPointsNearCircle(const SHAPE_LINE_CHAIN &aPolyline, const VECTOR2I &aCentre, int aRad, int aTolerance)
Predicate for checking a polyline has all the segment mid points on (near) a circle of given centre a...
bool cw
SHAPE_ARC arc2(c.m_arc2.GenerateArc())
VECTOR2I center
bool result_chain_to_chain
const SHAPE_LINE_CHAIN chain
static const std::vector< ARC_PT_COLLIDE_CASE > arc_pt_collide_cases
int radius
static void CheckArcGeom(const SHAPE_ARC &aArc, const ARC_PROPERTIES &aProps, const int aSynErrIU=1)
Check a SHAPE_ARC against a given set of geometric properties.
BOOST_CHECK_PREDICATE(ArePolylineEndPointsNearCircle,(chain)(c.m_geom.m_center_point)(radius)(accuracy+epsilon))
static const std::vector< ARC_SEC_CASE > arc_sec_cases
SHAPE_LINE_CHAIN arc1_slc(c.m_arc1.GenerateArc())
SHAPE * arc1_sh
BOOST_CHECK_EQUAL(this_arc.GetArcMid(), c.m_expected_mid)
static const std::vector< ARC_SME_CASE > arc_sme_cases
const std::vector< ARC_TO_POLYLINE_CASE > ArcToPolyline_cases
SHAPE * arc2_sh
VECTOR2I end
bool ArePolylineEndPointsNearCircle(const SHAPE_LINE_CHAIN &aPolyline, const VECTOR2I &aCentre, int aRad, int aTolerance)
Predicate for checking a polyline has all the points on (near) a circle of given centre and radius.
static const std::vector< ARC_SEG_COLLIDE_CASE > arc_seg_collide_cases
static void CheckArc(const SHAPE_ARC &aArc, const ARC_PROPERTIES &aProps, const int aSynErrIU=1)
Check an arcs geometry and other class functions.
BOOST_AUTO_TEST_CASE(NullCtor)
Check correct handling of filter strings (as used by WX)
BOOST_TEST_CONTEXT("Test Clearance")
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
int clearance
VECTOR2I location
SHAPE_ARC this_arc
static const std::vector< ARC_CICLE_COLLIDE_CASE > arc_circle_collide_cases
int actual
static const std::vector< ARC_CPA_CASE > arc_cases
bool result_arc_to_chain
static const std::vector< ARC_TTR_CASE > arc_ttr_cases
SHAPE * arc2_slc_sh
const int accuracy
SHAPE_LINE_CHAIN arc2_slc(c.m_arc2.GenerateArc())
static const std::vector< ARC_ARC_COLLIDE_CASE > arc_arc_collide_cases
bool result_arc_to_arc
SHAPE * arc1_slc_sh
bool result_chain_to_arc
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682