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
28#include <geometry/shape_arc.h>
29
31
33#include <qa_utils/numeric.h>
34
35#include "geom_test_utils.h"
36
37BOOST_AUTO_TEST_SUITE( ShapeArc )
38
39
44{
53};
54
61static void CheckArcGeom( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps, const int aSynErrIU = 1 )
62{
63 // Angular error - note this can get quite large for very small arcs,
64 // as the integral position rounding has a relatively greater effect
65 const double angle_tol_deg = 1.0;
66
67 // Position error - rounding to nearest integer
68 const int pos_tol = 1;
69
70 BOOST_CHECK_PREDICATE( KI_TEST::IsVecWithinTol<VECTOR2I>,
71 ( aProps.m_start_point )( aProps.m_start_point )( pos_tol ) );
72
73 BOOST_CHECK_PREDICATE( KI_TEST::IsVecWithinTol<VECTOR2I>,
74 ( aArc.GetP1() )( aProps.m_end_point )( pos_tol ) );
75
76 BOOST_CHECK_PREDICATE( KI_TEST::IsVecWithinTol<VECTOR2I>,
77 ( aArc.GetCenter() )( aProps.m_center_point )( aSynErrIU ) );
78
79 BOOST_CHECK_PREDICATE( KI_TEST::IsWithinWrapped<double>,
80 ( aArc.GetCentralAngle().AsDegrees() )( aProps.m_center_angle )( 360.0 )( angle_tol_deg ) );
81
82 BOOST_CHECK_PREDICATE( KI_TEST::IsWithinWrapped<double>,
83 ( aArc.GetStartAngle().AsDegrees() )( aProps.m_start_angle )( 360.0 )( angle_tol_deg ) );
84
85 BOOST_CHECK_PREDICATE( KI_TEST::IsWithinWrapped<double>,
86 ( aArc.GetEndAngle().AsDegrees() )( aProps.m_end_angle )( 360.0 )( angle_tol_deg ) );
87
88 BOOST_CHECK_PREDICATE( KI_TEST::IsWithin<double>,
89 ( aArc.GetRadius() )( aProps.m_radius )( aSynErrIU ) );
90
92 const auto chord = aArc.GetChord();
93
94 BOOST_CHECK_PREDICATE( KI_TEST::IsVecWithinTol<VECTOR2I>,
95 ( chord.A )( aProps.m_start_point )( pos_tol ) );
96
97 BOOST_CHECK_PREDICATE( KI_TEST::IsVecWithinTol<VECTOR2I>,
98 ( chord.B )( aProps.m_end_point )( pos_tol ) );
99
101 BOOST_CHECK_EQUAL( aArc.IsSolid(), true );
102
103 BOOST_CHECK_PREDICATE( KI_TEST::IsBoxWithinTol<BOX2I>,
104 ( aArc.BBox() )( aProps.m_bbox )( pos_tol ) );
105
107}
108
109
116static void CheckArc( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps, const int aSynErrIU = 1 )
117{
118 // Check the original arc
119 CheckArcGeom( aArc, aProps, aSynErrIU );
120
121 // Test the Clone function (also tests copy-ctor)
122 std::unique_ptr<SHAPE> new_shape{ aArc.Clone() };
123
124 BOOST_CHECK_EQUAL( new_shape->Type(), SH_ARC );
125
126 SHAPE_ARC* new_arc = dynamic_cast<SHAPE_ARC*>( new_shape.get() );
127
128 BOOST_REQUIRE( new_arc != nullptr );
129
131 CheckArcGeom( *new_arc, aProps, aSynErrIU );
132}
133
138{
139 auto arc = SHAPE_ARC();
140
141 BOOST_CHECK_EQUAL( arc.GetWidth(), 0 );
142
143 static ARC_PROPERTIES null_props{
144 { 0, 0 },
145 { 0, 0 },
146 { 0, 0 },
147 0,
148 0,
149 0,
150 0,
151 };
152
153 CheckArc( arc, null_props );
154}
155
156
163{
167};
168
169
171{
174
177
180};
181
182
183static const std::vector<ARC_CPA_CASE> arc_cases = {
184 {
185 "C(0,0) 114 + 360 degree",
186 {
187 { 0, 0 },
188 { -306451, 687368 },
189 360,
190 },
191 0,
192 {
193 { 0, 0 },
194 { -306451, 687368 },
195 { -306451, 687368 },
196 360,
197 113.95929,
198 113.95929,
199 752587,
200 { { -752587, -752587 }, { 1505174, 1505174 } },
201 },
202 },
203 {
204 "C(0,0) 180 + 360 degree",
205 {
206 { 0, 0 },
207 { -100, 0 },
208 360,
209 },
210 0,
211 {
212 { 0, 0 },
213 { -100, 0 },
214 { -100, 0 },
215 360,
216 180,
217 180,
218 100,
219 { { -100, -100 }, { 200, 200 } },
220 },
221 },
222 {
223 "C(0,0) 180 + 90 degree",
224 {
225 { 0, 0 },
226 { -100, 0 },
227 90,
228 },
229 0,
230 {
231 { 0, 0 },
232 { -100, 0 },
233 { 0, -100 },
234 90,
235 180,
236 270,
237 100,
238 { { -100, -100 }, { 100, 100 } },
239 },
240 },
241 {
242 "C(100,200) 0 - 30 degree",
243 {
244 { 100, 200 },
245 { 300, 200 },
246 -30,
247 },
248 0,
249 {
250 { 100, 200 },
251 { 300, 200 },
252 { 273, 100 }, // 200 * sin(30) = 100, 200* cos(30) = 173
253 -30,
254 0,
255 330,
256 200,
257 { { 273, 100 }, { 27, 100 } },
258 },
259 },
260 {
261 // This is a "fan shape" which includes the top quadrant point,
262 // so it exercises the bounding box code (centre and end points
263 // do not contain the top quadrant)
264 "C(0,0) 30 + 120 degree",
265 {
266 { 0, 0 },
267 { 17320, 10000 },
268 120,
269 },
270 0,
271 {
272 { 0, 0 },
273 { 17320, 10000 },
274 { -17320, 10000 }, // 200 * sin(30) = 100, 200* cos(30) = 173
275 120,
276 30,
277 150,
278 20000,
279 // bbox defined by: centre, top quadrant point, two endpoints
280 { { -17320, 10000 }, { 17320 * 2, 10000 } },
281 },
282 },
283 {
284 // An arc that covers three quadrant points (L/R, bottom)
285 "C(0,0) 150 + 240 degree",
286 {
287 { 0, 0 },
288 { -17320, 10000 },
289 240,
290 },
291 0,
292 {
293 { 0, 0 },
294 { -17320, 10000 },
295 { 17320, 10000 },
296 240,
297 150,
298 30,
299 20000,
300 // bbox defined by: L/R quads, bottom quad and start/end
301 { { -20000, -20000 }, { 40000, 30000 } },
302 },
303 },
304 {
305 // Same as above but reverse direction
306 "C(0,0) 30 - 300 degree",
307 {
308 { 0, 0 },
309 { 17320, 10000 },
310 -240,
311 },
312 0,
313 {
314 { 0, 0 },
315 { 17320, 10000 },
316 { -17320, 10000 },
317 -240,
318 30,
319 150,
320 20000,
321 // bbox defined by: L/R quads, bottom quad and start/end
322 { { -20000, -20000 }, { 40000, 30000 } },
323 },
324 },
325};
326
327
328BOOST_DATA_TEST_CASE( BasicCPAGeom, boost::unit_test::data::make( arc_cases ), c )
329{
330
331 const auto this_arc = SHAPE_ARC{ c.m_geom.m_center_point, c.m_geom.m_start_point,
332 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ),
333 c.m_width };
334
335 CheckArc( this_arc, c.m_properties );
336}
337
338
339
344{
348};
349
350
352{
355
358
361};
362
363
364static const std::vector<ARC_TTR_CASE> arc_ttr_cases = {
365 {
366 "90 degree segments intersecting",
367 {
368 { 0, 0, 0, 1000 },
369 { 0, 0, 1000, 0 },
370 1000,
371 },
372 0,
373 {
374 { 1000, 1000 },
375 { 0, 1000 }, //start on first segment
376 { 1000, 0 }, //end on second segment
377 90, //positive angle due to start/end
378 180,
379 270,
380 1000,
381 { { 0, 0 }, { 1000, 1000 } },
382 }
383 },
384 {
385 "45 degree segments intersecting",
386 {
387 { 0, 0, 0, 1000 },
388 { 0, 0, 1000, 1000 },
389 1000,
390 },
391 0,
392 {
393 { 1000, 2414 },
394 { 0, 2414 }, //start on first segment
395 { 1707, 1707 }, //end on second segment
396 135, //positive angle due to start/end
397 180,
398 225,
399 1000,
400 { { 0, 1414 }, { 1707, 1000 } },
401 }
402 },
403 {
404 "135 degree segments intersecting",
405 {
406 { 0, 0, 0, 1000 },
407 { 0, 0, 1000, -1000 },
408 1000,
409 },
410 0,
411 {
412 { 1000, 414 },
413 { 0, 414 }, //start on first segment ( radius * tan(45 /2) )
414 { 293, -293 }, //end on second segment (radius * 1-cos(45)) )
415 45, //positive angle due to start/end
416 180,
417 225,
418 1000,
419 { { 0, -293 }, { 293, 707 } },
420 }
421 }
422
423
424};
425
426
427BOOST_DATA_TEST_CASE( BasicTTRGeom, boost::unit_test::data::make( arc_ttr_cases ), c )
428{
429 for( int testCase = 0; testCase < 8; ++testCase )
430 {
431 SEG seg1 = c.m_geom.m_segment_1;
432 SEG seg2 = c.m_geom.m_segment_2;
433 ARC_PROPERTIES props = c.m_properties;
434
435 if( testCase > 3 )
436 {
437 //Swap input segments.
438 seg1 = c.m_geom.m_segment_2;
439 seg2 = c.m_geom.m_segment_1;
440
441 //The result should swap start and end points and invert the angles:
442 props.m_end_point = c.m_properties.m_start_point;
443 props.m_start_point = c.m_properties.m_end_point;
444 props.m_start_angle = c.m_properties.m_end_angle;
445 props.m_end_angle = c.m_properties.m_start_angle;
446 props.m_center_angle = -c.m_properties.m_center_angle;
447 }
448
449 //Test all combinations of start and end points for the segments
450 if( ( testCase % 4 ) == 1 || ( testCase % 4 ) == 3 )
451 {
452 //Swap start and end points for seg1
453 VECTOR2I temp = seg1.A;
454 seg1.A = seg1.B;
455 seg1.B = temp;
456 }
457
458 if( ( testCase % 4 ) == 2 || ( testCase % 4 ) == 3 )
459 {
460 //Swap start and end points for seg2
461 VECTOR2I temp = seg2.A;
462 seg2.A = seg2.B;
463 seg2.B = temp;
464 }
465
466 const auto this_arc = SHAPE_ARC{ seg1, seg2,
467 c.m_geom.m_radius, c.m_width };
468
469 // Error of 4 IU permitted for the center and radius calculation
471 }
472}
473
474
478struct ARC_START_END_CENTER
479{
480 VECTOR2I m_start;
481 VECTOR2I m_end;
482 VECTOR2I m_center;
483};
484
485
487{
489 ARC_START_END_CENTER m_geom;
490
493
496};
497
498
499
500static const std::vector<ARC_SEC_CASE> arc_sec_cases = {
501 { "180 deg, clockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } }, true, { 50, -50 } },
502 { "180 deg, anticlockwise", { { 100, 0 }, { 0, 0 }, { 50, 0 } }, false, { 50, 50 } },
503 { "180 deg flipped, clockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } }, true, { 50, 50 } },
504 { "180 deg flipped, anticlockwise", { { 0, 0 }, { 100, 0 }, { 50, 0 } }, false, { 50, -50 } },
505 { "90 deg, clockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } }, true, { -71, 71 } },
506 { "90 deg, anticlockwise", { { -100, 0 }, { 0, 100 }, { 0, 0 } }, false, { 71, -71 } },
507};
508
509
510BOOST_DATA_TEST_CASE( BasicSECGeom, boost::unit_test::data::make( arc_sec_cases ), c )
511{
512 VECTOR2I start = c.m_geom.m_start;
513 VECTOR2I end = c.m_geom.m_end;
514 VECTOR2I center = c.m_geom.m_center;
515 bool cw = c.m_clockwise;
516
519
520 BOOST_CHECK_EQUAL( this_arc.GetArcMid(), c.m_expected_mid );
521}
522
523
525{
531};
532
533
534static const std::vector<ARC_PT_COLLIDE_CASE> arc_pt_collide_cases = {
535 { " 270deg, 0 cl, 0 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 100, 0 }, true, 0 },
536 { " 270deg, 0 cl, 90 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 0, 100 }, true, 0 },
537 { " 270deg, 0 cl, 180 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { -100, 0 }, true, 0 },
538 { " 270deg, 0 cl, 270 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 0, -100 }, true, 0 },
539 { " 270deg, 0 cl, 45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 71, 71 }, true, 0 },
540 { " 270deg, 0 cl, -45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { 71, -71 }, false, -1 },
541 { "-270deg, 0 cl, 0 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 100, 0 }, true, 0 },
542 { "-270deg, 0 cl, 90 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 0, 100 }, true, 0 },
543 { "-270deg, 0 cl, 180 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { -100, 0 }, true, 0 },
544 { "-270deg, 0 cl, 270 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 0, -100 }, true, 0 },
545 { "-270deg, 0 cl, 45 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 71, 71 }, false, -1 },
546 { "-270deg, 0 cl, -45 deg ", { { 0, 0 }, { 100, 0 }, -270.0 }, 0, { 71, -71 }, true, 0 },
547 { " 270deg, 5 cl, 0 deg, 5 pos X", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 105, 0 }, true, 5 },
548 { " 270deg, 5 cl, 0 deg, 5 pos Y", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 100, -5 }, true, 5 },
549 { " 270deg, 5 cl, 90 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, 105 }, true, 5 },
550 { " 270deg, 5 cl, 180 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { -105, 0 }, true, 5 },
551 { " 270deg, 5 cl, 270 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, -105 }, true, 5 },
552 { " 270deg, 5 cl, 0 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 105, 0 }, true, 5 },
553 { " 270deg, 5 cl, 90 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, 105 }, true, 5 },
554 { " 270deg, 5 cl, 180 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { -105, 0 }, true, 5 },
555 { " 270deg, 5 cl, 270 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 0, -105 }, true, 5 },
556 { " 270deg, 5 cl, 45 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 74, 75 }, true, 5 }, // 74.246, -74.246
557 { " 270deg, 5 cl, -45 deg, 5 pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 74, -75 }, false, -1 }, //74.246, -74.246
558 { " 270deg, 5 cl, 45 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 67, 67 }, true, 5 }, // 67.17, 67.17
559 { " 270deg, 5 cl, -45 deg, 5 neg", { { 0, 0 }, { 100, 0 }, 270.0 }, 5, { 67, -67 }, false, -1 }, // 67.17, -67.17
560 { " 270deg, 4 cl, 0 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 105, 0 }, false, -1 },
561 { " 270deg, 4 cl, 90 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 0, 105 }, false, -1 },
562 { " 270deg, 4 cl, 180 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { -105, 0 }, false, -1 },
563 { " 270deg, 4 cl, 270 deg pos", { { 0, 0 }, { 100, 0 }, 270.0 }, 4, { 0, -105 }, false, -1 },
564 { " 90deg, 0 cl, 0 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 71, -71 }, true, 0 },
565 { " 90deg, 0 cl, 45 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 100, 0 }, true, 0 },
566 { " 90deg, 0 cl, 90 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 71, 71 }, true, 0 },
567 { " 90deg, 0 cl, 135 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 0, -100 }, false, -1 },
568 { " 90deg, 0 cl, -45 deg ", { { 0, 0 }, { 71, -71 }, 90.0 }, 0, { 0, 100 }, false, -1 },
569 { " -90deg, 0 cl, 0 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 71, -71 }, true, 0 },
570 { " -90deg, 0 cl, 45 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 100, 0 }, true, 0 },
571 { " -90deg, 0 cl, 90 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 71, 71 }, true, 0 },
572 { " -90deg, 0 cl, 135 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 0, -100 }, false, -1 },
573 { " -90deg, 0 cl, -45 deg ", { { 0, 0 }, { 71, 71 }, -90.0 }, 0, { 0, 100 }, false, -1 },
574 { "issue 11358 collide",
575 { { 119888000, 60452000 }, { 120904000, 60452000 }, 360.0 },
576 0,
577 { 120395500, 59571830 },
578 true,
579 0 },
580 { "issue 11358 dist",
581 { { 119888000, 60452000 }, { 120904000, 60452000 }, 360.0 },
582 100,
583 { 118872050, 60452000 },
584 true,
585 50 },
586};
587
588
589BOOST_DATA_TEST_CASE( CollidePt, boost::unit_test::data::make( arc_pt_collide_cases ), c )
590{
591 SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
592 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
593
594 // Test a zero width arc (distance should equal the clearance)
595 BOOST_TEST_CONTEXT( "Test Clearance" )
596 {
597 int dist = -1;
598 BOOST_CHECK_EQUAL( arc.Collide( c.m_point, c.m_arc_clearance, &dist ),
599 c.m_exp_result );
600 BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
601 }
602
603 // Test by changing the width of the arc (distance should equal zero)
604 BOOST_TEST_CONTEXT( "Test Width" )
605 {
606 int dist = -1;
607 arc.SetWidth( c.m_arc_clearance * 2 );
608 BOOST_CHECK_EQUAL( arc.Collide( c.m_point, 0, &dist ), c.m_exp_result );
609
610 if( c.m_exp_result )
611 BOOST_CHECK_EQUAL( dist, 0 );
612 else
613 BOOST_CHECK_EQUAL( dist, -1 );
614 }
615}
616
618{
624};
625
626
627static const std::vector<ARC_SEG_COLLIDE_CASE> arc_seg_collide_cases = {
628 { "0 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 100, 0 }, { 50, 0 } }, true, 0 },
629 { "90 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 0, 100 }, { 0, 50 } }, true, 0 },
630 { "180 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { -100, 0 }, { -50, 0 } }, true, 0 },
631 { "270 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 0, -100 }, { 0, -50 } }, true, 0 },
632 { "45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 71, 71 }, { 35, 35 } }, true, 0 },
633 { "-45 deg ", { { 0, 0 }, { 100, 0 }, 270.0 }, 0, { { 71, -71 }, { 35, -35 } }, false, -1 },
634 { "seg inside arc start", { { 0, 0 }, { 71, -71 }, 90.0 },
635 10, { { 90, 0 }, { -35, 0 } }, true, 10 },
636 { "seg inside arc end", { { 0, 0 }, { 71, -71 }, 90.0 },
637 10, { { -35, 0 }, { 90, 0 } }, true, 10 },
638 { "large diameter arc", { { 172367922, 82282076 }, { 162530000, 92120000 }, -45.0 },
639 433300, { { 162096732, 92331236 }, { 162096732, 78253268 } }, true, 433268 },
640};
641
642
643BOOST_DATA_TEST_CASE( CollideSeg, boost::unit_test::data::make( arc_seg_collide_cases ), c )
644{
645 SHAPE_ARC arc( c.m_geom.m_center_point, c.m_geom.m_start_point,
646 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ) );
647
648 // Test a zero width arc (distance should equal the clearance)
649 BOOST_TEST_CONTEXT( "Test Clearance" )
650 {
651 int dist = -1;
652 BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, c.m_arc_clearance, &dist ),
653 c.m_exp_result );
654 BOOST_CHECK_EQUAL( dist, c.m_exp_distance );
655 }
656
657 // Test by changing the width of the arc (distance should equal zero)
658 BOOST_TEST_CONTEXT( "Test Width" )
659 {
660 int dist = -1;
661 arc.SetWidth( c.m_arc_clearance * 2 );
662 BOOST_CHECK_EQUAL( arc.Collide( c.m_seg, 0, &dist ), c.m_exp_result );
663
664 if( c.m_exp_result )
665 BOOST_CHECK_EQUAL( dist, 0 );
666 else
667 BOOST_CHECK_EQUAL( dist, -1 );
668 }
669}
670
672{
673 // Coordinates and dimensions in millimeters
676 double m_start_x;
677 double m_start_y;
679 double m_width;
680
682 {
686
687 return arc;
688 }
689};
690
691
693{
698};
699
700
701static const std::vector<ARC_ARC_COLLIDE_CASE> arc_arc_collide_cases = {
702 { "case 1: No intersection",
703 { 73.843527, 74.355869, 71.713528, 72.965869, -76.36664803, 0.2 },
704 { 71.236473, 74.704131, 73.366472, 76.094131, -76.36664803, 0.2 },
705 0,
706 false },
707 { "case 2: No intersection",
708 { 82.542335, 74.825975, 80.413528, 73.435869, -76.4, 0.2 },
709 { 76.491192, 73.839894, 78.619999, 75.23, -76.4, 0.2 },
710 0,
711 false },
712 { "case 3: No intersection",
713 { 89.318807, 74.810106, 87.19, 73.42, -76.4, 0.2 },
714 { 87.045667, 74.632941, 88.826472, 75.794131, -267.9, 0.2 },
715 0,
716 false },
717 { "case 4: Co-centered not intersecting",
718 { 94.665667, 73.772941, 96.446472, 74.934131, -267.9, 0.2 },
719 { 94.665667, 73.772941, 93.6551, 73.025482, -255.5, 0.2 },
720 0,
721 false },
722 { "case 5: Not intersecting, but end points very close",
723 { 72.915251, 80.493054, 73.570159, 81.257692, -260.5, 0.2 },
724 { 73.063537, 82.295989, 71.968628, 81.581351, -255.5, 0.2 },
725 0,
726 false },
727 { "case 6: Coincident centers, colliding due to arc thickness",
728 { 79.279991, 80.67988, 80.3749, 81.394518, -255.5, 0.3 },
729 { 79.279991, 80.67988, 80.3749, 81.694518, -255.5, 0.3 },
730 0,
731 true },
732 { "case 7: Single intersection",
733 { 88.495265, 81.766089, 90.090174, 82.867869, -255.5, 0.2 },
734 { 86.995265, 81.387966, 89.090174, 82.876887, -255.5, 0.2 },
735 0,
736 true },
737 { "case 8: Double intersection",
738 { 96.149734, 81.792126, 94.99, 83.37, -347.2, 0.2 },
739 { 94.857156, 81.240589, 95.91, 83.9, -288.5, 0.2 },
740 0,
741 true },
742 { "case 9: Endpoints within arc width",
743 { 72.915251, 86.493054, 73.970159, 87.257692, -260.5, 0.2 },
744 { 73.063537, 88.295989, 71.968628, 87.581351, -255.5, 0.2 },
745 0,
746 true },
747 { "case 10: Endpoints close, outside, no collision",
748 { 78.915251, 86.393054, 79.970159, 87.157692, 99.5, 0.2 },
749 { 79.063537, 88.295989, 77.968628, 87.581351, -255.5, 0.2 },
750 0,
751 false },
752 { "case 11: Endpoints close, inside, collision due to arc width",
753 { 85.915251, 86.993054, 86.970159, 87.757692, 99.5, 0.2 },
754 { 86.063537, 88.295989, 84.968628, 87.581351, -255.5, 0.2 },
755 0,
756 true },
757 { "case 12: Simulated differential pair length-tuning",
758 { 94.6551, 88.296, 95.6551, 88.296, 90.0, 0.1 },
759 { 94.6551, 88.296, 95.8551, 88.296, 90.0, 0.1 },
760 0.1,
761 false },
762 { "case 13: One arc fully enclosed in other, non-concentric",
763 { 73.77532, 93.413654, 75.70532, 93.883054, 60.0, 0.1 },
764 { 73.86532, 93.393054, 75.86532, 93.393054, 90.0, 0.3 },
765 0,
766 true },
767 { "case 14: One arc fully enclosed in other, concentric",
768 { 79.87532, 93.413654, 81.64532, 94.113054, 60.0, 0.1 },
769 { 79.87532, 93.413654, 81.86532, 93.393054, 90.0, 0.3 },
770 0,
771 true },
772 { "case 15: Arcs separated by clearance",
773 { 303.7615, 149.9252, 303.695968, 149.925237, 90.0262, 0.065 },
774 { 303.6345, 149.2637, 303.634523, 148.85619, 89.9957, 0.065 },
775 0.15,
776 false },
777};
778
779
780BOOST_DATA_TEST_CASE( CollideArc, boost::unit_test::data::make( arc_arc_collide_cases ), c )
781{
782 SHAPE_ARC arc1( c.m_arc1.GenerateArc() );
783 SHAPE_ARC arc2( c.m_arc2.GenerateArc() );
784
785
786 SHAPE_LINE_CHAIN arc1_slc( c.m_arc1.GenerateArc() );
788
789 SHAPE_LINE_CHAIN arc2_slc( c.m_arc2.GenerateArc() );
790 arc2_slc.SetWidth( 0 );
791
792 int actual = 0;
794
795 SHAPE* arc1_sh = &arc1;
799
801 &actual, &location );
802
803 // For arc to chain collisions, we need to re-calculate the clearances because the
804 // SHAPE_LINE_CHAIN is zero width
805 int clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc2.GetWidth() / 2 );
806
809
810 clearance = pcbIUScale.mmToIU( c.m_clearance ) + ( arc1.GetWidth() / 2 );
813
814 clearance = ( arc1.GetWidth() / 2 ) + ( arc2.GetWidth() / 2 );
817
822}
823
824
825BOOST_AUTO_TEST_CASE( CollideArcToShapeLineChain )
826{
827 SHAPE_ARC arc( VECTOR2I( 206000000, 140110000 ), VECTOR2I( 201574617, 139229737 ),
828 VECTOR2I( 197822958, 136722959 ), 250000 );
829
830 SHAPE_LINE_CHAIN lc( { VECTOR2I( 159600000, 142500000 ), VECTOR2I( 159600000, 142600000 ),
831 VECTOR2I( 166400000, 135800000 ), VECTOR2I( 166400000, 111600000 ),
832 VECTOR2I( 190576804, 111600000 ), VECTOR2I( 192242284, 113265480 ),
833 VECTOR2I( 192255720, 113265480 ), VECTOR2I( 203682188, 124691948 ),
834 VECTOR2I( 203682188, 140332188 ), VECTOR2I( 206000000, 142650000 ) },
835 false );
836
837
838
839 SHAPE* arc_sh = &arc;
840 SHAPE* lc_sh = &lc;
841
842 BOOST_CHECK_EQUAL( arc_sh->Collide( &lc, 100000 ), true );
843 BOOST_CHECK_EQUAL( lc_sh->Collide( &arc, 100000 ), true );
844
845 SEG seg( VECTOR2I( 203682188, 124691948 ), VECTOR2I( 203682188, 140332188 ) );
846 BOOST_CHECK_EQUAL( arc.Collide( seg, 0 ), true );
847}
848
849
850BOOST_AUTO_TEST_CASE( CollideArcToPolygonApproximation )
851{
852 SHAPE_ARC arc( VECTOR2I( 73843527, 74355869 ), VECTOR2I( 71713528, 72965869 ),
853 EDA_ANGLE( -76.36664803, DEGREES_T ), 1000000 );
854
855 // Create a polyset approximation from the arc - error outside (simulating the zone filler)
856 SHAPE_POLY_SET arcBuffer;
857 int clearance = ( arc.GetWidth() * 3 ) / 2;
858 int polygonApproximationError = SHAPE_ARC::DefaultAccuracyForPCB();
859
860 TransformArcToPolygon( arcBuffer, arc.GetP0(), arc.GetArcMid(), arc.GetP1(),
861 arc.GetWidth() + 2 * clearance,
862 polygonApproximationError, ERROR_OUTSIDE );
863
864 BOOST_REQUIRE_EQUAL( arcBuffer.OutlineCount(), 1 );
865 BOOST_CHECK_EQUAL( arcBuffer.HoleCount( 0 ), 0 );
866
867 // Make a reasonably large rectangular outline around the arc shape
868 BOX2I arcbbox = arc.BBox( clearance * 4 );
869
870 SHAPE_LINE_CHAIN zoneOutline( { arcbbox.GetPosition(),
871 arcbbox.GetPosition() + VECTOR2I( arcbbox.GetWidth(), 0 ),
872 arcbbox.GetEnd(),
873 arcbbox.GetEnd() - VECTOR2I( arcbbox.GetWidth(), 0 )
874 },
875 true );
876
877 // Create a synthetic "zone fill" polygon
878 SHAPE_POLY_SET zoneFill;
879 zoneFill.AddOutline( zoneOutline );
880 zoneFill.AddHole( arcBuffer.Outline( 0 ) );
881 zoneFill.CacheTriangulation( false );
882
883 int actual = 0;
885 int epsilon = polygonApproximationError / 10;
886
887 BOOST_CHECK_EQUAL( zoneFill.Collide( &arc, clearance + epsilon, &actual, &location ), true );
888
889 BOOST_CHECK_EQUAL( zoneFill.Collide( &arc, clearance - epsilon, &actual, &location ), false );
890}
891
892
894{
896};
897
898
908bool ArePolylineEndPointsNearCircle( const SHAPE_LINE_CHAIN& aPolyline, const VECTOR2I& aCentre,
909 int aRad, int aTolerance )
910{
911 std::vector<VECTOR2I> points;
912
913 for( int i = 0; i < aPolyline.PointCount(); ++i )
914 {
915 points.push_back( aPolyline.CPoint( i ) );
916 }
917
918 return GEOM_TEST::ArePointsNearCircle( points, aCentre, aRad, aTolerance );
919}
920
921
931bool ArePolylineMidPointsNearCircle( const SHAPE_LINE_CHAIN& aPolyline, const VECTOR2I& aCentre,
932 int aRad, int aTolerance )
933{
934 std::vector<VECTOR2I> points;
935
936 for( int i = 0; i < aPolyline.PointCount() - 1; ++i )
937 {
938 const VECTOR2I mid_pt = ( aPolyline.CPoint( i ) + aPolyline.CPoint( i + 1 ) ) / 2;
939 points.push_back( mid_pt );
940 }
941
942 return GEOM_TEST::ArePointsNearCircle( points, aCentre, aRad, aTolerance );
943}
944
945
946const std::vector<ARC_TO_POLYLINE_CASE> ArcToPolyline_cases{
947 {
948 "Zero rad",
949 {
950 { 0, 0 },
951 { 0, 0 },
952 180,
953 },
954 },
955 {
956 "Semicircle",
957 {
958 { 0, 0 },
959 { -1000000, 0 },
960 180,
961 },
962 },
963 {
964 // check that very small circles don't fall apart and that reverse angles
965 // work too
966 "Extremely small semicircle",
967 {
968 { 0, 0 },
969 { -1000, 0 },
970 -180,
971 },
972 },
973 {
974 // Make sure it doesn't only work for "easy" angles
975 "Non-round geometry",
976 {
977 { 0, 0 },
978 { 1234567, 0 },
979 42.22,
980 },
981 },
982};
983
984
985BOOST_DATA_TEST_CASE( ArcToPolyline, boost::unit_test::data::make( ArcToPolyline_cases ), c )
986{
987 const int width = 0;
988
989 // Note: do not expect accuracies around 1 to work. We use integers internally so we're
990 // liable to rounding errors. In PCBNew accuracy defaults to 5000 and we don't recommend
991 // anything lower than 1000 (for performance reasons).
992 const int accuracy = 100;
993 const int epsilon = 1;
994
995 const SHAPE_ARC this_arc{ c.m_geom.m_center_point, c.m_geom.m_start_point,
996 EDA_ANGLE( c.m_geom.m_center_angle, DEGREES_T ), width };
997
999
1000 BOOST_TEST_MESSAGE( "Polyline has " << chain.PointCount() << " points" );
1001
1002 // Start point (exactly) where expected
1003 BOOST_CHECK_EQUAL( chain.CPoint( 0 ), c.m_geom.m_start_point );
1004
1005 // End point (exactly) where expected
1007
1008 int radius = ( c.m_geom.m_center_point - c.m_geom.m_start_point ).EuclideanNorm();
1009
1010 // Other points within accuracy + epsilon (for rounding) of where they should be
1012 ( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
1013
1015 ( chain )( c.m_geom.m_center_point )( radius )( accuracy + epsilon ) );
1016}
1017
1018
@ ERROR_OUTSIDE
Definition: approximation.h:33
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
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
double AsDegrees() const
Definition: eda_angle.h:113
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
EDA_ANGLE GetCentralAngle() const
Definition: shape_arc.cpp:864
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:118
SEG GetChord() const
Definition: shape_arc.h:242
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
Definition: shape_arc.cpp:413
EDA_ANGLE GetEndAngle() const
Definition: shape_arc.cpp:841
int GetWidth() const
Definition: shape_arc.h:210
SHAPE_ARC & ConstructFromStartEndCenter(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aClockwise=false, double aWidth=0)
Constructs this arc from the given start, end and center.
Definition: shape_arc.cpp:213
const VECTOR2I & GetP1() const
Definition: shape_arc.h:117
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,...
Definition: shape_arc.cpp:254
const SHAPE_LINE_CHAIN ConvertToPolyline(double aAccuracy=DefaultAccuracyForPCB(), double *aEffectiveAccuracy=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
Definition: shape_arc.cpp:886
double GetRadius() const
Definition: shape_arc.cpp:880
EDA_ANGLE GetStartAngle() const
Definition: shape_arc.cpp:833
static double DefaultAccuracyForPCB()
Definition: shape_arc.h:276
SHAPE * Clone() const override
Return a dynamically allocated copy of the shape.
Definition: shape_arc.h:86
const VECTOR2I & GetP0() const
Definition: shape_arc.h:116
bool IsSolid() const override
Definition: shape_arc.h:215
const VECTOR2I & GetCenter() const
Definition: shape_arc.cpp:849
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
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.
void SetWidth(int aWidth)
Set the width of all segments in the chain.
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.
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.
Numerical test predicates.
@ SH_ARC
circular arc
Definition: shape.h:54
Info to set up an arc by centre, start point and angle.
ARC_CENTRE_PT_ANGLE m_geom
Geom of the arc.
ARC_PROPERTIES m_properties
Expected properties.
int m_width
Arc line width.
double m_center_angle
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_start_point
VECTOR2I m_end_point
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
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.
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
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()
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)
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.
const int epsilon
BOOST_AUTO_TEST_CASE(NullCtor)
Check correct handling of filter strings (as used by WX)
BOOST_TEST_CONTEXT("Test Clearance")
int clearance
VECTOR2I location
SHAPE_ARC this_arc
BOOST_TEST_MESSAGE("Polyline has "<< chain.PointCount()<< " points")
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