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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25#include <boost/test/data/test_case.hpp>
26
27#include <limits>
28
30#include <geometry/shape_arc.h>
33
35#include <qa_utils/numeric.h>
36
37#include "geom_test_utils.h"
38
39BOOST_AUTO_TEST_SUITE( ShapeArc )
40
41
56
63static void CheckArcGeom( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps, const int aSynErrIU = 1 )
64{
65 // Angular error - note this can get quite large for very small arcs,
66 // as the integral position rounding has a relatively greater effect
67 const double angle_tol_deg = 2.0;
68
69 // Position error - rounding to nearest integer
70 const int pos_tol = 1;
71
73 ( aProps.m_start_point )( aProps.m_start_point )( pos_tol ) );
74
76 ( aArc.GetP1() )( aProps.m_end_point )( pos_tol ) );
77
79 ( aArc.GetCenter() )( aProps.m_center_point )( aSynErrIU ) );
80
82 ( aArc.GetCentralAngle().AsDegrees() )( aProps.m_center_angle )( 360.0 )( angle_tol_deg ) );
83
85 ( aArc.GetStartAngle().AsDegrees() )( aProps.m_start_angle )( 360.0 )( angle_tol_deg ) );
86
88 ( aArc.GetEndAngle().AsDegrees() )( aProps.m_end_angle )( 360.0 )( angle_tol_deg ) );
89
91 ( aArc.GetRadius() )( aProps.m_radius )( aSynErrIU ) );
92
93 // Angle normalization contracts
94 BOOST_TEST( aArc.GetStartAngle().AsDegrees() >= 0.0 );
95 BOOST_TEST( aArc.GetStartAngle().AsDegrees() <= 360.0 );
96
97 BOOST_TEST( aArc.GetEndAngle().AsDegrees() >= 0.0 );
98 BOOST_TEST( aArc.GetEndAngle().AsDegrees() <= 360.0 );
99
100 BOOST_TEST( aArc.GetCentralAngle().AsDegrees() >= -360.0 );
101 BOOST_TEST( aArc.GetCentralAngle().AsDegrees() <= 360.0 );
102
104 const SEG chord = aArc.GetChord();
105
107 ( chord.A )( aProps.m_start_point )( pos_tol ) );
108
110 ( chord.B )( aProps.m_end_point )( pos_tol ) );
111
113 BOOST_CHECK_EQUAL( aArc.IsSolid(), true );
114
116 ( aArc.BBox() )( aProps.m_bbox )( pos_tol ) );
117
119}
120
121
128static void CheckArc( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps, const int aSynErrIU = 1 )
129{
130 // Check the original arc
131 CheckArcGeom( aArc, aProps, aSynErrIU );
132
133 // Test the Clone function (also tests copy-ctor)
134 std::unique_ptr<SHAPE> new_shape{ aArc.Clone() };
135
136 BOOST_REQUIRE_EQUAL( new_shape->Type(), SH_ARC );
137
138 SHAPE_ARC* new_arc = dynamic_cast<SHAPE_ARC*>( new_shape.get() );
139
140 BOOST_REQUIRE( new_arc != nullptr );
141
143 CheckArcGeom( *new_arc, aProps, aSynErrIU );
144}
145
150{
151 auto arc = SHAPE_ARC();
152
153 BOOST_CHECK_EQUAL( arc.GetWidth(), 0 );
154
155 static ARC_PROPERTIES null_props{
156 { 0, 0 },
157 { 0, 0 },
158 { 0, 0 },
159 0,
160 0,
161 0,
162 0,
163 };
164
165 CheckArc( arc, null_props );
166}
167
168
178
190
191
192static const std::vector<ARC_SME_CASE> arc_sme_cases = {
193 {
194 "S(-100,0), M(0,100), E(100,0)",
195 {
196 { -100, 0 },
197 { 0, 100 },
198 { 100, 0 },
199 },
200 0,
201 {
202 { 0, 0 },
203 { -100, 0 },
204 { 100, 0 },
205 180,
206 180,
207 0,
208 100,
209 { { -100, 0 }, { 200, 100 } },
210 },
211 },
212 {
213 "S(100,0), M(0,100), E(-100,0) (reversed)",
214 {
215 { 100, 0 },
216 { 0, 100 },
217 { -100, 0 },
218 },
219 0,
220 {
221 { 0, 0 },
222 { 100, 0 },
223 { -100, 0 },
224 -180,
225 0,
226 180,
227 100,
228 { { -100, 0 }, { 200, 100 } },
229 },
230 },
231 {
232 // This data has a midpoint not exactly at the midway point of the arc.
233 // This should be corrected by the constructor.
234 // The mid point should be at about (-71, -71) for a 270 degree arc, with the
235 // bottom right quadrant open.
236 "S(100,0), M(-100,0), E(0,100) (bad midpoint)",
237 {
238 { 100, 0 },
239 { -100, 0 },
240 { 0, 100 },
241 },
242 0,
243 {
244 { 0, 0 },
245 { 100, 0 },
246 { 0, 100 },
247 -270,
248 0,
249 90,
250 100,
251 { { -100, -100 }, { 200, 200 } },
252 },
253 }
254};
255
256
257BOOST_DATA_TEST_CASE( BasicSMEGeom, boost::unit_test::data::make( arc_sme_cases ), c )
258{
259 const SHAPE_ARC this_arc{
260 c.m_geom.m_start_point,
261 c.m_geom.m_mid_point,
262 c.m_geom.m_end_point,
263 c.m_width,
264 };
265
266 CheckArc( this_arc, c.m_properties );
267}
268
269
279
280
292
293
294static const std::vector<ARC_CPA_CASE> arc_cases = {
295 {
296 "C(0,0) 114 + 360 degree",
297 {
298 { 0, 0 },
299 { -306451, 687368 },
300 360,
301 },
302 0,
303 {
304 { 0, 0 },
305 { -306451, 687368 },
306 { -306451, 687368 },
307 360,
308 113.95929,
309 113.95929,
310 752587,
311 { { -752587, -752587 }, { 1505174, 1505174 } },
312 },
313 },
314 {
315 "C(0,0) 180 + 360 degree",
316 {
317 { 0, 0 },
318 { -100, 0 },
319 360,
320 },
321 0,
322 {
323 { 0, 0 },
324 { -100, 0 },
325 { -100, 0 },
326 360,
327 180,
328 180,
329 100,
330 { { -100, -100 }, { 200, 200 } },
331 },
332 },
333 {
334 "C(0,0) 180 + 90 degree",
335 {
336 { 0, 0 },
337 { -100, 0 },
338 90,
339 },
340 0,
341 {
342 { 0, 0 },
343 { -100, 0 },
344 { 0, -100 },
345 90,
346 180,
347 270,
348 100,
349 { { -100, -100 }, { 100, 100 } },
350 },
351 },
352 {
353 "C(100,200) 0 - 30 degree",
354 {
355 { 100, 200 },
356 { 300, 200 },
357 -30,
358 },
359 0,
360 {
361 { 100, 200 },
362 { 300, 200 },
363 { 273, 100 }, // 200 * sin(30) = 100, 200* cos(30) = 173
364 -30,
365 0,
366 330,
367 200,
368 { { 273, 100 }, { 27, 100 } },
369 },
370 },
371 {
372 // This is a "fan shape" which includes the top quadrant point,
373 // so it exercises the bounding box code (centre and end points
374 // do not contain the top quadrant)
375 "C(0,0) 30 + 120 degree",
376 {
377 { 0, 0 },
378 { 17320, 10000 },
379 120,
380 },
381 0,
382 {
383 { 0, 0 },
384 { 17320, 10000 },
385 { -17320, 10000 }, // 200 * sin(30) = 100, 200* cos(30) = 173
386 120,
387 30,
388 150,
389 20000,
390 // bbox defined by: centre, top quadrant point, two endpoints
391 { { -17320, 10000 }, { 17320 * 2, 10000 } },
392 },
393 },
394 {
395 // An arc that covers three quadrant points (L/R, bottom)
396 "C(0,0) 150 + 240 degree",
397 {
398 { 0, 0 },
399 { -17320, 10000 },
400 240,
401 },
402 0,
403 {
404 { 0, 0 },
405 { -17320, 10000 },
406 { 17320, 10000 },
407 240,
408 150,
409 30,
410 20000,
411 // bbox defined by: L/R quads, bottom quad and start/end
412 { { -20000, -20000 }, { 40000, 30000 } },
413 },
414 },
415 {
416 // Same as above but reverse direction
417 "C(0,0) 30 - 300 degree",
418 {
419 { 0, 0 },
420 { 17320, 10000 },
421 -240,
422 },
423 0,
424 {
425 { 0, 0 },
426 { 17320, 10000 },
427 { -17320, 10000 },
428 -240,
429 30,
430 150,
431 20000,
432 // bbox defined by: L/R quads, bottom quad and start/end
433 { { -20000, -20000 }, { 40000, 30000 } },
434 },
435 },
436};
437
438
439BOOST_DATA_TEST_CASE( BasicCPAGeom, boost::unit_test::data::make( arc_cases ), c )
440{
441 const SHAPE_ARC this_arc{
442 c.m_geom.m_center_point,
443 c.m_geom.m_start_point,
444 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ),
445 c.m_width,
446 };
447
448 CheckArc( this_arc, c.m_properties );
449}
450
451
452
462
463
475
476
477static const std::vector<ARC_TTR_CASE> arc_ttr_cases = {
478 {
479 "90 degree segments intersecting",
480 {
481 { 0, 0, 0, 1000 },
482 { 0, 0, 1000, 0 },
483 1000,
484 },
485 0,
486 {
487 { 1000, 1000 },
488 { 0, 1000 }, //start on first segment
489 { 1000, 0 }, //end on second segment
490 90, //positive angle due to start/end
491 180,
492 270,
493 1000,
494 { { 0, 0 }, { 1000, 1000 } },
495 }
496 },
497 {
498 "45 degree segments intersecting",
499 {
500 { 0, 0, 0, 1000 },
501 { 0, 0, 1000, 1000 },
502 1000,
503 },
504 0,
505 {
506 { 1000, 2414 },
507 { 0, 2414 }, //start on first segment
508 { 1707, 1707 }, //end on second segment
509 135, //positive angle due to start/end
510 180,
511 315,
512 1000,
513 { { 0, 1414 }, { 1707, 1000 } },
514 }
515 },
516 {
517 "135 degree segments intersecting",
518 {
519 { 0, 0, 0, 1000 },
520 { 0, 0, 1000, -1000 },
521 1000,
522 },
523 0,
524 {
525 { 1000, 414 },
526 { 0, 414 }, //start on first segment ( radius * tan(45 /2) )
527 { 293, -293 }, //end on second segment (radius * 1-cos(45)) )
528 45, //positive angle due to start/end
529 180,
530 225,
531 1000,
532 { { 0, -293 }, { 293, 707 } },
533 }
534 }
535
536
537};
538
539
540BOOST_DATA_TEST_CASE( BasicTTRGeom, boost::unit_test::data::make( arc_ttr_cases ), c )
541{
542 for( int testCase = 0; testCase < 8; ++testCase )
543 {
544 SEG seg1 = c.m_geom.m_segment_1;
545 SEG seg2 = c.m_geom.m_segment_2;
546 ARC_PROPERTIES props = c.m_properties;
547
548 if( testCase > 3 )
549 {
550 //Swap input segments.
551 seg1 = c.m_geom.m_segment_2;
552 seg2 = c.m_geom.m_segment_1;
553
554 //The result should swap start and end points and invert the angles:
555 props.m_end_point = c.m_properties.m_start_point;
556 props.m_start_point = c.m_properties.m_end_point;
557 props.m_start_angle = c.m_properties.m_end_angle;
558 props.m_end_angle = c.m_properties.m_start_angle;
559 props.m_center_angle = -c.m_properties.m_center_angle;
560 }
561
562 //Test all combinations of start and end points for the segments
563 if( ( testCase % 4 ) == 1 || ( testCase % 4 ) == 3 )
564 {
565 //Swap start and end points for seg1
566 VECTOR2I temp = seg1.A;
567 seg1.A = seg1.B;
568 seg1.B = temp;
569 }
570
571 if( ( testCase % 4 ) == 2 || ( testCase % 4 ) == 3 )
572 {
573 //Swap start and end points for seg2
574 VECTOR2I temp = seg2.A;
575 seg2.A = seg2.B;
576 seg2.B = temp;
577 }
578
579 const auto this_arc = SHAPE_ARC{ seg1, seg2,
580 c.m_geom.m_radius, c.m_width };
581
582 // Error of 4 IU permitted for the center and radius calculation
584 }
585}
586
587
591struct ARC_START_END_CENTER
592{
593 VECTOR2I m_start;
594 VECTOR2I m_end;
595 VECTOR2I m_center;
596};
597
598
600{
602 ARC_START_END_CENTER m_geom;
603
606
609};
610
611
612
613static const std::vector<ARC_SEC_CASE> arc_sec_cases = {
614 { "180 deg, clockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } }, true, { 50, -50 } },
615 { "180 deg, anticlockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } }, false, { 50, 50 } },
616 { "180 deg flipped, clockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } }, true, { 50, 50 } },
617 { "180 deg flipped, anticlockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } }, false, { 50, -50 } },
618 { "90 deg, clockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } }, true, { -71, 71 } },
619 { "90 deg, anticlockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } }, false, { 71, -71 } },
620};
621
622
623BOOST_DATA_TEST_CASE( BasicSECGeom, boost::unit_test::data::make( arc_sec_cases ), c )
624{
625 VECTOR2I start = c.m_geom.m_start;
626 VECTOR2I end = c.m_geom.m_end;
627 VECTOR2I center = c.m_geom.m_center;
628 bool cw = c.m_clockwise;
629
631 this_arc.ConstructFromStartEndCenter( start, end, center, cw );
632
633 BOOST_CHECK_EQUAL( this_arc.GetArcMid(), c.m_expected_mid );
634}
635
636
646
647static const std::vector<ARC_CICLE_COLLIDE_CASE> arc_circle_collide_cases = {
648 { " Issue 20336, large arc", { { 183000000, 65710001}, {150496913, 147587363},{116291153, 66406583}}, 2000000 / 2, {116300000, 133100000}, 300000, true, 53319 }
649};
650
651
652BOOST_DATA_TEST_CASE( CollideCircle, boost::unit_test::data::make( arc_circle_collide_cases ), c )
653{
654 SHAPE_ARC arc( c.m_geom.m_start_point, c.m_geom.m_mid_point, c.m_geom.m_end_point, 0 );
655 SHAPE_CIRCLE circle( c.m_circle_center, c.m_circle_radius );
656
657 // Test a zero width arc (distance should equal the clearance)
658 BOOST_TEST_CONTEXT( "Test Clearance" )
659 {
660 int dist = -1;
661 BOOST_CHECK_EQUAL( arc.Collide( &circle, c.m_arc_clearance, &dist ), c.m_exp_result );
662 BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
663 }
664
665 // Test by changing the width of the arc (distance should equal zero)
666 BOOST_TEST_CONTEXT( "Test Width" )
667 {
668 int dist = -1;
669 arc.SetWidth( c.m_arc_clearance * 2 );
670 BOOST_CHECK_EQUAL( arc.Collide( &circle, 0, &dist ), c.m_exp_result );
671
672 if( c.m_exp_result )
673 BOOST_CHECK_EQUAL( dist, 0 );
674 else
675 BOOST_CHECK_EQUAL( dist, -1 );
676 }
677}
678
679
688
689
690static const std::vector<ARC_PT_COLLIDE_CASE> arc_pt_collide_cases = {
691 { " 270deg, 0 cl, 0 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 100, 0 }, true, 0 },
692 { " 270deg, 0 cl, 90 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 0, 100 }, true, 0 },
693 { " 270deg, 0 cl, 180 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { -100, 0 }, true, 0 },
694 { " 270deg, 0 cl, 270 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 0, -100 }, true, 0 },
695 { " 270deg, 0 cl, 45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 71, 71 }, true, 0 },
696 { " 270deg, 0 cl, -45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 71, -71 }, false, -1 },
697 { "-270deg, 0 cl, 0 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 100, 0 }, true, 0 },
698 { "-270deg, 0 cl, 90 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 0, 100 }, true, 0 },
699 { "-270deg, 0 cl, 180 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { -100, 0 }, true, 0 },
700 { "-270deg, 0 cl, 270 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 0, -100 }, true, 0 },
701 { "-270deg, 0 cl, 45 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 71, 71 }, false, -1 },
702 { "-270deg, 0 cl, -45 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 71, -71 }, true, 0 },
703 { " 270deg, 5 cl, 0 deg, 5 pos X", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 105, 0 }, true, 5 },
704 { " 270deg, 5 cl, 0 deg, 5 pos Y", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 100, -5 }, true, 5 },
705 { " 270deg, 5 cl, 90 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, 105 }, true, 5 },
706 { " 270deg, 5 cl, 180 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { -105, 0 }, true, 5 },
707 { " 270deg, 5 cl, 270 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, -105 }, true, 5 },
708 { " 270deg, 5 cl, 0 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 105, 0 }, true, 5 },
709 { " 270deg, 5 cl, 90 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, 105 }, true, 5 },
710 { " 270deg, 5 cl, 180 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { -105, 0 }, true, 5 },
711 { " 270deg, 5 cl, 270 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, -105 }, true, 5 },
712 { " 270deg, 5 cl, 45 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 74, 75 }, true, 5 }, // 74.246, -74.246
713 { " 270deg, 5 cl, -45 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 74, -75 }, false, -1 }, //74.246, -74.246
714 { " 270deg, 5 cl, 45 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 67, 67 }, true, 5 }, // 67.17, 67.17
715 { " 270deg, 5 cl, -45 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 67, -67 }, false, -1 }, // 67.17, -67.17
716 { " 270deg, 4 cl, 0 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 105, 0 }, false, -1 },
717 { " 270deg, 4 cl, 90 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 0, 105 }, false, -1 },
718 { " 270deg, 4 cl, 180 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { -105, 0 }, false, -1 },
719 { " 270deg, 4 cl, 270 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 0, -105 }, false, -1 },
720 { " 90deg, 0 cl, 0 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 71, -71 }, true, 0 },
721 { " 90deg, 0 cl, 45 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 100, 0 }, true, 0 },
722 { " 90deg, 0 cl, 90 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 71, 71 }, true, 0 },
723 { " 90deg, 0 cl, 135 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 0, -100 }, false, -1 },
724 { " 90deg, 0 cl, -45 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 0, 100 }, false, -1 },
725 { " -90deg, 0 cl, 0 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 71, -71 }, true, 0 },
726 { " -90deg, 0 cl, 45 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 100, 0 }, true, 0 },
727 { " -90deg, 0 cl, 90 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 71, 71 }, true, 0 },
728 { " -90deg, 0 cl, 135 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 0, -100 }, false, -1 },
729 { " -90deg, 0 cl, -45 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 0, 100 }, false, -1 },
730 { "issue 11358 collide",
731 { { 119888000, 60452000 }, { 120904000, 60452000 }, 360.0 },
732 0,
733 { 120395500, 59571830 },
734 true,
735 0 },
736 { "issue 11358 dist",
737 { { 119888000, 60452000 }, { 120904000, 60452000 }, 360.0 },
738 100,
739 { 118872050, 60452000 },
740 true,
741 50 },
742};
743
744
745BOOST_DATA_TEST_CASE( CollidePt, boost::unit_test::data::make( arc_pt_collide_cases ), c )
746{
747 SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
748 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
749
750 // Test a zero width arc (distance should equal the clearance)
751 BOOST_TEST_CONTEXT( "Test Clearance" )
752 {
753 int dist = -1;
754 BOOST_CHECK_EQUAL( arc.Collide( c.m_point, c.m_arc_clearance, &dist ),
755 c.m_exp_result );
756 BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
757 }
758
759 // Test by changing the width of the arc (distance should equal zero)
760 BOOST_TEST_CONTEXT( "Test Width" )
761 {
762 int dist = -1;
763 arc.SetWidth( c.m_arc_clearance * 2 );
764 BOOST_CHECK_EQUAL( arc.Collide( c.m_point, 0, &dist ), c.m_exp_result );
765
766 if( c.m_exp_result )
767 BOOST_CHECK_EQUAL( dist, 0 );
768 else
769 BOOST_CHECK_EQUAL( dist, -1 );
770 }
771}
772
782
783
784static const std::vector<ARC_SEG_COLLIDE_CASE> arc_seg_collide_cases = {
785 { "0 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 100, 0 }, { 50, 0 } }, true, 0, { 100, 0 } },
786 { "90 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 0, 100 }, { 0, 50 } }, true, 0, { 0, 100 } },
787 { "180 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { -100, 0 }, { -50, 0 } }, true, 0, { -100, 0 } },
788 { "270 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 0, -100 }, { 0, -50 } }, true, 0, { 0, -100 } },
789 { "45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 71, 71 }, { 35, 35 } }, true, 0, { 70, 70 } },
790 { "-45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 71, -71 }, { 35, -35 } }, false, -1, { 0, 0 } },
791 { "seg inside arc start", { { 0, 0 }, { 71, -71 }, 90.0 },
792 10, { { 90, 0 }, { -35, 0 } }, true, 10, { 100, 0 } },
793 { "seg inside arc end", { { 0, 0 }, { 71, -71 }, 90.0 },
794 10, { { -35, 0 }, { 90, 0 } }, true, 10, { 100, 0 } },
795 { "large diameter arc", { { 172367922, 82282076 }, { 162530000, 92120000 }, -45.0 },
796 433300, { { 162096732, 92331236 }, { 162096732, 78253268 } }, true, 433268, { 162530000, 92120000 } },
797 { "upside down collide", { { 26250000, 16520000 }, { 28360000, 16520000 }, 90.0 },
798 0, { { 27545249, 18303444 }, { 27545249, 18114500 } }, true, 0, { 27545249, 18185662 } }
799};
800
801
802BOOST_DATA_TEST_CASE( CollideSeg, boost::unit_test::data::make( arc_seg_collide_cases ), c )
803{
804 SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
805 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
806
807 // Test a zero width arc (distance should equal the clearance)
808 BOOST_TEST_CONTEXT( "Test Clearance" )
809 {
810 int dist = -1;
811 BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, c.m_arc_clearance, &dist ),
812 c.m_exp_result );
813 BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
814 }
815
816 // Test by changing the width of the arc (distance should equal zero)
817 BOOST_TEST_CONTEXT( "Test Width" )
818 {
819 int dist = -1;
820 arc.SetWidth( c.m_arc_clearance * 2 );
821 BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, 0, &dist ), c.m_exp_result );
822
823 if( c.m_exp_result )
824 BOOST_CHECK_EQUAL( dist, 0 );
825 else
826 BOOST_CHECK_EQUAL( dist, -1 );
827 }
828
829 BOOST_TEST_CONTEXT( "Test Collide Point" )
830 {
831 VECTOR2I collide_point;
832 int dist = -1;
833
834 if( c.m_exp_result )
835 {
836 arc.Collide( c.m_seg, c.m_arc_clearance, &dist, &collide_point );
837 BOOST_CHECK_EQUAL( collide_point, c.m_collide_point );
838 }
839 }
840}
841
843{
844 // Coordinates and dimensions in millimeters
847 double m_start_x;
848 double m_start_y;
850 double m_width;
851
853 {
854 SHAPE_ARC arc( VECTOR2D( pcbIUScale.mmToIU( m_center_x ), pcbIUScale.mmToIU( m_center_y ) ),
855 VECTOR2D( pcbIUScale.mmToIU( m_start_x ), pcbIUScale.mmToIU( m_start_y ) ),
857
858 return arc;
859 }
860};
861
862
870
871
872static const std::vector<ARC_ARC_COLLIDE_CASE> arc_arc_collide_cases = {
873 { "case 1: No intersection",
874 { 73.843527, 74.355869, 71.713528, 72.965869, -76.36664803, 0.2 },
875 { 71.236473, 74.704131, 73.366472, 76.094131, -76.36664803, 0.2 },
876 0,
877 false },
878 { "case 2: No intersection",
879 { 82.542335, 74.825975, 80.413528, 73.435869, -76.4, 0.2 },
880 { 76.491192, 73.839894, 78.619999, 75.23, -76.4, 0.2 },
881 0,
882 false },
883 { "case 3: No intersection",
884 { 89.318807, 74.810106, 87.19, 73.42, -76.4, 0.2 },
885 { 87.045667, 74.632941, 88.826472, 75.794131, -267.9, 0.2 },
886 0,
887 false },
888 { "case 4: Co-centered not intersecting",
889 { 94.665667, 73.772941, 96.446472, 74.934131, -267.9, 0.2 },
890 { 94.665667, 73.772941, 93.6551, 73.025482, -255.5, 0.2 },
891 0,
892 false },
893 { "case 5: Not intersecting, but end points very close",
894 { 72.915251, 80.493054, 73.570159, 81.257692, -260.5, 0.2 },
895 { 73.063537, 82.295989, 71.968628, 81.581351, -255.5, 0.2 },
896 0,
897 false },
898 { "case 6: Coincident centers, colliding due to arc thickness",
899 { 79.279991, 80.67988, 80.3749, 81.394518, -255.5, 0.3 },
900 { 79.279991, 80.67988, 80.3749, 81.694518, -255.5, 0.3 },
901 0,
902 true },
903 { "case 7: Single intersection",
904 { 88.495265, 81.766089, 90.090174, 82.867869, -255.5, 0.2 },
905 { 86.995265, 81.387966, 89.090174, 82.876887, -255.5, 0.2 },
906 0,
907 true },
908 { "case 8: Double intersection",
909 { 96.149734, 81.792126, 94.99, 83.37, -347.2, 0.2 },
910 { 94.857156, 81.240589, 95.91, 83.9, -288.5, 0.2 },
911 0,
912 true },
913 { "case 9: Endpoints within arc width",
914 { 72.915251, 86.493054, 73.970159, 87.257692, -260.5, 0.2 },
915 { 73.063537, 88.295989, 71.968628, 87.581351, -255.5, 0.2 },
916 0,
917 true },
918 { "case 10: Endpoints close, outside, no collision",
919 { 78.915251, 86.393054, 79.970159, 87.157692, 99.5, 0.2 },
920 { 79.063537, 88.295989, 77.968628, 87.581351, -255.5, 0.2 },
921 0,
922 false },
923 { "case 11: Endpoints close, inside, collision due to arc width",
924 { 85.915251, 86.993054, 86.970159, 87.757692, 99.5, 0.2 },
925 { 86.063537, 88.295989, 84.968628, 87.581351, -255.5, 0.2 },
926 0,
927 true },
928 { "case 12: Simulated differential pair length-tuning",
929 { 94.6551, 88.296, 95.6551, 88.296, 90.0, 0.1 },
930 { 94.6551, 88.296, 95.8551, 88.296, 90.0, 0.1 },
931 0.1,
932 false },
933 { "case 13: One arc fully enclosed in other, non-concentric",
934 { 73.77532, 93.413654, 75.70532, 93.883054, 60.0, 0.1 },
935 { 73.86532, 93.393054, 75.86532, 93.393054, 90.0, 0.3 },
936 0,
937 true },
938 { "case 14: One arc fully enclosed in other, concentric",
939 { 79.87532, 93.413654, 81.64532, 94.113054, 60.0, 0.1 },
940 { 79.87532, 93.413654, 81.86532, 93.393054, 90.0, 0.3 },
941 0,
942 true },
943 { "case 15: Arcs separated by clearance",
944 { 303.7615, 149.9252, 303.695968, 149.925237, 90.0262, 0.065 },
945 { 303.6345, 149.2637, 303.634523, 148.85619, 89.9957, 0.065 },
946 0.15,
947 false },
948};
949
950
951BOOST_DATA_TEST_CASE( CollideArc, boost::unit_test::data::make( arc_arc_collide_cases ), c )
952{
953 SHAPE_ARC arc1( c.m_arc1.GenerateArc() );
954 SHAPE_ARC arc2( c.m_arc2.GenerateArc() );
955
956
957 SHAPE_LINE_CHAIN arc1_slc( c.m_arc1.GenerateArc() );
958 arc1_slc.SetWidth( 0 );
959
960 SHAPE_LINE_CHAIN arc2_slc( c.m_arc2.GenerateArc() );
961 arc2_slc.SetWidth( 0 );
962
963 int actual = 0;
965
966 SHAPE* arc1_sh = &arc1;
970
971 bool result_arc_to_arc = arc1_sh->Collide( arc2_sh, pcbIUScale.mmToIU( c.m_clearance ),
972 &actual, &location );
973
974 // For arc to chain collisions, we need to re-calculate the clearances because the
975 // SHAPE_LINE_CHAIN is zero width
976 int clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc2.GetWidth() / 2 );
977
980
981 clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc1.GetWidth() / 2 );
984
985 clearance = ( arc1.GetWidth() / 2 ) + ( arc2.GetWidth() / 2 );
988
993}
994
995
996BOOST_AUTO_TEST_CASE( CollideArcToShapeLineChain )
997{
998 SHAPE_ARC arc( VECTOR2I( 206000000, 140110000 ), VECTOR2I( 201574617, 139229737 ),
999 VECTOR2I( 197822958, 136722959 ), 250000 );
1000
1001 SHAPE_LINE_CHAIN lc( { VECTOR2I( 159600000, 142500000 ), VECTOR2I( 159600000, 142600000 ),
1002 VECTOR2I( 166400000, 135800000 ), VECTOR2I( 166400000, 111600000 ),
1003 VECTOR2I( 190576804, 111600000 ), VECTOR2I( 192242284, 113265480 ),
1004 VECTOR2I( 192255720, 113265480 ), VECTOR2I( 203682188, 124691948 ),
1005 VECTOR2I( 203682188, 140332188 ), VECTOR2I( 206000000, 142650000 ) },
1006 false );
1007
1008
1009
1010 SHAPE* arc_sh = &arc;
1011 SHAPE* lc_sh = &lc;
1012
1013 BOOST_CHECK_EQUAL( arc_sh->Collide( &lc, 100000 ), true );
1014 BOOST_CHECK_EQUAL( lc_sh->Collide( &arc, 100000 ), true );
1015
1016 SEG seg( VECTOR2I( 203682188, 124691948 ), VECTOR2I( 203682188, 140332188 ) );
1017 BOOST_CHECK_EQUAL( arc.Collide( seg, 0 ), true );
1018}
1019
1020
1021BOOST_AUTO_TEST_CASE( CollideArcToPolygonApproximation )
1022{
1023 SHAPE_ARC arc( VECTOR2I( 73843527, 74355869 ), VECTOR2I( 71713528, 72965869 ),
1024 EDA_ANGLE( -76.36664803, DEGREES_T ), 1000000 );
1025
1026 // Create a polyset approximation from the arc - error outside (simulating the zone filler)
1027 SHAPE_POLY_SET arcBuffer;
1028 int clearance = ( arc.GetWidth() * 3 ) / 2;
1029 int polygonApproximationError = SHAPE_ARC::DefaultAccuracyForPCB();
1030
1031 TransformArcToPolygon( arcBuffer, arc.GetP0(), arc.GetArcMid(), arc.GetP1(),
1032 arc.GetWidth() + 2 * clearance,
1033 polygonApproximationError, ERROR_OUTSIDE );
1034
1035 BOOST_REQUIRE_EQUAL( arcBuffer.OutlineCount(), 1 );
1036 BOOST_CHECK_EQUAL( arcBuffer.HoleCount( 0 ), 0 );
1037
1038 // Make a reasonably large rectangular outline around the arc shape
1039 BOX2I arcbbox = arc.BBox( clearance * 4 );
1040
1041 SHAPE_LINE_CHAIN zoneOutline( { arcbbox.GetPosition(),
1042 arcbbox.GetPosition() + VECTOR2I( arcbbox.GetWidth(), 0 ),
1043 arcbbox.GetEnd(),
1044 arcbbox.GetEnd() - VECTOR2I( arcbbox.GetWidth(), 0 )
1045 },
1046 true );
1047
1048 // Create a synthetic "zone fill" polygon
1049 SHAPE_POLY_SET zoneFill;
1050 zoneFill.AddOutline( zoneOutline );
1051 zoneFill.AddHole( arcBuffer.Outline( 0 ) );
1052 zoneFill.CacheTriangulation( false );
1053
1054 int actual = 0;
1056 int epsilon = polygonApproximationError / 10;
1057
1058 BOOST_CHECK_EQUAL( zoneFill.Collide( &arc, clearance + epsilon, &actual, &location ), true );
1059
1060 BOOST_CHECK_EQUAL( zoneFill.Collide( &arc, clearance - epsilon, &actual, &location ), false );
1061}
1062
1063
1068
1069
1079bool ArePolylineEndPointsNearCircle( const SHAPE_LINE_CHAIN& aPolyline, const VECTOR2I& aCentre,
1080 int aRad, int aTolerance )
1081{
1082 std::vector<VECTOR2I> points;
1083
1084 for( int i = 0; i < aPolyline.PointCount(); ++i )
1085 {
1086 points.push_back( aPolyline.CPoint( i ) );
1087 }
1088
1089 return GEOM_TEST::ArePointsNearCircle( points, aCentre, aRad, aTolerance );
1090}
1091
1092
1102bool ArePolylineMidPointsNearCircle( const SHAPE_LINE_CHAIN& aPolyline, const VECTOR2I& aCentre,
1103 int aRad, int aTolerance )
1104{
1105 std::vector<VECTOR2I> points;
1106
1107 for( int i = 0; i < aPolyline.PointCount() - 1; ++i )
1108 {
1109 const VECTOR2I mid_pt = ( aPolyline.CPoint( i ) + aPolyline.CPoint( i + 1 ) ) / 2;
1110 points.push_back( mid_pt );
1111 }
1112
1113 return GEOM_TEST::ArePointsNearCircle( points, aCentre, aRad, aTolerance );
1114}
1115
1116
1117const std::vector<ARC_TO_POLYLINE_CASE> ArcToPolyline_cases{
1118 {
1119 "Zero rad",
1120 {
1121 { 0, 0 },
1122 { 0, 0 },
1123 180,
1124 },
1125 },
1126 {
1127 "Semicircle",
1128 {
1129 { 0, 0 },
1130 { -1000000, 0 },
1131 180,
1132 },
1133 },
1134 {
1135 // check that very small circles don't fall apart and that reverse angles
1136 // work too
1137 "Extremely small semicircle",
1138 {
1139 { 0, 0 },
1140 { -1000, 0 },
1141 -180,
1142 },
1143 },
1144 {
1145 // Make sure it doesn't only work for "easy" angles
1146 "Non-round geometry",
1147 {
1148 { 0, 0 },
1149 { 1234567, 0 },
1150 42.22,
1151 },
1152 },
1153};
1154
1155
1156BOOST_DATA_TEST_CASE( ArcToPolyline, boost::unit_test::data::make( ArcToPolyline_cases ), c )
1157{
1158 const int width = 0;
1159
1160 // Note: do not expect accuracies around 1 to work. We use integers internally so we're
1161 // liable to rounding errors. In PCBNew accuracy defaults to 5000 and we don't recommend
1162 // anything lower than 1000 (for performance reasons).
1163 const int accuracy = 100;
1164 const int epsilon = 1;
1165
1166 const SHAPE_ARC this_arc{ c.m_geom.m_center_point, c.m_geom.m_start_point,
1167 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ), width };
1168
1169 const SHAPE_LINE_CHAIN chain = this_arc.ConvertToPolyline( accuracy );
1170
1171 BOOST_TEST_MESSAGE( "Polyline has " << chain.PointCount() << " points" );
1172
1173 // Start point (exactly) where expected
1174 BOOST_CHECK_EQUAL( chain.CPoint( 0 ), c.m_geom.m_start_point );
1175
1176 // End point (exactly) where expected
1177 BOOST_CHECK_EQUAL( chain.CLastPoint(), this_arc.GetP1() );
1178
1179 int radius = ( c.m_geom.m_center_point - c.m_geom.m_start_point ).EuclideanNorm();
1180
1181 // Other points within accuracy + epsilon (for rounding) of where they should be
1183 ( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
1184
1186 ( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
1187}
1188
1189
1198BOOST_AUTO_TEST_CASE( TransformShallowArcToPolygon )
1199{
1200 SHAPE_POLY_SET buffer;
1201
1202 // Create an arc where the mid-point is only slightly off the start-end line.
1203 // This creates a very large radius arc that previously caused integer overflow.
1204 const VECTOR2I start( 0, 0 );
1205 const VECTOR2I end( 10000000, 0 ); // 10mm chord length
1206 const VECTOR2I mid( 5000000, 5 ); // Mid-point only 5nm off the line
1207
1208 const int width = 250000; // 0.25mm track width
1209 const int aError = 5000; // Default error tolerance
1210
1211 // This should not crash or produce invalid geometry
1212 TransformArcToPolygon( buffer, start, mid, end, width, aError, ERROR_INSIDE );
1213
1214 // Should produce at least one outline
1215 BOOST_CHECK( buffer.OutlineCount() >= 1 );
1216
1217 // The outline should be valid (closed, has points)
1218 if( buffer.OutlineCount() > 0 )
1219 {
1220 const SHAPE_LINE_CHAIN& outline = buffer.COutline( 0 );
1221 BOOST_CHECK( outline.IsClosed() );
1222 BOOST_CHECK( outline.PointCount() >= 3 );
1223
1224 // The bounding box should be reasonable (roughly the track width around the chord)
1225 BOX2I bbox = outline.BBox();
1226 BOOST_CHECK( bbox.GetWidth() <= end.x + width * 2 );
1227 BOOST_CHECK( bbox.GetHeight() <= width * 2 + 100 ); // Allow some tolerance
1228 }
1229}
1230
1231
1236BOOST_AUTO_TEST_CASE( TransformVeryShallowArcToPolygon )
1237{
1238 SHAPE_POLY_SET buffer;
1239
1240 // Create an arc that is effectively a straight line - mid-point essentially on the line.
1241 // This should be detected by IsEffectiveLine() and handled as a line segment.
1242 const VECTOR2I start( 0, 0 );
1243 const VECTOR2I end( 50000000, 0 ); // 50mm chord length
1244 const VECTOR2I mid( 25000000, 1 ); // Mid-point only 1nm off the line
1245
1246 const int width = 250000; // 0.25mm track width
1247 const int aError = 5000;
1248
1249 // This should not crash and should produce valid geometry
1250 TransformArcToPolygon( buffer, start, mid, end, width, aError, ERROR_INSIDE );
1251
1252 BOOST_CHECK( buffer.OutlineCount() >= 1 );
1253
1254 if( buffer.OutlineCount() > 0 )
1255 {
1256 const SHAPE_LINE_CHAIN& outline = buffer.COutline( 0 );
1257 BOOST_CHECK( outline.IsClosed() );
1258 BOOST_CHECK( outline.PointCount() >= 3 );
1259 }
1260}
1261
1262
1267BOOST_AUTO_TEST_CASE( TransformIssue22475ArcToPolygon )
1268{
1269 SHAPE_POLY_SET buffer;
1270
1271 // Values approximating one of the problematic arcs from issue #22475:
1272 // radius=24.35 mm, distToMid=12960 nm, chord=1.62 mm, angle=-3.8 deg
1273 // Compute start, mid, end points for such an arc
1274 const VECTOR2I start( 0, 0 );
1275 const VECTOR2I end( 1620000, 0 ); // 1.62mm chord length
1276 const VECTOR2I mid( 810000, 12960 ); // Mid-point 12960nm (12.96µm) off the line
1277
1278 const int width = 200000; // 0.2mm track width
1279 const int aError = 5000;
1280
1281 // Before the fix, this should create a polygon with very large extent
1282 // After the fix, it should create a reasonable oval-shaped polygon
1283 TransformArcToPolygon( buffer, start, mid, end, width, aError, ERROR_INSIDE );
1284
1285 BOOST_REQUIRE( buffer.OutlineCount() >= 1 );
1286
1287 const SHAPE_LINE_CHAIN& outline = buffer.COutline( 0 );
1288 BOOST_CHECK( outline.IsClosed() );
1289 BOOST_CHECK( outline.PointCount() >= 3 );
1290
1291 BOX2I bbox = outline.BBox();
1292
1293 // The bounding box should be reasonable - roughly chord + 2*width wide, 2*width high
1294 // With the fix (treating as oval), width should be ~1620000 + 2*200000 = 2020000
1295 // Height should be ~2*200000 = 400000
1296 // Without the fix, the height could be enormous due to the large arc radius
1297
1298 // Check that the polygon isn't ridiculously large
1299 BOOST_CHECK_MESSAGE( bbox.GetWidth() <= 3000000,
1300 wxString::Format( "Polygon width %lld is too large (expected ~2020000)", (long long)bbox.GetWidth() ) );
1301 BOOST_CHECK_MESSAGE( bbox.GetHeight() <= 1000000,
1302 wxString::Format( "Polygon height %lld is too large (expected ~400000)", (long long)bbox.GetHeight() ) );
1303}
1304
1305
1311BOOST_AUTO_TEST_CASE( CollideNearlyFlatArcDoesNotOverflow )
1312{
1313 // Values from the core dump: a nearly-flat arc with enormous radius
1314 const VECTOR2I start( 68208364, -8000 );
1315 const VECTOR2I mid( 771364, 500000 );
1316 const VECTOR2I end( 35224335, -7999 );
1317 const int width = 1270000;
1318
1319 SHAPE_ARC arc( start, mid, end, width );
1320
1321 // Radius should be near or above INT_MAX/2, triggering the segment fallback
1322 BOOST_CHECK( arc.GetRadius() >= (double) std::numeric_limits<int>::max() / 2.0 );
1323
1324 // Point near the arc endpoints. Must not crash.
1325 const VECTOR2I testPt( 35224298, -5381 );
1326 int actual = 0;
1328
1329 BOOST_CHECK_NO_THROW( arc.Collide( testPt, 635000, &actual, &location ) );
1330
1331 // Segment near the arc. Must not crash.
1332 const SEG testSeg( VECTOR2I( 35224298, -5381 ), VECTOR2I( 35696364, -32988651 ) );
1333
1334 BOOST_CHECK_NO_THROW( arc.Collide( testSeg, 635000, &actual, &location ) );
1335}
1336
1337
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr const Vec & GetPosition() const
Definition box2.h:211
constexpr const Vec GetEnd() const
Definition box2.h:212
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr size_type GetHeight() const
Definition box2.h:215
double AsDegrees() const
Definition eda_angle.h:116
Definition seg.h:42
VECTOR2I A
Definition seg.h:49
VECTOR2I B
Definition seg.h:50
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:120
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
const VECTOR2I & GetP1() const
Definition shape_arc.h:119
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:88
const VECTOR2I & GetP0() const
Definition shape_arc.h:118
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
An abstract shape on 2D plane.
Definition shape.h:126
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
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
Definition shape.h:131
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:43
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:61
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:54
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_TEST(contains==c.ExpectedContains)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
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
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694