KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_eda_shape.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 3
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 */
17
18
20
21#include <cmath>
22
23#include <base_units.h>
24#include <eda_shape.h>
25#include <math/util.h>
27#include <qa_utils/geometry/geometry.h> // For KI_TEST::IsVecWithinTol
28#include <geometry/shape_arc.h> // For SHAPE_ARC::DefaultAccuracyForPCB()
29
30
31BOOST_AUTO_TEST_SUITE( EdaShape )
32
34{
35public:
36 EDA_SHAPE_MOCK( SHAPE_T aShapeType ) : EDA_SHAPE( aShapeType, 0, FILL_T::NO_FILL ){};
37};
38
39
49
50
51static const std::vector<SET_ANGLE_END_CASE> set_angle_end_cases =
52{
53 {
54 "Issue 13626: clockwise semicircle",
55 {-428880000, 117229160 },
56 {-430060565, 113472820 },
57 180.0,
58 {-431241130, 109716480 },
59 false
60 },
61 {
62 "Issue 13626: anticlockwise arc",
63 { -431241130, 109716480 },
64 { -434923630, 112954230 },
65 -138.46654568595355,
66 { -439827050, 112936200 },
67 true
68 }
69};
70
71
72BOOST_AUTO_TEST_CASE( SetAngleAndEnd )
73{
74 for( const auto& c : set_angle_end_cases )
75 {
76 BOOST_TEST_INFO_SCOPE( c.m_CaseName );
77
79 shape.SetStart( c.m_Start );
80 shape.SetCenter( c.m_Center );
81
82 shape.SetArcAngleAndEnd( EDA_ANGLE( c.m_Angle, DEGREES_T ), true );
83
84 BOOST_CHECK_EQUAL( shape.EndsSwapped(), c.m_ExpectedStartEndSwapped );
85
86 const VECTOR2I newEnd = shape.EndsSwapped() ? shape.GetStart() : shape.GetEnd();
87
90 (newEnd) ( c.m_ExpectedEndBeforeSwap ) ( SHAPE_ARC::DefaultAccuracyForPCB() ) );
91 }
92}
93
94
107
108static const std::vector<SET_ARC_GEOMETRY_CASE> set_arc_geometry_cases = {
109 {
110 // Test that when setting an arc by start/mid/end, the winding
111 // direction is correctly determined (in 15694, this was in FP_SHAPE,
112 // but the logic has since been merged with EDA_SHAPE).
113 "Issue 15694: clockwise arc",
114 { 10000000, 0 },
115 { 0, 10000000 },
116 { -10000000, 0 },
117 { 0, 0 },
118 10000000,
119 false,
120 { -10000000, 0 }, // unchanged
121 180.0,
122 },
123 {
124 "Issue 15694: anticlockwise arc",
125 { -10000000, 0 },
126 { 0, 10000000 },
127 { 10000000, 0 },
128 { 0, 0 },
129 10000000,
130 true,
131 { 10000000, 0 }, // the start is the end after swapping
132 180.0, // angle is positive after swapping
133 },
134};
135
136BOOST_AUTO_TEST_CASE( SetArcGeometry )
137{
138 const double angle_tol = 0.1;
139
140 for( const auto& c : set_arc_geometry_cases )
141 {
142 BOOST_TEST_INFO_SCOPE( c.m_CaseName );
143
145
146 shape.SetArcGeometry( c.m_Start, c.m_Mid, c.m_End );
147
148 const VECTOR2I center = shape.getCenter();
149
152 (center) ( c.m_ExpectedCenter ) ( SHAPE_ARC::DefaultAccuracyForPCB() ) );
153
154 const int radius = shape.GetRadius();
155
158 (radius) ( c.m_ExpectedRadius ) ( SHAPE_ARC::DefaultAccuracyForPCB() ) );
159
160 BOOST_CHECK_EQUAL( shape.EndsSwapped(), c.m_ExpectedStartEndSwapped );
161
162 const VECTOR2I newEnd = shape.EndsSwapped() ? shape.GetStart() : shape.GetEnd();
163
166 (newEnd) ( c.m_ExpectedEndAfterSwap ) ( SHAPE_ARC::DefaultAccuracyForPCB() ) );
167
168 const EDA_ANGLE angle = shape.GetArcAngle();
169
172 ( angle.AsDegrees() )( c.m_ExpectedAngleAfterSwapDeg )( 360.0 )( angle_tol ) );
173
174 // Check that the centre is still correct
175 }
176}
177
184BOOST_AUTO_TEST_CASE( ArcEditKeepsSmallSchematicRadius )
185{
186 // 50 mil radius arc in schematic IUs, well under the buggy 100 mil floor.
187 const int radius = schIUScale.MilsToIU( 50 );
188 const VECTOR2I center( 0, 0 );
189 const VECTOR2I start( radius, 0 );
190 const VECTOR2I end( 0, radius );
191 const VECTOR2I mid( KiROUND( radius / std::sqrt( 2.0 ) ),
192 KiROUND( radius / std::sqrt( 2.0 ) ) );
193
195 arc.SetArcGeometry( start, mid, end );
196
197 BOOST_REQUIRE_LT( arc.GetRadius(), schIUScale.MilsToIU( 100 ) );
198
199 // Drag the endpoint a few IU; with the bug the radius snaps up to 100 mil.
200 const VECTOR2I newEnd( 5, radius );
201
202 KI_ARC_EDIT::EditArcEndpointKeepCenter( arc, center, start, mid, newEnd, newEnd, schIUScale );
203 BOOST_CHECK_LT( arc.GetRadius(), schIUScale.MilsToIU( 100 ) );
204
205 // Same for the mid-point helper, which has its own minimum-radius clamp.
206 const VECTOR2I smallerMid( KiROUND( ( radius - 100 ) / std::sqrt( 2.0 ) ),
207 KiROUND( ( radius - 100 ) / std::sqrt( 2.0 ) ) );
208
210 arc2.SetArcGeometry( start, mid, end );
211
212 KI_ARC_EDIT::EditArcMidKeepCenter( arc2, center, start, mid, end, smallerMid, schIUScale );
213 BOOST_CHECK_LT( arc2.GetRadius(), schIUScale.MilsToIU( 100 ) );
214}
215
216
224BOOST_AUTO_TEST_CASE( PolygonBehaviorSurvivesAssignment )
225{
227
228 SHAPE_POLY_SET& poly = shape.GetPolyShape();
229 poly.NewOutline();
230 poly.Append( { 0, 0 } );
231 poly.Append( { 1000000, 0 } );
232 poly.Append( { 1000000, 1000000 } );
233
234 EDA_POLYGON_POINT_EDIT_BEHAVIOR behavior( shape );
235
236 EDIT_POINTS points( nullptr );
237 behavior.MakePoints( points );
238 BOOST_CHECK_EQUAL( points.PointsSize(), 3u );
239
240 EDA_SHAPE_MOCK copy( shape );
241 shape = copy;
242
243 // After assignment, shape.m_poly is a fresh allocation.
244 // The behavior must still work (not use-after-free).
245 EDIT_POINTS points2( nullptr );
246 behavior.MakePoints( points2 );
247 BOOST_CHECK_EQUAL( points2.PointsSize(), 3u );
248
249 BOOST_CHECK( behavior.UpdatePoints( points ) );
250}
251
252
253BOOST_AUTO_TEST_CASE( EllipseBasicAccessors )
254{
255 // Construct a closed ellipse EDA_SHAPE and round-trip every accessor.
257 e.SetEllipseCenter( VECTOR2I( 100, 200 ) );
258 e.SetEllipseMajorRadius( 500 );
259 e.SetEllipseMinorRadius( 300 );
261
262 BOOST_CHECK( e.GetShape() == SHAPE_T::ELLIPSE );
267 BOOST_CHECK_CLOSE( e.GetEllipseRotation().AsDegrees(), 30.0, 1e-6 );
268
269 // Closed ellipse reports itself as a closed shape.
270 BOOST_CHECK( e.IsClosed() );
271}
272
273
274BOOST_AUTO_TEST_CASE( EllipseArcIsOpenCurve )
275{
276 // Elliptical arcs are open
277 // IsClosed() must return false.
279 arc.SetEllipseCenter( VECTOR2I( 0, 0 ) );
280 arc.SetEllipseMajorRadius( 500 );
281 arc.SetEllipseMinorRadius( 300 );
284 arc.SetEllipseEndAngle( EDA_ANGLE( 180.0, DEGREES_T ) );
285
286 BOOST_CHECK( arc.GetShape() == SHAPE_T::ELLIPSE_ARC );
287 BOOST_CHECK( !arc.IsClosed() );
288
289 // Start/end angles round trip through the accessors.
290 BOOST_CHECK_CLOSE( arc.GetEllipseStartAngle().AsDegrees(), 0.0, 1e-6 );
291 BOOST_CHECK_CLOSE( arc.GetEllipseEndAngle().AsDegrees(), 180.0, 1e-6 );
292}
293
294
295BOOST_AUTO_TEST_CASE( EllipsePerimeterForCircleCase )
296{
297 // An ellipse with MajorRadius == MinorRadius is a circle.
298 // Ramanujan's approximation returns 2πr for this case.
300 e.SetEllipseCenter( VECTOR2I( 0, 0 ) );
301 e.SetEllipseMajorRadius( 1000 );
302 e.SetEllipseMinorRadius( 1000 );
304
305 const double expected = 2.0 * M_PI * 1000.0;
306 BOOST_CHECK_CLOSE( e.GetLength(), expected, 1e-6 );
307}
308
309
310BOOST_AUTO_TEST_CASE( EllipseMakeEffectiveShapesNonEmpty )
311{
312 // MakeEffectiveShapes converts the ellipse into primitive shapes that DRC
313 // the router, and exporters consume. Verify it returns at least one shape
314
316 e.SetEllipseCenter( VECTOR2I( 0, 0 ) );
317 e.SetEllipseMajorRadius( 500 );
318 e.SetEllipseMinorRadius( 300 );
320
321 std::vector<SHAPE*> shapes = e.MakeEffectiveShapes();
322 BOOST_CHECK( !shapes.empty() );
323
324 for( SHAPE* s : shapes )
325 delete s;
326}
327
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:123
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
double AsDegrees() const
Definition eda_angle.h:116
"Standard" polygon editing behavior for EDA_SHAPE polygons.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
EDA_SHAPE_MOCK(SHAPE_T aShapeType)
EDA_ANGLE GetArcAngle() const
int GetEllipseMinorRadius() const
Definition eda_shape.h:310
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:292
void SetCenter(const VECTOR2I &aCenter)
VECTOR2I getCenter() const
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:338
int GetEllipseMajorRadius() const
Definition eda_shape.h:301
virtual std::vector< SHAPE * > MakeEffectiveShapes(bool aEdgeOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition eda_shape.h:462
SHAPE_POLY_SET & GetPolyShape()
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:319
virtual void SetEllipseEndAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:331
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:185
virtual void SetEllipseRotation(const EDA_ANGLE &aA)
Definition eda_shape.h:312
EDA_SHAPE(SHAPE_T aType, int aLineWidth, FILL_T aFill)
Definition eda_shape.cpp:53
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:240
bool IsClosed() const
virtual void SetEllipseCenter(const VECTOR2I &aPt)
Definition eda_shape.h:285
virtual void SetEllipseMinorRadius(int aR)
Definition eda_shape.h:303
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
virtual void SetEllipseMajorRadius(int aR)
Definition eda_shape.h:294
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:329
bool EndsSwapped() const
Have the start and end points been swapped since they were set?
Definition eda_shape.h:365
virtual void SetEllipseStartAngle(const EDA_ANGLE &aA)
Definition eda_shape.h:322
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
double GetLength() const
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
virtual void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:194
EDIT_POINTS is a VIEW_ITEM that manages EDIT_POINTs and EDIT_LINEs and draws them.
unsigned int PointsSize() const
Return number of stored EDIT_POINTs.
static int DefaultAccuracyForPCB()
Definition shape_arc.h:279
Represent a set of closed polygons.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
An abstract shape on 2D plane.
Definition shape.h:124
@ DEGREES_T
Definition eda_angle.h:31
SHAPE_T
Definition eda_shape.h:44
@ ELLIPSE
Definition eda_shape.h:52
@ ELLIPSE_ARC
Definition eda_shape.h:53
FILL_T
Definition eda_shape.h:59
@ NO_FILL
Definition eda_shape.h:60
void EditArcEndpointKeepCenter(EDA_SHAPE &aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCursor, const EDA_IU_SCALE &aIuScale)
Move an arc endpoint around the existing center, pulling the opposite endpoint along to keep the radi...
void EditArcMidKeepCenter(EDA_SHAPE &aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCursor, const EDA_IU_SCALE &aIuScale)
Move the mid point of an arc while keeping the center, rotating the endpoints onto the new radius.
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 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
VECTOR2I m_ExpectedEndBeforeSwap
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
static const std::vector< SET_ARC_GEOMETRY_CASE > set_arc_geometry_cases
BOOST_AUTO_TEST_CASE(SetAngleAndEnd)
static const std::vector< SET_ANGLE_END_CASE > set_angle_end_cases
BOOST_AUTO_TEST_SUITE_END()
VECTOR3I expected(15, 30, 45)
SHAPE_ARC arc2(c.m_arc2.GenerateArc())
VECTOR2I center
int radius
BOOST_CHECK_PREDICATE(ArePolylineEndPointsNearCircle,(chain)(c.m_geom.m_center_point)(radius)(accuracy+epsilon))
VECTOR2I end
BOOST_CHECK_EQUAL(result, "25.4")
#define M_PI
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683