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>
31#include <trigo.h>
32
34#include <qa_utils/numeric.h>
35
36#include "geom_test_utils.h"
37
38BOOST_AUTO_TEST_SUITE( ShapeArc )
39
40
55
62static void CheckArcGeom( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps, const int aSynErrIU = 1 )
63{
64 // Angular error - note this can get quite large for very small arcs,
65 // as the integral position rounding has a relatively greater effect
66 const double angle_tol_deg = 2.0;
67
68 // Position error - rounding to nearest integer
69 const int pos_tol = 1;
70
72 ( aProps.m_start_point )( aProps.m_start_point )( pos_tol ) );
73
75 ( aArc.GetP1() )( aProps.m_end_point )( pos_tol ) );
76
78 ( aArc.GetCenter() )( aProps.m_center_point )( aSynErrIU ) );
79
81 ( aArc.GetCentralAngle().AsDegrees() )( aProps.m_center_angle )( 360.0 )( angle_tol_deg ) );
82
84 ( aArc.GetStartAngle().AsDegrees() )( aProps.m_start_angle )( 360.0 )( angle_tol_deg ) );
85
87 ( aArc.GetEndAngle().AsDegrees() )( aProps.m_end_angle )( 360.0 )( angle_tol_deg ) );
88
90 ( aArc.GetRadius() )( aProps.m_radius )( aSynErrIU ) );
91
92 // Angle normalization contracts
93 BOOST_TEST( aArc.GetStartAngle().AsDegrees() >= 0.0 );
94 BOOST_TEST( aArc.GetStartAngle().AsDegrees() <= 360.0 );
95
96 BOOST_TEST( aArc.GetEndAngle().AsDegrees() >= 0.0 );
97 BOOST_TEST( aArc.GetEndAngle().AsDegrees() <= 360.0 );
98
99 BOOST_TEST( aArc.GetCentralAngle().AsDegrees() >= -360.0 );
100 BOOST_TEST( aArc.GetCentralAngle().AsDegrees() <= 360.0 );
101
103 const SEG chord = aArc.GetChord();
104
106 ( chord.A )( aProps.m_start_point )( pos_tol ) );
107
109 ( chord.B )( aProps.m_end_point )( pos_tol ) );
110
112 BOOST_CHECK_EQUAL( aArc.IsSolid(), true );
113
115 ( aArc.BBox() )( aProps.m_bbox )( pos_tol ) );
116
118}
119
120
127static void CheckArc( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps, const int aSynErrIU = 1 )
128{
129 // Check the original arc
130 CheckArcGeom( aArc, aProps, aSynErrIU );
131
132 // Test the Clone function (also tests copy-ctor)
133 std::unique_ptr<SHAPE> new_shape{ aArc.Clone() };
134
135 BOOST_REQUIRE_EQUAL( new_shape->Type(), SH_ARC );
136
137 SHAPE_ARC* new_arc = dynamic_cast<SHAPE_ARC*>( new_shape.get() );
138
139 BOOST_REQUIRE( new_arc != nullptr );
140
142 CheckArcGeom( *new_arc, aProps, aSynErrIU );
143}
144
149{
150 auto arc = SHAPE_ARC();
151
152 BOOST_CHECK_EQUAL( arc.GetWidth(), 0 );
153
154 static ARC_PROPERTIES null_props{
155 { 0, 0 },
156 { 0, 0 },
157 { 0, 0 },
158 0,
159 0,
160 0,
161 0,
162 };
163
164 CheckArc( arc, null_props );
165}
166
167
177
189
190
191static const std::vector<ARC_SME_CASE> arc_sme_cases = {
192 {
193 "S(-100,0), M(0,100), E(100,0)",
194 {
195 { -100, 0 },
196 { 0, 100 },
197 { 100, 0 },
198 },
199 0,
200 {
201 { 0, 0 },
202 { -100, 0 },
203 { 100, 0 },
204 180,
205 180,
206 0,
207 100,
208 { { -100, 0 }, { 200, 100 } },
209 },
210 },
211 {
212 "S(100,0), M(0,100), E(-100,0) (reversed)",
213 {
214 { 100, 0 },
215 { 0, 100 },
216 { -100, 0 },
217 },
218 0,
219 {
220 { 0, 0 },
221 { 100, 0 },
222 { -100, 0 },
223 -180,
224 0,
225 180,
226 100,
227 { { -100, 0 }, { 200, 100 } },
228 },
229 },
230 {
231 // This data has a midpoint not exactly at the midway point of the arc.
232 // This should be corrected by the constructor.
233 // The mid point should be at about (-71, -71) for a 270 degree arc, with the
234 // bottom right quadrant open.
235 "S(100,0), M(-100,0), E(0,100) (bad midpoint)",
236 {
237 { 100, 0 },
238 { -100, 0 },
239 { 0, 100 },
240 },
241 0,
242 {
243 { 0, 0 },
244 { 100, 0 },
245 { 0, 100 },
246 -270,
247 0,
248 90,
249 100,
250 { { -100, -100 }, { 200, 200 } },
251 },
252 }
253};
254
255
256BOOST_DATA_TEST_CASE( BasicSMEGeom, boost::unit_test::data::make( arc_sme_cases ), c )
257{
258 const SHAPE_ARC this_arc{
259 c.m_geom.m_start_point,
260 c.m_geom.m_mid_point,
261 c.m_geom.m_end_point,
262 c.m_width,
263 };
264
265 CheckArc( this_arc, c.m_properties );
266}
267
268
278
279
291
292
293static const std::vector<ARC_CPA_CASE> arc_cases = {
294 {
295 "C(0,0) 114 + 360 degree",
296 {
297 { 0, 0 },
298 { -306451, 687368 },
299 360,
300 },
301 0,
302 {
303 { 0, 0 },
304 { -306451, 687368 },
305 { -306451, 687368 },
306 360,
307 113.95929,
308 113.95929,
309 752587,
310 { { -752587, -752587 }, { 1505174, 1505174 } },
311 },
312 },
313 {
314 "C(0,0) 180 + 360 degree",
315 {
316 { 0, 0 },
317 { -100, 0 },
318 360,
319 },
320 0,
321 {
322 { 0, 0 },
323 { -100, 0 },
324 { -100, 0 },
325 360,
326 180,
327 180,
328 100,
329 { { -100, -100 }, { 200, 200 } },
330 },
331 },
332 {
333 "C(0,0) 180 + 90 degree",
334 {
335 { 0, 0 },
336 { -100, 0 },
337 90,
338 },
339 0,
340 {
341 { 0, 0 },
342 { -100, 0 },
343 { 0, -100 },
344 90,
345 180,
346 270,
347 100,
348 { { -100, -100 }, { 100, 100 } },
349 },
350 },
351 {
352 "C(100,200) 0 - 30 degree",
353 {
354 { 100, 200 },
355 { 300, 200 },
356 -30,
357 },
358 0,
359 {
360 { 100, 200 },
361 { 300, 200 },
362 { 273, 100 }, // 200 * sin(30) = 100, 200* cos(30) = 173
363 -30,
364 0,
365 330,
366 200,
367 { { 273, 100 }, { 27, 100 } },
368 },
369 },
370 {
371 // This is a "fan shape" which includes the top quadrant point,
372 // so it exercises the bounding box code (centre and end points
373 // do not contain the top quadrant)
374 "C(0,0) 30 + 120 degree",
375 {
376 { 0, 0 },
377 { 17320, 10000 },
378 120,
379 },
380 0,
381 {
382 { 0, 0 },
383 { 17320, 10000 },
384 { -17320, 10000 }, // 200 * sin(30) = 100, 200* cos(30) = 173
385 120,
386 30,
387 150,
388 20000,
389 // bbox defined by: centre, top quadrant point, two endpoints
390 { { -17320, 10000 }, { 17320 * 2, 10000 } },
391 },
392 },
393 {
394 // An arc that covers three quadrant points (L/R, bottom)
395 "C(0,0) 150 + 240 degree",
396 {
397 { 0, 0 },
398 { -17320, 10000 },
399 240,
400 },
401 0,
402 {
403 { 0, 0 },
404 { -17320, 10000 },
405 { 17320, 10000 },
406 240,
407 150,
408 30,
409 20000,
410 // bbox defined by: L/R quads, bottom quad and start/end
411 { { -20000, -20000 }, { 40000, 30000 } },
412 },
413 },
414 {
415 // Same as above but reverse direction
416 "C(0,0) 30 - 300 degree",
417 {
418 { 0, 0 },
419 { 17320, 10000 },
420 -240,
421 },
422 0,
423 {
424 { 0, 0 },
425 { 17320, 10000 },
426 { -17320, 10000 },
427 -240,
428 30,
429 150,
430 20000,
431 // bbox defined by: L/R quads, bottom quad and start/end
432 { { -20000, -20000 }, { 40000, 30000 } },
433 },
434 },
435};
436
437
438BOOST_DATA_TEST_CASE( BasicCPAGeom, boost::unit_test::data::make( arc_cases ), c )
439{
440 const SHAPE_ARC this_arc{
441 c.m_geom.m_center_point,
442 c.m_geom.m_start_point,
443 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ),
444 c.m_width,
445 };
446
447 CheckArc( this_arc, c.m_properties );
448}
449
450
451
461
462
474
475
476static const std::vector<ARC_TTR_CASE> arc_ttr_cases = {
477 {
478 "90 degree segments intersecting",
479 {
480 { 0, 0, 0, 1000 },
481 { 0, 0, 1000, 0 },
482 1000,
483 },
484 0,
485 {
486 { 1000, 1000 },
487 { 0, 1000 }, //start on first segment
488 { 1000, 0 }, //end on second segment
489 90, //positive angle due to start/end
490 180,
491 270,
492 1000,
493 { { 0, 0 }, { 1000, 1000 } },
494 }
495 },
496 {
497 "45 degree segments intersecting",
498 {
499 { 0, 0, 0, 1000 },
500 { 0, 0, 1000, 1000 },
501 1000,
502 },
503 0,
504 {
505 { 1000, 2414 },
506 { 0, 2414 }, //start on first segment
507 { 1707, 1707 }, //end on second segment
508 135, //positive angle due to start/end
509 180,
510 315,
511 1000,
512 { { 0, 1414 }, { 1707, 1000 } },
513 }
514 },
515 {
516 "135 degree segments intersecting",
517 {
518 { 0, 0, 0, 1000 },
519 { 0, 0, 1000, -1000 },
520 1000,
521 },
522 0,
523 {
524 { 1000, 414 },
525 { 0, 414 }, //start on first segment ( radius * tan(45 /2) )
526 { 293, -293 }, //end on second segment (radius * 1-cos(45)) )
527 45, //positive angle due to start/end
528 180,
529 225,
530 1000,
531 { { 0, -293 }, { 293, 707 } },
532 }
533 }
534
535
536};
537
538
539BOOST_DATA_TEST_CASE( BasicTTRGeom, boost::unit_test::data::make( arc_ttr_cases ), c )
540{
541 for( int testCase = 0; testCase < 8; ++testCase )
542 {
543 SEG seg1 = c.m_geom.m_segment_1;
544 SEG seg2 = c.m_geom.m_segment_2;
545 ARC_PROPERTIES props = c.m_properties;
546
547 if( testCase > 3 )
548 {
549 //Swap input segments.
550 seg1 = c.m_geom.m_segment_2;
551 seg2 = c.m_geom.m_segment_1;
552
553 //The result should swap start and end points and invert the angles:
554 props.m_end_point = c.m_properties.m_start_point;
555 props.m_start_point = c.m_properties.m_end_point;
556 props.m_start_angle = c.m_properties.m_end_angle;
557 props.m_end_angle = c.m_properties.m_start_angle;
558 props.m_center_angle = -c.m_properties.m_center_angle;
559 }
560
561 //Test all combinations of start and end points for the segments
562 if( ( testCase % 4 ) == 1 || ( testCase % 4 ) == 3 )
563 {
564 //Swap start and end points for seg1
565 VECTOR2I temp = seg1.A;
566 seg1.A = seg1.B;
567 seg1.B = temp;
568 }
569
570 if( ( testCase % 4 ) == 2 || ( testCase % 4 ) == 3 )
571 {
572 //Swap start and end points for seg2
573 VECTOR2I temp = seg2.A;
574 seg2.A = seg2.B;
575 seg2.B = temp;
576 }
577
578 const auto this_arc = SHAPE_ARC{ seg1, seg2,
579 c.m_geom.m_radius, c.m_width };
580
581 // Error of 4 IU permitted for the center and radius calculation
583 }
584}
585
586
590struct ARC_START_END_CENTER
591{
592 VECTOR2I m_start;
593 VECTOR2I m_end;
594 VECTOR2I m_center;
595};
596
597
599{
601 ARC_START_END_CENTER m_geom;
602
605
608};
609
610
611
612static const std::vector<ARC_SEC_CASE> arc_sec_cases = {
613 { "180 deg, clockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } }, true, { 50, -50 } },
614 { "180 deg, anticlockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } }, false, { 50, 50 } },
615 { "180 deg flipped, clockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } }, true, { 50, 50 } },
616 { "180 deg flipped, anticlockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } }, false, { 50, -50 } },
617 { "90 deg, clockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } }, true, { -71, 71 } },
618 { "90 deg, anticlockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } }, false, { 71, -71 } },
619};
620
621
622BOOST_DATA_TEST_CASE( BasicSECGeom, boost::unit_test::data::make( arc_sec_cases ), c )
623{
624 VECTOR2I start = c.m_geom.m_start;
625 VECTOR2I end = c.m_geom.m_end;
626 VECTOR2I center = c.m_geom.m_center;
627 bool cw = c.m_clockwise;
628
630 this_arc.ConstructFromStartEndCenter( start, end, center, cw );
631
632 BOOST_CHECK_EQUAL( this_arc.GetArcMid(), c.m_expected_mid );
633}
634
635
645
646static const std::vector<ARC_CICLE_COLLIDE_CASE> arc_circle_collide_cases = {
647 { " Issue 20336, large arc", { { 183000000, 65710001}, {150496913, 147587363},{116291153, 66406583}}, 2000000 / 2, {116300000, 133100000}, 300000, true, 53319 }
648};
649
650
651BOOST_DATA_TEST_CASE( CollideCircle, boost::unit_test::data::make( arc_circle_collide_cases ), c )
652{
653 SHAPE_ARC arc( c.m_geom.m_start_point, c.m_geom.m_mid_point, c.m_geom.m_end_point, 0 );
654 SHAPE_CIRCLE circle( c.m_circle_center, c.m_circle_radius );
655
656 // Test a zero width arc (distance should equal the clearance)
657 BOOST_TEST_CONTEXT( "Test Clearance" )
658 {
659 int dist = -1;
660 BOOST_CHECK_EQUAL( arc.Collide( &circle, c.m_arc_clearance, &dist ), c.m_exp_result );
661 BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
662 }
663
664 // Test by changing the width of the arc (distance should equal zero)
665 BOOST_TEST_CONTEXT( "Test Width" )
666 {
667 int dist = -1;
668 arc.SetWidth( c.m_arc_clearance * 2 );
669 BOOST_CHECK_EQUAL( arc.Collide( &circle, 0, &dist ), c.m_exp_result );
670
671 if( c.m_exp_result )
672 BOOST_CHECK_EQUAL( dist, 0 );
673 else
674 BOOST_CHECK_EQUAL( dist, -1 );
675 }
676}
677
678
687
688
689static const std::vector<ARC_PT_COLLIDE_CASE> arc_pt_collide_cases = {
690 { " 270deg, 0 cl, 0 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 100, 0 }, true, 0 },
691 { " 270deg, 0 cl, 90 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 0, 100 }, true, 0 },
692 { " 270deg, 0 cl, 180 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { -100, 0 }, true, 0 },
693 { " 270deg, 0 cl, 270 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 0, -100 }, true, 0 },
694 { " 270deg, 0 cl, 45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 71, 71 }, true, 0 },
695 { " 270deg, 0 cl, -45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 71, -71 }, false, -1 },
696 { "-270deg, 0 cl, 0 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 100, 0 }, true, 0 },
697 { "-270deg, 0 cl, 90 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 0, 100 }, true, 0 },
698 { "-270deg, 0 cl, 180 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { -100, 0 }, true, 0 },
699 { "-270deg, 0 cl, 270 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 0, -100 }, true, 0 },
700 { "-270deg, 0 cl, 45 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 71, 71 }, false, -1 },
701 { "-270deg, 0 cl, -45 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 71, -71 }, true, 0 },
702 { " 270deg, 5 cl, 0 deg, 5 pos X", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 105, 0 }, true, 5 },
703 { " 270deg, 5 cl, 0 deg, 5 pos Y", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 100, -5 }, true, 5 },
704 { " 270deg, 5 cl, 90 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, 105 }, true, 5 },
705 { " 270deg, 5 cl, 180 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { -105, 0 }, true, 5 },
706 { " 270deg, 5 cl, 270 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, -105 }, true, 5 },
707 { " 270deg, 5 cl, 0 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 105, 0 }, true, 5 },
708 { " 270deg, 5 cl, 90 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, 105 }, true, 5 },
709 { " 270deg, 5 cl, 180 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { -105, 0 }, true, 5 },
710 { " 270deg, 5 cl, 270 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, -105 }, true, 5 },
711 { " 270deg, 5 cl, 45 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 74, 75 }, true, 5 }, // 74.246, -74.246
712 { " 270deg, 5 cl, -45 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 74, -75 }, false, -1 }, //74.246, -74.246
713 { " 270deg, 5 cl, 45 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 67, 67 }, true, 5 }, // 67.17, 67.17
714 { " 270deg, 5 cl, -45 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 67, -67 }, false, -1 }, // 67.17, -67.17
715 { " 270deg, 4 cl, 0 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 105, 0 }, false, -1 },
716 { " 270deg, 4 cl, 90 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 0, 105 }, false, -1 },
717 { " 270deg, 4 cl, 180 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { -105, 0 }, false, -1 },
718 { " 270deg, 4 cl, 270 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 0, -105 }, false, -1 },
719 { " 90deg, 0 cl, 0 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 71, -71 }, true, 0 },
720 { " 90deg, 0 cl, 45 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 100, 0 }, true, 0 },
721 { " 90deg, 0 cl, 90 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 71, 71 }, true, 0 },
722 { " 90deg, 0 cl, 135 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 0, -100 }, false, -1 },
723 { " 90deg, 0 cl, -45 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 0, 100 }, false, -1 },
724 { " -90deg, 0 cl, 0 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 71, -71 }, true, 0 },
725 { " -90deg, 0 cl, 45 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 100, 0 }, true, 0 },
726 { " -90deg, 0 cl, 90 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 71, 71 }, true, 0 },
727 { " -90deg, 0 cl, 135 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 0, -100 }, false, -1 },
728 { " -90deg, 0 cl, -45 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 0, 100 }, false, -1 },
729 { "issue 11358 collide",
730 { { 119888000, 60452000 }, { 120904000, 60452000 }, 360.0 },
731 0,
732 { 120395500, 59571830 },
733 true,
734 0 },
735 { "issue 11358 dist",
736 { { 119888000, 60452000 }, { 120904000, 60452000 }, 360.0 },
737 100,
738 { 118872050, 60452000 },
739 true,
740 50 },
741};
742
743
744BOOST_DATA_TEST_CASE( CollidePt, boost::unit_test::data::make( arc_pt_collide_cases ), c )
745{
746 SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
747 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
748
749 // Test a zero width arc (distance should equal the clearance)
750 BOOST_TEST_CONTEXT( "Test Clearance" )
751 {
752 int dist = -1;
753 BOOST_CHECK_EQUAL( arc.Collide( c.m_point, c.m_arc_clearance, &dist ),
754 c.m_exp_result );
755 BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
756 }
757
758 // Test by changing the width of the arc (distance should equal zero)
759 BOOST_TEST_CONTEXT( "Test Width" )
760 {
761 int dist = -1;
762 arc.SetWidth( c.m_arc_clearance * 2 );
763 BOOST_CHECK_EQUAL( arc.Collide( c.m_point, 0, &dist ), c.m_exp_result );
764
765 if( c.m_exp_result )
766 BOOST_CHECK_EQUAL( dist, 0 );
767 else
768 BOOST_CHECK_EQUAL( dist, -1 );
769 }
770}
771
781
782
783static const std::vector<ARC_SEG_COLLIDE_CASE> arc_seg_collide_cases = {
784 { "0 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 100, 0 }, { 50, 0 } }, true, 0, { 100, 0 } },
785 { "90 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 0, 100 }, { 0, 50 } }, true, 0, { 0, 100 } },
786 { "180 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { -100, 0 }, { -50, 0 } }, true, 0, { -100, 0 } },
787 { "270 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 0, -100 }, { 0, -50 } }, true, 0, { 0, -100 } },
788 { "45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 71, 71 }, { 35, 35 } }, true, 0, { 70, 70 } },
789 { "-45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 71, -71 }, { 35, -35 } }, false, -1, { 0, 0 } },
790 { "seg inside arc start", { { 0, 0 }, { 71, -71 }, 90.0 },
791 10, { { 90, 0 }, { -35, 0 } }, true, 10, { 100, 0 } },
792 { "seg inside arc end", { { 0, 0 }, { 71, -71 }, 90.0 },
793 10, { { -35, 0 }, { 90, 0 } }, true, 10, { 100, 0 } },
794 { "large diameter arc", { { 172367922, 82282076 }, { 162530000, 92120000 }, -45.0 },
795 433300, { { 162096732, 92331236 }, { 162096732, 78253268 } }, true, 433268, { 162530000, 92120000 } },
796 { "upside down collide", { { 26250000, 16520000 }, { 28360000, 16520000 }, 90.0 },
797 0, { { 27545249, 18303444 }, { 27545249, 18114500 } }, true, 0, { 27545249, 18185662 } }
798};
799
800
801BOOST_DATA_TEST_CASE( CollideSeg, boost::unit_test::data::make( arc_seg_collide_cases ), c )
802{
803 SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
804 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
805
806 // Test a zero width arc (distance should equal the clearance)
807 BOOST_TEST_CONTEXT( "Test Clearance" )
808 {
809 int dist = -1;
810 BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, c.m_arc_clearance, &dist ),
811 c.m_exp_result );
812 BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
813 }
814
815 // Test by changing the width of the arc (distance should equal zero)
816 BOOST_TEST_CONTEXT( "Test Width" )
817 {
818 int dist = -1;
819 arc.SetWidth( c.m_arc_clearance * 2 );
820 BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, 0, &dist ), c.m_exp_result );
821
822 if( c.m_exp_result )
823 BOOST_CHECK_EQUAL( dist, 0 );
824 else
825 BOOST_CHECK_EQUAL( dist, -1 );
826 }
827
828 BOOST_TEST_CONTEXT( "Test Collide Point" )
829 {
830 VECTOR2I collide_point;
831 int dist = -1;
832
833 if( c.m_exp_result )
834 {
835 arc.Collide( c.m_seg, c.m_arc_clearance, &dist, &collide_point );
836 BOOST_CHECK_EQUAL( collide_point, c.m_collide_point );
837 }
838 }
839}
840
842{
843 // Coordinates and dimensions in millimeters
846 double m_start_x;
847 double m_start_y;
849 double m_width;
850
852 {
853 SHAPE_ARC arc( VECTOR2D( pcbIUScale.mmToIU( m_center_x ), pcbIUScale.mmToIU( m_center_y ) ),
854 VECTOR2D( pcbIUScale.mmToIU( m_start_x ), pcbIUScale.mmToIU( m_start_y ) ),
856
857 return arc;
858 }
859};
860
861
869
870
871static const std::vector<ARC_ARC_COLLIDE_CASE> arc_arc_collide_cases = {
872 { "case 1: No intersection",
873 { 73.843527, 74.355869, 71.713528, 72.965869, -76.36664803, 0.2 },
874 { 71.236473, 74.704131, 73.366472, 76.094131, -76.36664803, 0.2 },
875 0,
876 false },
877 { "case 2: No intersection",
878 { 82.542335, 74.825975, 80.413528, 73.435869, -76.4, 0.2 },
879 { 76.491192, 73.839894, 78.619999, 75.23, -76.4, 0.2 },
880 0,
881 false },
882 { "case 3: No intersection",
883 { 89.318807, 74.810106, 87.19, 73.42, -76.4, 0.2 },
884 { 87.045667, 74.632941, 88.826472, 75.794131, -267.9, 0.2 },
885 0,
886 false },
887 { "case 4: Co-centered not intersecting",
888 { 94.665667, 73.772941, 96.446472, 74.934131, -267.9, 0.2 },
889 { 94.665667, 73.772941, 93.6551, 73.025482, -255.5, 0.2 },
890 0,
891 false },
892 { "case 5: Not intersecting, but end points very close",
893 { 72.915251, 80.493054, 73.570159, 81.257692, -260.5, 0.2 },
894 { 73.063537, 82.295989, 71.968628, 81.581351, -255.5, 0.2 },
895 0,
896 false },
897 { "case 6: Coincident centers, colliding due to arc thickness",
898 { 79.279991, 80.67988, 80.3749, 81.394518, -255.5, 0.3 },
899 { 79.279991, 80.67988, 80.3749, 81.694518, -255.5, 0.3 },
900 0,
901 true },
902 { "case 7: Single intersection",
903 { 88.495265, 81.766089, 90.090174, 82.867869, -255.5, 0.2 },
904 { 86.995265, 81.387966, 89.090174, 82.876887, -255.5, 0.2 },
905 0,
906 true },
907 { "case 8: Double intersection",
908 { 96.149734, 81.792126, 94.99, 83.37, -347.2, 0.2 },
909 { 94.857156, 81.240589, 95.91, 83.9, -288.5, 0.2 },
910 0,
911 true },
912 { "case 9: Endpoints within arc width",
913 { 72.915251, 86.493054, 73.970159, 87.257692, -260.5, 0.2 },
914 { 73.063537, 88.295989, 71.968628, 87.581351, -255.5, 0.2 },
915 0,
916 true },
917 { "case 10: Endpoints close, outside, no collision",
918 { 78.915251, 86.393054, 79.970159, 87.157692, 99.5, 0.2 },
919 { 79.063537, 88.295989, 77.968628, 87.581351, -255.5, 0.2 },
920 0,
921 false },
922 { "case 11: Endpoints close, inside, collision due to arc width",
923 { 85.915251, 86.993054, 86.970159, 87.757692, 99.5, 0.2 },
924 { 86.063537, 88.295989, 84.968628, 87.581351, -255.5, 0.2 },
925 0,
926 true },
927 { "case 12: Simulated differential pair length-tuning",
928 { 94.6551, 88.296, 95.6551, 88.296, 90.0, 0.1 },
929 { 94.6551, 88.296, 95.8551, 88.296, 90.0, 0.1 },
930 0.1,
931 false },
932 { "case 13: One arc fully enclosed in other, non-concentric",
933 { 73.77532, 93.413654, 75.70532, 93.883054, 60.0, 0.1 },
934 { 73.86532, 93.393054, 75.86532, 93.393054, 90.0, 0.3 },
935 0,
936 true },
937 { "case 14: One arc fully enclosed in other, concentric",
938 { 79.87532, 93.413654, 81.64532, 94.113054, 60.0, 0.1 },
939 { 79.87532, 93.413654, 81.86532, 93.393054, 90.0, 0.3 },
940 0,
941 true },
942 { "case 15: Arcs separated by clearance",
943 { 303.7615, 149.9252, 303.695968, 149.925237, 90.0262, 0.065 },
944 { 303.6345, 149.2637, 303.634523, 148.85619, 89.9957, 0.065 },
945 0.15,
946 false },
947};
948
949
950BOOST_DATA_TEST_CASE( CollideArc, boost::unit_test::data::make( arc_arc_collide_cases ), c )
951{
952 SHAPE_ARC arc1( c.m_arc1.GenerateArc() );
953 SHAPE_ARC arc2( c.m_arc2.GenerateArc() );
954
955
956 SHAPE_LINE_CHAIN arc1_slc( c.m_arc1.GenerateArc() );
957 arc1_slc.SetWidth( 0 );
958
959 SHAPE_LINE_CHAIN arc2_slc( c.m_arc2.GenerateArc() );
960 arc2_slc.SetWidth( 0 );
961
962 int actual = 0;
964
965 SHAPE* arc1_sh = &arc1;
969
970 bool result_arc_to_arc = arc1_sh->Collide( arc2_sh, pcbIUScale.mmToIU( c.m_clearance ),
971 &actual, &location );
972
973 // For arc to chain collisions, we need to re-calculate the clearances because the
974 // SHAPE_LINE_CHAIN is zero width
975 int clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc2.GetWidth() / 2 );
976
978
979 clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc1.GetWidth() / 2 );
981
982 clearance = ( arc1.GetWidth() / 2 ) + ( arc2.GetWidth() / 2 );
984
989}
990
991
992BOOST_AUTO_TEST_CASE( CollideArcToShapeLineChain )
993{
994 SHAPE_ARC arc( VECTOR2I( 206000000, 140110000 ), VECTOR2I( 201574617, 139229737 ),
995 VECTOR2I( 197822958, 136722959 ), 250000 );
996
997 SHAPE_LINE_CHAIN lc( { VECTOR2I( 159600000, 142500000 ), VECTOR2I( 159600000, 142600000 ),
998 VECTOR2I( 166400000, 135800000 ), VECTOR2I( 166400000, 111600000 ),
999 VECTOR2I( 190576804, 111600000 ), VECTOR2I( 192242284, 113265480 ),
1000 VECTOR2I( 192255720, 113265480 ), VECTOR2I( 203682188, 124691948 ),
1001 VECTOR2I( 203682188, 140332188 ), VECTOR2I( 206000000, 142650000 ) },
1002 false );
1003
1004
1005
1006 SHAPE* arc_sh = &arc;
1007 SHAPE* lc_sh = &lc;
1008
1009 BOOST_CHECK_EQUAL( arc_sh->Collide( &lc, 100000 ), true );
1010 BOOST_CHECK_EQUAL( lc_sh->Collide( &arc, 100000 ), true );
1011
1012 SEG seg( VECTOR2I( 203682188, 124691948 ), VECTOR2I( 203682188, 140332188 ) );
1013 BOOST_CHECK_EQUAL( arc.Collide( seg, 0 ), true );
1014}
1015
1016
1017BOOST_AUTO_TEST_CASE( CollideArcToClosedChainMatchesSegmentScan )
1018{
1019 struct CASE
1020 {
1021 SHAPE_ARC m_arc;
1022 SHAPE_SIMPLE m_chain;
1023 int m_clearance;
1024 bool m_collides;
1025 };
1026
1027 const SHAPE_ARC semicircle( VECTOR2I( -100, 0 ), VECTOR2I( 0, 100 ), VECTOR2I( 100, 0 ), 0 );
1028 const SHAPE_ARC major( VECTOR2I( 0, 0 ), VECTOR2I( -87, -50 ), EDA_ANGLE( 300.0, DEGREES_T ), 0 );
1029
1030 BOOST_REQUIRE( major.GetCentralAngle().AsDegrees() > 180.0 );
1031 BOOST_REQUIRE( major.BBox( 150 ).GetLeft() > -238 );
1032
1033 auto rect =
1034 []( int aX0, int aY0, int aX1, int aY1 )
1035 {
1036 return SHAPE_SIMPLE( SHAPE_LINE_CHAIN( { VECTOR2I( aX0, aY0 ), VECTOR2I( aX1, aY0 ),
1037 VECTOR2I( aX1, aY1 ), VECTOR2I( aX0, aY1 ) }, true ) );
1038 };
1039
1040 // SHAPE_SIMPLE routes through the SHAPE_LINE_CHAIN_BASE overload that carries the box reject
1041 const CASE cases[] = { { SHAPE_ARC( semicircle, 4 ), rect( -10, 102, 10, 110 ), 0, true },
1042 { semicircle, rect( -10, 105, 10, 110 ), 5, false },
1043 { major, rect( -239, -10, -238, 10 ), 150, true } };
1044
1045 for( const CASE& c : cases )
1046 {
1047 BOOST_REQUIRE( !c.m_chain.PointInside( c.m_arc.GetP0() ) );
1048
1049 int expectedActual = std::numeric_limits<int>::max();
1050 VECTOR2I expectedLocation;
1051
1052 for( size_t i = 0; i < c.m_chain.GetSegmentCount(); i++ )
1053 {
1054 int segmentActual = 0;
1055 VECTOR2I segmentLocation;
1056
1057 if( c.m_arc.Collide( c.m_chain.GetSegment( i ), c.m_clearance, &segmentActual, &segmentLocation )
1058 && segmentActual < expectedActual )
1059 {
1060 expectedActual = segmentActual;
1061 expectedLocation = segmentLocation;
1062 }
1063 }
1064
1065 bool expected = expectedActual == 0 || expectedActual < c.m_clearance;
1066 BOOST_REQUIRE_EQUAL( expected, c.m_collides );
1067
1068 int actual = 0;
1070 bool collided = static_cast<const SHAPE&>( c.m_arc ).Collide( &c.m_chain, c.m_clearance,
1071 &actual, &location );
1072
1073 BOOST_CHECK_EQUAL( collided, expected );
1074
1075 if( expected )
1076 {
1077 BOOST_CHECK_EQUAL( actual, expectedActual );
1078 BOOST_CHECK( location == expectedLocation );
1079 }
1080 }
1081}
1082
1083
1084BOOST_AUTO_TEST_CASE( CollideArcToPolygonApproximation )
1085{
1086 SHAPE_ARC arc( VECTOR2I( 73843527, 74355869 ), VECTOR2I( 71713528, 72965869 ),
1087 EDA_ANGLE( -76.36664803, DEGREES_T ), 1000000 );
1088
1089 // Create a polyset approximation from the arc - error outside (simulating the zone filler)
1090 SHAPE_POLY_SET arcBuffer;
1091 int clearance = ( arc.GetWidth() * 3 ) / 2;
1092 int polygonApproximationError = SHAPE_ARC::DefaultAccuracyForPCB();
1093
1094 TransformArcToPolygon( arcBuffer, arc.GetP0(), arc.GetArcMid(), arc.GetP1(),
1095 arc.GetWidth() + 2 * clearance,
1096 polygonApproximationError, ERROR_OUTSIDE );
1097
1098 BOOST_REQUIRE_EQUAL( arcBuffer.OutlineCount(), 1 );
1099 BOOST_CHECK_EQUAL( arcBuffer.HoleCount( 0 ), 0 );
1100
1101 // Make a reasonably large rectangular outline around the arc shape
1102 BOX2I arcbbox = arc.BBox( clearance * 4 );
1103
1104 SHAPE_LINE_CHAIN zoneOutline( { arcbbox.GetPosition(),
1105 arcbbox.GetPosition() + VECTOR2I( arcbbox.GetWidth(), 0 ),
1106 arcbbox.GetEnd(),
1107 arcbbox.GetEnd() - VECTOR2I( arcbbox.GetWidth(), 0 )
1108 },
1109 true );
1110
1111 // Create a synthetic "zone fill" polygon
1112 SHAPE_POLY_SET zoneFill;
1113 zoneFill.AddOutline( zoneOutline );
1114 zoneFill.AddHole( arcBuffer.Outline( 0 ) );
1115 zoneFill.CacheTriangulation();
1116
1117 int actual = 0;
1119 int epsilon = polygonApproximationError / 10;
1120
1121 BOOST_CHECK_EQUAL( zoneFill.Collide( &arc, clearance + epsilon, &actual, &location ), true );
1122
1123 BOOST_CHECK_EQUAL( zoneFill.Collide( &arc, clearance - epsilon, &actual, &location ), false );
1124}
1125
1126
1131
1132
1142bool ArePolylineEndPointsNearCircle( const SHAPE_LINE_CHAIN& aPolyline, const VECTOR2I& aCentre,
1143 int aRad, int aTolerance )
1144{
1145 std::vector<VECTOR2I> points;
1146
1147 for( int i = 0; i < aPolyline.PointCount(); ++i )
1148 {
1149 points.push_back( aPolyline.CPoint( i ) );
1150 }
1151
1152 return GEOM_TEST::ArePointsNearCircle( points, aCentre, aRad, aTolerance );
1153}
1154
1155
1165bool ArePolylineMidPointsNearCircle( const SHAPE_LINE_CHAIN& aPolyline, const VECTOR2I& aCentre,
1166 int aRad, int aTolerance )
1167{
1168 std::vector<VECTOR2I> points;
1169
1170 for( int i = 0; i < aPolyline.PointCount() - 1; ++i )
1171 {
1172 const VECTOR2I mid_pt = ( aPolyline.CPoint( i ) + aPolyline.CPoint( i + 1 ) ) / 2;
1173 points.push_back( mid_pt );
1174 }
1175
1176 return GEOM_TEST::ArePointsNearCircle( points, aCentre, aRad, aTolerance );
1177}
1178
1179
1180const std::vector<ARC_TO_POLYLINE_CASE> ArcToPolyline_cases{
1181 {
1182 "Zero rad",
1183 {
1184 { 0, 0 },
1185 { 0, 0 },
1186 180,
1187 },
1188 },
1189 {
1190 "Semicircle",
1191 {
1192 { 0, 0 },
1193 { -1000000, 0 },
1194 180,
1195 },
1196 },
1197 {
1198 // check that very small circles don't fall apart and that reverse angles
1199 // work too
1200 "Extremely small semicircle",
1201 {
1202 { 0, 0 },
1203 { -1000, 0 },
1204 -180,
1205 },
1206 },
1207 {
1208 // Make sure it doesn't only work for "easy" angles
1209 "Non-round geometry",
1210 {
1211 { 0, 0 },
1212 { 1234567, 0 },
1213 42.22,
1214 },
1215 },
1216};
1217
1218
1219BOOST_DATA_TEST_CASE( ArcToPolyline, boost::unit_test::data::make( ArcToPolyline_cases ), c )
1220{
1221 const int width = 0;
1222
1223 // Note: do not expect accuracies around 1 to work. We use integers internally so we're
1224 // liable to rounding errors. In PCBNew accuracy defaults to 5000 and we don't recommend
1225 // anything lower than 1000 (for performance reasons).
1226 const int accuracy = 100;
1227 const int epsilon = 1;
1228
1229 const SHAPE_ARC this_arc{ c.m_geom.m_center_point, c.m_geom.m_start_point,
1230 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ), width };
1231
1232 const SHAPE_LINE_CHAIN chain = this_arc.ConvertToPolyline( accuracy );
1233
1234 BOOST_TEST_MESSAGE( "Polyline has " << chain.PointCount() << " points" );
1235
1236 // Start point (exactly) where expected
1237 BOOST_CHECK_EQUAL( chain.CPoint( 0 ), c.m_geom.m_start_point );
1238
1239 // End point (exactly) where expected
1240 BOOST_CHECK_EQUAL( chain.CLastPoint(), this_arc.GetP1() );
1241
1242 int radius = ( c.m_geom.m_center_point - c.m_geom.m_start_point ).EuclideanNorm();
1243
1244 // Other points within accuracy + epsilon (for rounding) of where they should be
1246 ( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
1247
1249 ( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
1250}
1251
1252
1261BOOST_AUTO_TEST_CASE( TransformShallowArcToPolygon )
1262{
1263 SHAPE_POLY_SET buffer;
1264
1265 // Create an arc where the mid-point is only slightly off the start-end line.
1266 // This creates a very large radius arc that previously caused integer overflow.
1267 const VECTOR2I start( 0, 0 );
1268 const VECTOR2I end( 10000000, 0 ); // 10mm chord length
1269 const VECTOR2I mid( 5000000, 5 ); // Mid-point only 5nm off the line
1270
1271 const int width = 250000; // 0.25mm track width
1272 const int aError = 5000; // Default error tolerance
1273
1274 // This should not crash or produce invalid geometry
1275 TransformArcToPolygon( buffer, start, mid, end, width, aError, ERROR_INSIDE );
1276
1277 // Should produce at least one outline
1278 BOOST_CHECK( buffer.OutlineCount() >= 1 );
1279
1280 // The outline should be valid (closed, has points)
1281 if( buffer.OutlineCount() > 0 )
1282 {
1283 const SHAPE_LINE_CHAIN& outline = buffer.COutline( 0 );
1284 BOOST_CHECK( outline.IsClosed() );
1285 BOOST_CHECK( outline.PointCount() >= 3 );
1286
1287 // The bounding box should be reasonable (roughly the track width around the chord)
1288 BOX2I bbox = outline.BBox();
1289 BOOST_CHECK( bbox.GetWidth() <= end.x + width * 2 );
1290 BOOST_CHECK( bbox.GetHeight() <= width * 2 + 100 ); // Allow some tolerance
1291 }
1292}
1293
1294
1299BOOST_AUTO_TEST_CASE( TransformVeryShallowArcToPolygon )
1300{
1301 SHAPE_POLY_SET buffer;
1302
1303 // Create an arc that is effectively a straight line - mid-point essentially on the line.
1304 // This should be detected by IsEffectiveLine() and handled as a line segment.
1305 const VECTOR2I start( 0, 0 );
1306 const VECTOR2I end( 50000000, 0 ); // 50mm chord length
1307 const VECTOR2I mid( 25000000, 1 ); // Mid-point only 1nm off the line
1308
1309 const int width = 250000; // 0.25mm track width
1310 const int aError = 5000;
1311
1312 // This should not crash and should produce valid geometry
1313 TransformArcToPolygon( buffer, start, mid, end, width, aError, ERROR_INSIDE );
1314
1315 BOOST_CHECK( buffer.OutlineCount() >= 1 );
1316
1317 if( buffer.OutlineCount() > 0 )
1318 {
1319 const SHAPE_LINE_CHAIN& outline = buffer.COutline( 0 );
1320 BOOST_CHECK( outline.IsClosed() );
1321 BOOST_CHECK( outline.PointCount() >= 3 );
1322 }
1323}
1324
1325
1330BOOST_AUTO_TEST_CASE( TransformIssue22475ArcToPolygon )
1331{
1332 SHAPE_POLY_SET buffer;
1333
1334 // Values approximating one of the problematic arcs from issue #22475:
1335 // radius=24.35 mm, distToMid=12960 nm, chord=1.62 mm, angle=-3.8 deg
1336 // Compute start, mid, end points for such an arc
1337 const VECTOR2I start( 0, 0 );
1338 const VECTOR2I end( 1620000, 0 ); // 1.62mm chord length
1339 const VECTOR2I mid( 810000, 12960 ); // Mid-point 12960nm (12.96µm) off the line
1340
1341 const int width = 200000; // 0.2mm track width
1342 const int aError = 5000;
1343
1344 // Before the fix, this should create a polygon with very large extent
1345 // After the fix, it should create a reasonable oval-shaped polygon
1346 TransformArcToPolygon( buffer, start, mid, end, width, aError, ERROR_INSIDE );
1347
1348 BOOST_REQUIRE( buffer.OutlineCount() >= 1 );
1349
1350 const SHAPE_LINE_CHAIN& outline = buffer.COutline( 0 );
1351 BOOST_CHECK( outline.IsClosed() );
1352 BOOST_CHECK( outline.PointCount() >= 3 );
1353
1354 BOX2I bbox = outline.BBox();
1355
1356 // The bounding box should be reasonable - roughly chord + 2*width wide, 2*width high
1357 // With the fix (treating as oval), width should be ~1620000 + 2*200000 = 2020000
1358 // Height should be ~2*200000 = 400000
1359 // Without the fix, the height could be enormous due to the large arc radius
1360
1361 // Check that the polygon isn't ridiculously large
1362 BOOST_CHECK_MESSAGE( bbox.GetWidth() <= 3000000,
1363 wxString::Format( "Polygon width %lld is too large (expected ~2020000)", (long long)bbox.GetWidth() ) );
1364 BOOST_CHECK_MESSAGE( bbox.GetHeight() <= 1000000,
1365 wxString::Format( "Polygon height %lld is too large (expected ~400000)", (long long)bbox.GetHeight() ) );
1366}
1367
1368
1374BOOST_AUTO_TEST_CASE( CollideNearlyFlatArcDoesNotOverflow )
1375{
1376 // Values from the core dump: a nearly-flat arc with enormous radius
1377 const VECTOR2I start( 68208364, -8000 );
1378 const VECTOR2I mid( 771364, 500000 );
1379 const VECTOR2I end( 35224335, -7999 );
1380 const int width = 1270000;
1381
1382 SHAPE_ARC arc( start, mid, end, width );
1383
1384 // Radius should be near or above INT_MAX/2, triggering the segment fallback
1385 BOOST_CHECK( arc.GetRadius() >= (double) std::numeric_limits<int>::max() / 2.0 );
1386
1387 // Point near the arc endpoints. Must not crash.
1388 const VECTOR2I testPt( 35224298, -5381 );
1389 int actual = 0;
1391
1392 BOOST_CHECK_NO_THROW( arc.Collide( testPt, 635000, &actual, &location ) );
1393
1394 // Segment near the arc. Must not crash.
1395 const SEG testSeg( VECTOR2I( 35224298, -5381 ), VECTOR2I( 35696364, -32988651 ) );
1396
1397 BOOST_CHECK_NO_THROW( arc.Collide( testSeg, 635000, &actual, &location ) );
1398}
1399
1400
1412BOOST_AUTO_TEST_CASE( DegenerateArcCoincidentPoints )
1413{
1414 SHAPE_ARC arc( VECTOR2I( 135674000, 84576744 ),
1415 VECTOR2I( 135673999, 84576744 ),
1416 VECTOR2I( 135673998, 84576744 ),
1417 100000 );
1418
1419 BOOST_CHECK_LT( arc.GetRadius(), 10.0 );
1420
1422 BOOST_CHECK_LT( poly.BBox().GetWidth(), 1000 ); // < 1 µm
1423 BOOST_CHECK_LT( poly.BBox().GetHeight(), 1000 );
1424 BOOST_CHECK_LT( arc.GetLength(), 1000.0 );
1425}
1426
1427
1428// Collinear points from the reported board. The coincident start/mid sends CalcArcCenter() to
1429// the chord midpoint, which makes start and end antipodal and the raw sweep a clean half turn
1430BOOST_AUTO_TEST_CASE( CollinearArcSweepIsNotAFullTurn )
1431{
1432 const SHAPE_ARC arc( VECTOR2I( 2275000, 3123714 ),
1433 VECTOR2I( 2275000, 3123715 ),
1434 VECTOR2I( 2275000, 3123720 ),
1435 127000 );
1436
1437 BOOST_CHECK_LT( std::abs( arc.GetCentralAngle().AsDegrees() ), 1.0 );
1438
1439 // The 6 nm chord is the whole run; a fabricated sweep inflates this without bound
1440 BOOST_CHECK_CLOSE( arc.GetLength(), 6.0, 1.0 );
1441
1442 const SHAPE_LINE_CHAIN poly = arc.ConvertToPolyline();
1443 BOOST_CHECK_LT( poly.BBox().GetWidth(), 10 );
1444 BOOST_CHECK_LT( poly.BBox().GetHeight(), 10 );
1445}
1446
1447
1448// The guard must not catch major arcs, which are the only users of the full-turn correction
1449BOOST_AUTO_TEST_CASE( CurvedArcsKeepTheirSweep )
1450{
1451 const SHAPE_ARC quarter( VECTOR2I( 1000000, 0 ), VECTOR2I( 707107, 707107 ),
1452 VECTOR2I( 0, 1000000 ), 127000 );
1453
1454 BOOST_CHECK_CLOSE( quarter.GetCentralAngle().AsDegrees(), 90.0, 0.01 );
1455
1456 const SHAPE_ARC major( VECTOR2I( 1000000, 0 ), VECTOR2I( -1000000, 0 ),
1457 VECTOR2I( 0, -1000000 ), 127000 );
1458
1459 BOOST_CHECK_CLOSE( major.GetCentralAngle().AsDegrees(), 270.0, 0.01 );
1460}
1461
1462
1463// Coincident start/mid plus a distant end previously produced a center far off from the inputs
1464BOOST_AUTO_TEST_CASE( CalcArcCenterTwoCoincidentStartMid )
1465{
1466 const VECTOR2D start( 0.0, 0.0 );
1467 const VECTOR2D mid ( 0.0, 0.0 );
1468 const VECTOR2D end ( 1000.0, 0.0 );
1469
1470 VECTOR2D center = CalcArcCenter( start, mid, end );
1471
1472 BOOST_CHECK_CLOSE( center.x, 500.0, 1e-9 );
1473 BOOST_CHECK_SMALL( center.y, 1e-9 );
1474}
1475
1476
1477BOOST_AUTO_TEST_CASE( CalcArcCenterTwoCoincidentMidEnd )
1478{
1479 const VECTOR2D start( -1000.0, 0.0 );
1480 const VECTOR2D mid ( 0.0, 0.0 );
1481 const VECTOR2D end ( 0.0, 0.0 );
1482
1483 VECTOR2D center = CalcArcCenter( start, mid, end );
1484
1485 BOOST_CHECK_CLOSE( center.x, -500.0, 1e-9 );
1486 BOOST_CHECK_SMALL( center.y, 1e-9 );
1487}
1488
1489
1490BOOST_AUTO_TEST_CASE( CalcArcCenterTwoCoincidentStartEnd )
1491{
1492 // Coincident start/end with a distinct mid is a 360-degree arc, center is midpoint to mid
1493 const VECTOR2D start( 0.0, 0.0 );
1494 const VECTOR2D mid ( 1000.0, 0.0 );
1495 const VECTOR2D end ( 0.0, 0.0 );
1496
1497 VECTOR2D center = CalcArcCenter( start, mid, end );
1498
1499 BOOST_CHECK_CLOSE( center.x, 500.0, 1e-9 );
1500 BOOST_CHECK_SMALL( center.y, 1e-9 );
1501}
1502
1503
1504// Three near-coincident points collapse to the centroid via the bbox guard, not the pairwise guard
1505BOOST_AUTO_TEST_CASE( CalcArcCenterThreeNearCoincident )
1506{
1507 const VECTOR2D start( 100.0, 200.0 );
1508 const VECTOR2D mid ( 101.0, 200.0 );
1509 const VECTOR2D end ( 100.0, 201.0 );
1510
1511 VECTOR2D center = CalcArcCenter( start, mid, end );
1512
1513 BOOST_CHECK_CLOSE( center.x, ( start.x + mid.x + end.x ) / 3.0, 1e-9 );
1514 BOOST_CHECK_CLOSE( center.y, ( start.y + mid.y + end.y ) / 3.0, 1e-9 );
1515}
1516
1517
1518// A thin arc must not trip the coincident-point guards despite its short chord and small sagitta
1519BOOST_AUTO_TEST_CASE( CalcArcCenterThinArcNotDegenerate )
1520{
1521 const VECTOR2D start( 0.0, 0.0 );
1522 const VECTOR2D mid ( 10.0, 1.0 );
1523 const VECTOR2D end ( 20.0, 0.0 );
1524
1525 VECTOR2D center = CalcArcCenter( start, mid, end );
1526
1527 double rs = ( center - start ).EuclideanNorm();
1528 double rm = ( center - mid ).EuclideanNorm();
1529 double re = ( center - end ).EuclideanNorm();
1530
1531 // Snapping to the 10 nm grid moves the center off the exact (10, -49.5) by half an IU
1532 BOOST_CHECK_SMALL( rs - rm, 1.0 );
1533 BOOST_CHECK_SMALL( rm - re, 1.0 );
1534
1535 // True circumradius is 50.5 IU; a wrongly-triggered midpoint guard would give ~10 IU
1536 BOOST_CHECK_GT( rs, 40.0 );
1537}
1538
1539
1540// A few-IU arc rounds its own mid point off the true circle, but the three points still span a
1541// healthy triangle. Reading that as a coincident pair collapses the centre onto the chord and
1542// turns a quarter turn into a reflex sweep
1543BOOST_AUTO_TEST_CASE( CalcArcCenterFewUnitArcKeepsItsCircumcircle )
1544{
1545 const SHAPE_ARC arc( VECTOR2I( 0, 0 ), VECTOR2I( 5, 0 ), ANGLE_90 );
1546
1547 BOOST_CHECK_LT( std::abs( arc.GetCentralAngle().AsDegrees() ), 180.0 );
1548
1549 // The chord midpoint fallback sits at (3, 3), inside the arc it is supposed to circumscribe
1550 BOOST_CHECK_GT( ( arc.GetCenter() - arc.GetArcMid() ).EuclideanNorm(), arc.GetRadius() / 2.0 );
1551}
1552
1553
1554// A board-scale arc must not regress from the new pairwise coincidence guards
1555BOOST_AUTO_TEST_CASE( CalcArcCenterBoardScaleSanity )
1556{
1557 const double R = 50000000.0;
1558 const VECTOR2D start( R, 0.0 );
1559 const VECTOR2D mid ( 0.0, R );
1560 const VECTOR2D end ( -R, 0.0 );
1561
1562 VECTOR2D center = CalcArcCenter( start, mid, end );
1563
1564 BOOST_CHECK_SMALL( center.x, 1.0 );
1565 BOOST_CHECK_SMALL( center.y, 1.0 );
1566}
1567
1568namespace
1569{
1570// Circumcenter of integer points relative to start, in long double
1571VECTOR2<long double> exactCircumcenter( const VECTOR2I& aS, const VECTOR2I& aM, const VECTOR2I& aE )
1572{
1573 long double bx = (long double) aM.x - aS.x, by = (long double) aM.y - aS.y;
1574 long double cx = (long double) aE.x - aS.x, cy = (long double) aE.y - aS.y;
1575 long double b2 = bx * bx + by * by, c2 = cx * cx + cy * cy;
1576 long double d = 2.0L * ( bx * cy - by * cx );
1577
1578 return VECTOR2<long double>( aS.x + ( b2 * cy - c2 * by ) / d, aS.y + ( c2 * bx - b2 * cx ) / d );
1579}
1580
1581
1582// Point on the circle about aCenter, rounded to integer IU
1583VECTOR2I arcPoint( const VECTOR2D& aCenter, double aRadius, double aDegrees, const VECTOR2I& aOffset )
1584{
1585 double a = aDegrees * M_PI / 180.0;
1586
1587 return VECTOR2I( KiROUND( aCenter.x + aRadius * std::cos( a ) ) + aOffset.x,
1588 KiROUND( aCenter.y + aRadius * std::sin( a ) ) + aOffset.y );
1589}
1590} // namespace
1591
1592
1593// A 150 um arc must get the same center wherever it sits. The slope formulation drifted by
1594// 147 nm at 10 cm and 1244 nm at 1 m
1595BOOST_AUTO_TEST_CASE( CalcArcCenterTranslationInvariant )
1596{
1597 const VECTOR2D c0( 18833.0, 51544.24 );
1598 const double radius = 150000.0;
1599 const int offsets[] = { 0, 100000000, 1000000000, -1000000000 };
1600
1601 VECTOR2I baseCenter;
1602
1603 for( int off : offsets )
1604 {
1605 const VECTOR2I o( off, off );
1606 VECTOR2I s = arcPoint( c0, radius, 20.0, o );
1607 VECTOR2I m = arcPoint( c0, radius, 60.0, o );
1608 VECTOR2I e = arcPoint( c0, radius, 100.0, o );
1609
1610 VECTOR2I center = CalcArcCenter( s, m, e );
1611 VECTOR2<long double> exact = exactCircumcenter( s, m, e );
1612 VECTOR2D dcenter = CalcArcCenter( VECTOR2D( s ), VECTOR2D( m ), VECTOR2D( e ) );
1613
1614 BOOST_CHECK_LE( std::abs( (long double) center.x - exact.x ), 1.5L );
1615 BOOST_CHECK_LE( std::abs( (long double) center.y - exact.y ), 1.5L );
1616
1617 double rs = ( dcenter - VECTOR2D( s ) ).EuclideanNorm();
1618 double re = ( dcenter - VECTOR2D( e ) ).EuclideanNorm();
1619 BOOST_CHECK_LE( std::abs( rs - re ), 1.0 );
1620
1621 if( off == 0 )
1622 baseCenter = center;
1623 else
1624 BOOST_CHECK_LE( ( center - o - baseCenter ).EuclideanNorm(), 1.5 );
1625 }
1626}
1627
1628
1629// Codex found the slope form returning (1e9 + 5, 1e9) for this right-angled arc
1630BOOST_AUTO_TEST_CASE( CalcArcCenterFarFromOrigin )
1631{
1632 const VECTOR2I s( 1000000000, 1000000000 );
1633 const VECTOR2I m( 1000000010, 1000000000 );
1634 const VECTOR2I e( 1000000000, 1000000010 );
1635
1636 BOOST_CHECK_EQUAL( CalcArcCenter( s, m, e ), VECTOR2I( 1000000005, 1000000005 ) );
1638 VECTOR2D( 1000000005.0, 1000000005.0 ) );
1639}
1640
1641
1642// A center a nm or two off the 100 nm grid snaps to it, since every radius still agrees
1643BOOST_AUTO_TEST_CASE( CalcArcCenterSnapsToGridWhenRadiiAgree )
1644{
1645 const VECTOR2I c( 1000000, 2000000 );
1646 const VECTOR2I s = c + VECTOR2I( 250000, 0 );
1647 const VECTOR2I m = c + VECTOR2I( 150000, 200001 );
1648 const VECTOR2I e = c + VECTOR2I( -150000, 200000 );
1649
1650 VECTOR2<long double> exact = exactCircumcenter( s, m, e );
1651
1652 BOOST_CHECK_GT( std::abs( exact.x - c.x ) + std::abs( exact.y - c.y ), 1.0L );
1653 BOOST_CHECK_EQUAL( CalcArcCenter( s, m, e ), c );
1655}
1656
1657
1658// The 10 nm grid point would put the radii 4 nm apart, so the exact center is kept
1659BOOST_AUTO_TEST_CASE( CalcArcCenterKeepsExactWhenSnapBreaksRadii )
1660{
1661 const VECTOR2I s( 0, 0 );
1662 const VECTOR2I m( 10, 0 );
1663 const VECTOR2I e( 0, 10 );
1664
1665 BOOST_CHECK_EQUAL( CalcArcCenter( s, m, e ), VECTOR2I( 5, 5 ) );
1666}
1667
1668// Near a half turn r^2 - chord^2/4 cancels to nothing; the center must sit at the true offset
1669BOOST_AUTO_TEST_CASE( CalcArcCenterFromAngleNearHalfTurn )
1670{
1671 const VECTOR2D start( 0.0, 0.0 );
1672 const VECTOR2D end( 1.0e9, 0.0 );
1673 const EDA_ANGLE angle( 179.999999, DEGREES_T );
1674
1675 VECTOR2D center = CalcArcCenter( start, end, angle );
1676
1677 long double half = angle.AsRadians() / 2.0L;
1678 long double offset = 5.0e8L * std::cos( half ) / std::sin( half );
1679
1680 BOOST_CHECK_LE( std::abs( (long double) center.x - 5.0e8L ), 1e-3L );
1681 BOOST_CHECK_LE( std::abs( std::abs( (long double) center.y ) - offset ), 0.1L );
1682}
1683
1684// The center is rounded once from an exact numerator, so a .4999999 fraction cannot be pushed up
1685BOOST_AUTO_TEST_CASE( CalcArcCenterRoundsOnceNearHalf )
1686{
1687 const VECTOR2I s( -2123435277, -1114039370 );
1688 const VECTOR2I m( -1828331619, -517415848 );
1689 const VECTOR2I e( 2011509719, -1246150838 );
1690
1691 BOOST_CHECK_EQUAL( CalcArcCenter( s, m, e ), VECTOR2I( -74371215, -1756259108 ) );
1692}
1693
1694
1695// GetClearance passes an INT_MAX / 2 sentinel that must not overflow the arc bounding box
1696BOOST_AUTO_TEST_CASE( GetClearanceAgainstSegment )
1697{
1698 const SHAPE_ARC arc( VECTOR2I( 0, 0 ), VECTOR2I( 1000000, 0 ), ANGLE_90, 50000 );
1699 const SHAPE_SEGMENT seg( VECTOR2I( 2000000, 0 ), VECTOR2I( 2000000, 0 ), 50000 );
1700
1701 // Gap between the arc start point and the segment minus both half widths
1702 BOOST_CHECK_EQUAL( arc.GetClearance( &seg ), 1000000 - 25000 - 25000 );
1703 BOOST_CHECK_EQUAL( seg.GetClearance( &arc ), 1000000 - 25000 - 25000 );
1704}
1705
1706
1707// Beyond the search cutoff no collision is reported, which must not read as zero clearance
1708BOOST_AUTO_TEST_CASE( GetClearanceBeyondCutoff )
1709{
1710 const SHAPE_SEGMENT a( VECTOR2I( 0, 0 ), VECTOR2I( 0, 0 ) );
1711 const SHAPE_SEGMENT b( VECTOR2I( 1500000000, 0 ), VECTOR2I( 1500000000, 0 ) );
1712
1713 BOOST_CHECK_EQUAL( a.GetClearance( &b ), std::numeric_limits<int>::max() );
1714}
1715
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
constexpr const Vec & GetPosition() const
Definition box2.h:208
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
double AsDegrees() const
Definition eda_angle.h:115
double AsRadians() const
Definition eda_angle.h:119
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:247
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:215
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:283
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:220
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
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
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:181
int GetClearance(const SHAPE *aOther) const
Return the actual minimum distance between two shapes.
Definition shape.cpp:51
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
Definition shape.h:129
Define a general 2D-vector/point.
Definition vector2d.h:67
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.
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
@ DEGREES_T
Definition eda_angle.h:30
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
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
Numerical test predicates.
const double epsilon
@ SH_ARC
circular arc
Definition shape.h:50
static bool Collide(const SHAPE_CIRCLE &aA, const SHAPE_CIRCLE &aB, int aClearance, int *aActual, VECTOR2I *aLocation, VECTOR2I *aMTV)
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)
VECTOR3I expected(15, 30, 45)
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
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
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
#define M_PI
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition trigo.cpp:506
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707