KiCad PCB EDA Suite
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 (C) 2018 KiCad Developers, see CHANGELOG.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 
24 #include <geometry/shape_arc.h>
25 
27 
31 
32 #include "geom_test_utils.h"
33 
34 BOOST_AUTO_TEST_SUITE( ShapeArc )
35 
36 
41 {
46  double m_start_angle;
47  double m_end_angle;
48  int m_radius;
50 };
51 
55 static void CheckArcGeom( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps )
56 {
57  // Angular error - note this can get quite large for very small arcs,
58  // as the integral position rounding has a relatively greater effect
59  const double angle_tol_deg = 1.0;
60 
61  // Position error - rounding to nearest integer
62  const int pos_tol = 1;
63 
64  BOOST_CHECK_PREDICATE( KI_TEST::IsVecWithinTol<VECTOR2I>,
65  ( aProps.m_start_point )( aProps.m_start_point )( pos_tol ) );
66  BOOST_CHECK_PREDICATE(
67  KI_TEST::IsVecWithinTol<VECTOR2I>, ( aArc.GetP1() )( aProps.m_end_point )( pos_tol ) );
68  BOOST_CHECK_PREDICATE( KI_TEST::IsVecWithinTol<VECTOR2I>,
69  ( aArc.GetCenter() )( aProps.m_center_point )( pos_tol ) );
70  BOOST_CHECK_PREDICATE( KI_TEST::IsWithinWrapped<double>,
71  ( aArc.GetCentralAngle() )( aProps.m_center_angle )( 360.0 )( angle_tol_deg ) );
72  BOOST_CHECK_PREDICATE( KI_TEST::IsWithinWrapped<double>,
73  ( aArc.GetStartAngle() )( aProps.m_start_angle )( 360.0 )( angle_tol_deg ) );
74  BOOST_CHECK_PREDICATE( KI_TEST::IsWithinWrapped<double>,
75  ( aArc.GetEndAngle() )( aProps.m_end_angle )( 360.0 )( angle_tol_deg ) );
76  BOOST_CHECK_PREDICATE(
77  KI_TEST::IsWithin<double>, ( aArc.GetRadius() )( aProps.m_radius )( pos_tol ) );
78 
80  const auto chord = aArc.GetChord();
81 
82  BOOST_CHECK_PREDICATE(
83  KI_TEST::IsVecWithinTol<VECTOR2I>, ( chord.A )( aProps.m_start_point )( pos_tol ) );
84  BOOST_CHECK_PREDICATE(
85  KI_TEST::IsVecWithinTol<VECTOR2I>, ( chord.B )( aProps.m_end_point )( pos_tol ) );
86 
88  BOOST_CHECK_EQUAL( aArc.IsSolid(), true );
89 
91  #if 0 // Only for debug.
92  printf("abox %d %d %d %d prp %d %d %d %d\n",
93  aArc.BBox().GetX(), aArc.BBox().GetY(), aArc.BBox().GetSize().x, aArc.BBox().GetSize().y,
94  aProps.m_bbox.GetX(),aProps.m_bbox.GetY(),
95  aProps.m_bbox.GetSize().x, aProps.m_bbox.GetSize().y );
96  fflush(0);
97  #endif
98 
99  BOOST_CHECK_PREDICATE(
100  KI_TEST::IsBoxWithinTol<BOX2I>, ( aArc.BBox() )( aProps.m_bbox )( pos_tol ) );
101 
103 }
104 
108 static void CheckArc( const SHAPE_ARC& aArc, const ARC_PROPERTIES& aProps )
109 {
110  // Check the original arc
111  CheckArcGeom( aArc, aProps );
112 
113  // Test the Clone function (also tests copy-ctor)
114  std::unique_ptr<SHAPE> new_shape{ aArc.Clone() };
115 
116  BOOST_CHECK_EQUAL( new_shape->Type(), SH_ARC );
117 
118  SHAPE_ARC* new_arc = dynamic_cast<SHAPE_ARC*>( new_shape.get() );
119 
120  BOOST_REQUIRE( new_arc != nullptr );
121 
123  CheckArcGeom( *new_arc, aProps );
124 }
125 
130 {
131  auto arc = SHAPE_ARC();
132 
133  BOOST_CHECK_EQUAL( arc.GetWidth(), 0 );
134 
135  static ARC_PROPERTIES null_props{
136  { 0, 0 },
137  { 0, 0 },
138  { 0, 0 },
139  0,
140  0,
141  0,
142  0,
143  };
144 
145  CheckArc( arc, null_props );
146 }
147 
154 {
158 };
159 
161 {
163  std::string m_ctx_name;
164 
167 
169  int m_width;
170 
173 };
174 
175 static const std::vector<ARC_CPA_CASE> arc_cases = {
176  {
177  "C(0,0) 114 + 360 degree",
178  {
179  { 0, 0 },
180  { -306451, 687368 },
181  360,
182  },
183  0,
184  {
185  { 0, 0 },
186  { -306451, 687368 },
187  { -306451, 687368 },
188  360,
189  113.95929,
190  113.95929,
191  752587,
192  { { -752587, -752587 }, { 1505174, 1505174 } },
193  },
194  },
195  {
196  "C(0,0) 180 + 360 degree",
197  {
198  { 0, 0 },
199  { -100, 0 },
200  360,
201  },
202  0,
203  {
204  { 0, 0 },
205  { -100, 0 },
206  { -100, 0 },
207  360,
208  180,
209  180,
210  100,
211  { { -100, -100 }, { 200, 200 } },
212  },
213  },
214  {
215  "C(0,0) 180 + 90 degree",
216  {
217  { 0, 0 },
218  { -100, 0 },
219  90,
220  },
221  0,
222  {
223  { 0, 0 },
224  { -100, 0 },
225  { 0, -100 },
226  90,
227  180,
228  270,
229  100,
230  { { -100, -100 }, { 100, 100 } },
231  },
232  },
233  {
234  "C(100,200) 0 - 30 degree",
235  {
236  { 100, 200 },
237  { 300, 200 },
238  -30,
239  },
240  0,
241  {
242  { 100, 200 },
243  { 300, 200 },
244  { 273, 100 }, // 200 * sin(30) = 100, 200* cos(30) = 173
245  -30,
246  0,
247  330,
248  200,
249  { { 273, 100 }, { 27, 100 } },
250  },
251  },
252  {
253  // This is a "fan shape" which includes the top quadrant point,
254  // so it exercises the bounding box code (centre and end points
255  // do not contain the top quadrant)
256  "C(0,0) 30 + 120 degree",
257  {
258  { 0, 0 },
259  { 17320, 10000 },
260  120,
261  },
262  0,
263  {
264  { 0, 0 },
265  { 17320, 10000 },
266  { -17320, 10000 }, // 200 * sin(30) = 100, 200* cos(30) = 173
267  120,
268  30,
269  150,
270  20000,
271  // bbox defined by: centre, top quadrant point, two endpoints
272  { { -17320, 10000 }, { 17320 * 2, 10000 } },
273  },
274  },
275  {
276  // An arc that covers three quadrant points (L/R, bottom)
277  "C(0,0) 150 + 240 degree",
278  {
279  { 0, 0 },
280  { -17320, 10000 },
281  240,
282  },
283  0,
284  {
285  { 0, 0 },
286  { -17320, 10000 },
287  { 17320, 10000 },
288  240,
289  150,
290  30,
291  20000,
292  // bbox defined by: L/R quads, bottom quad and start/end
293  { { -20000, -20000 }, { 40000, 30000 } },
294  },
295  },
296  {
297  // Same as above but reverse direction
298  "C(0,0) 30 - 300 degree",
299  {
300  { 0, 0 },
301  { 17320, 10000 },
302  -240,
303  },
304  0,
305  {
306  { 0, 0 },
307  { 17320, 10000 },
308  { -17320, 10000 },
309  -240,
310  30,
311  150,
312  20000,
313  // bbox defined by: L/R quads, bottom quad and start/end
314  { { -20000, -20000 }, { 40000, 30000 } },
315  },
316  },
317 };
318 
319 BOOST_AUTO_TEST_CASE( BasicCPAGeom )
320 {
321  for( const auto& c : arc_cases )
322  {
323  BOOST_TEST_CONTEXT( c.m_ctx_name )
324  {
325 
326  const auto this_arc = SHAPE_ARC{ c.m_geom.m_center_point, c.m_geom.m_start_point,
327  c.m_geom.m_center_angle, c.m_width };
328 
329  CheckArc( this_arc, c.m_properties );
330  }
331  }
332 }
333 
334 
336 {
337  std::string m_ctx_name;
339 };
340 
341 
352  const SHAPE_LINE_CHAIN& aPolyline, const VECTOR2I& aCentre, int aRad, int aTolEnds )
353 {
354  std::vector<VECTOR2I> points;
355 
356  for( int i = 0; i < aPolyline.PointCount(); ++i )
357  {
358  points.push_back( aPolyline.CPoint( i ) );
359  }
360 
361  return GEOM_TEST::ArePointsNearCircle( points, aCentre, aRad, aTolEnds );
362 }
363 
374  const SHAPE_LINE_CHAIN& aPolyline, const VECTOR2I& aCentre, int aRad, int aTolMidPts )
375 {
376  std::vector<VECTOR2I> points;
377 
378  for( int i = 0; i < aPolyline.PointCount() - 1; ++i )
379  {
380  const VECTOR2I mid_pt = ( aPolyline.CPoint( i ) + aPolyline.CPoint( i + 1 ) ) / 2;
381  points.push_back( mid_pt );
382  }
383 
384  return GEOM_TEST::ArePointsNearCircle( points, aCentre, aRad, aTolMidPts );
385 }
386 
387 
388 BOOST_AUTO_TEST_CASE( ArcToPolyline )
389 {
390  const std::vector<ARC_TO_POLYLINE_CASE> cases = {
391  {
392  "Zero rad",
393  {
394  { 0, 0 },
395  { 0, 0 },
396  180,
397  },
398  },
399  {
400  "Semicircle",
401  {
402  { 0, 0 },
403  { -10, 0 },
404  180,
405  },
406  },
407  {
408  // check larger sizes still have required precisions
409  // and that reverse angles work too
410  "Larger semicircle",
411  {
412  { 0, 0 },
413  { -10000, 0 },
414  -180,
415  },
416  },
417  {
418  // Make sure it doesn't only work for "easy" angles
419  "Non-round geometry",
420  {
421  { 0, 0 },
422  { -1234, 0 },
423  42.22,
424  },
425  },
426  };
427 
428  const int width = 0;
429  const double accuracy = 1.0;
430 
431  for( const auto& c : cases )
432  {
433  BOOST_TEST_CONTEXT( c.m_ctx_name )
434  {
435  const SHAPE_ARC this_arc{ c.m_geom.m_center_point, c.m_geom.m_start_point,
436  c.m_geom.m_center_angle, width };
437 
438  const SHAPE_LINE_CHAIN chain = this_arc.ConvertToPolyline( accuracy );
439 
440  BOOST_TEST_MESSAGE( "Polyline has " << chain.PointCount() << " points" );
441 
442  const int pt_tol = 1;
443 
444  // Start point where expected
445  BOOST_CHECK_EQUAL( chain.CPoint( 0 ), c.m_geom.m_start_point );
446 
447  // End point where expected
448  BOOST_CHECK_PREDICATE( KI_TEST::IsVecWithinTol<VECTOR2I>,
449  ( chain.CPoint( -1 ) )( this_arc.GetP1() )( pt_tol ) );
450 
451  const int radius = ( c.m_geom.m_center_point - c.m_geom.m_start_point ).EuclideanNorm();
452 
453  const int ep_tol = 2;
454  BOOST_CHECK_PREDICATE( ArePolylineEndPointsNearCircle,
455  ( chain )( c.m_geom.m_center_point )( radius )( ep_tol ) );
456 
457  const int mp_tol = 3;
458  BOOST_CHECK_PREDICATE( ArePolylineMidPointsNearCircle,
459  ( chain )( c.m_geom.m_center_point )( radius )( mp_tol ) );
460  }
461  }
462 }
463 
464 
465 BOOST_AUTO_TEST_SUITE_END()
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:128
ARC_CENTRE_PT_ANGLE m_geom
ARC_PROPERTIES m_properties
Expected properties.
All properties of an arc (depending on how it's constructed, some of these might be the same as the c...
coord_type GetX() const
Definition: box2.h:190
VECTOR2I m_start_point
double GetRadius() const
Definition: shape_arc.cpp:225
double GetStartAngle() const
Definition: shape_arc.cpp:186
int PointCount() const
Function PointCount()
std::string m_ctx_name
The text context name.
bool ArePolylineMidPointsNearCircle(const SHAPE_LINE_CHAIN &aPolyline, const VECTOR2I &aCentre, int aRad, int aTolMidPts)
Predicate for checking a polyline has all the segment mid points on (near) a circle of given centre a...
const VECTOR2I & CPoint(int aIndex) const
Function Point()
VECTOR2I m_end_point
int m_width
Arc line width.
VECTOR2I m_center_point
SEG GetChord() const
Definition: shape_arc.h:108
compound shape, consisting of multiple simple shapes
Definition: shape.h:48
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.
#define BOOST_TEST_CONTEXT(A)
SHAPE * Clone() const override
Function Clone()
Definition: shape_arc.h:64
double GetEndAngle() const
Definition: shape_arc.cpp:196
static void CheckArcGeom(const SHAPE_ARC &aArc, const ARC_PROPERTIES &aProps)
Check a SHAPE_ARC against a given set of geometric properties.
BOOST_AUTO_TEST_CASE(NullCtor)
Check correct handling of filter strings (as used by WX)
bool ArePolylineEndPointsNearCircle(const SHAPE_LINE_CHAIN &aPolyline, const VECTOR2I &aCentre, int aRad, int aTolEnds)
Predicate for checking a polyline has all the points on (near) a circle of given centre and radius.
coord_type GetY() const
Definition: box2.h:191
bool IsSolid() const override
Definition: shape_arc.h:89
static void CheckArc(const SHAPE_ARC &aArc, const ARC_PROPERTIES &aProps)
Check an arcs geometry and other class functions.
SHAPE_LINE_CHAIN.
double GetCentralAngle() const
Definition: shape_arc.cpp:212
ARC_CENTRE_PT_ANGLE m_geom
Geom of the arc.
const BOX2I BBox(int aClearance=0) const override
Function BBox()
Definition: shape_arc.cpp:148
Numerical test predicates.
const Vec & GetSize() const
Definition: box2.h:189
Info to set up an arc by centre, start point and angle.
int m_width
Definition: shape_arc.h:144
static const std::vector< ARC_CPA_CASE > arc_cases
const VECTOR2I & GetP1() const
Definition: shape_arc.h:70
VECTOR2I GetCenter() const
Definition: shape_arc.cpp:206