KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_arc_chord_params.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
21#include <boost/test/data/test_case.hpp>
22
24#include <cmath>
25
26
27BOOST_AUTO_TEST_SUITE( ArcChordParams )
28
29
30
33BOOST_AUTO_TEST_CASE( DegenerateCases )
34{
35 ARC_CHORD_PARAMS params;
36
37 // Collinear points (mid on the line between start and end)
38 BOOST_CHECK( !params.Compute( VECTOR2I( 0, 0 ), VECTOR2I( 500000, 0 ), VECTOR2I( 1000000, 0 ) ) );
39 BOOST_CHECK( !params.IsValid() );
40
41 // Same start and end point
42 BOOST_CHECK( !params.Compute( VECTOR2I( 0, 0 ), VECTOR2I( 500000, 500000 ), VECTOR2I( 0, 0 ) ) );
43 BOOST_CHECK( !params.IsValid() );
44
45 // All three points the same
46 BOOST_CHECK( !params.Compute( VECTOR2I( 100, 100 ), VECTOR2I( 100, 100 ), VECTOR2I( 100, 100 ) ) );
47 BOOST_CHECK( !params.IsValid() );
48}
49
50
54BOOST_AUTO_TEST_CASE( QuarterCircle )
55{
56 ARC_CHORD_PARAMS params;
57
58 // Quarter circle with radius 1000000, center at origin
59 // Start at (1000000, 0), mid at approximately (707107, 707107), end at (0, 1000000)
60 int radius = 1000000;
61 int midCoord = KiROUND( radius * std::sqrt( 2.0 ) / 2.0 );
62
63 VECTOR2I start( radius, 0 );
64 VECTOR2I mid( midCoord, midCoord );
65 VECTOR2I end( 0, radius );
66
67 BOOST_REQUIRE( params.Compute( start, mid, end ) );
68 BOOST_CHECK( params.IsValid() );
69
70 // Check radius (should be close to 1000000)
71 BOOST_CHECK_CLOSE( params.GetRadius(), radius, 0.1 );
72
73 // Check arc angle (should be close to 90 degrees = PI/2 radians)
74 BOOST_CHECK_CLOSE( params.GetArcAngle(), M_PI / 2.0, 0.1 );
75
76 // Check center point (should be close to origin)
78 BOOST_CHECK_SMALL( center.x, 1.0 );
79 BOOST_CHECK_SMALL( center.y, 1.0 );
80
81 // Check chord length (should be sqrt(2) * radius)
82 BOOST_CHECK_CLOSE( params.GetChordLength(), radius * std::sqrt( 2.0 ), 0.1 );
83
84 // Check sagitta (perpendicular distance from chord midpoint to arc)
85 // For 90-degree arc: sagitta = radius * (1 - cos(45)) = radius * (1 - sqrt(2)/2)
86 double expectedSagitta = radius * ( 1.0 - std::sqrt( 2.0 ) / 2.0 );
87 BOOST_CHECK_CLOSE( params.GetSagitta(), expectedSagitta, 0.1 );
88}
89
90
95{
96 ARC_CHORD_PARAMS params;
97
98 int radius = 500000;
99 VECTOR2I start( -radius, 0 );
100 VECTOR2I mid( 0, radius );
101 VECTOR2I end( radius, 0 );
102
103 BOOST_REQUIRE( params.Compute( start, mid, end ) );
104 BOOST_CHECK( params.IsValid() );
105
106 // Check radius
107 BOOST_CHECK_CLOSE( params.GetRadius(), radius, 0.1 );
108
109 // Check arc angle (should be PI radians = 180 degrees)
110 BOOST_CHECK_CLOSE( params.GetArcAngle(), M_PI, 0.1 );
111
112 // Check center point (should be at origin)
113 VECTOR2D center = params.GetCenterPoint();
114 BOOST_CHECK_SMALL( center.x, 1.0 );
115 BOOST_CHECK_SMALL( center.y, 1.0 );
116
117 // Check chord length (should be 2 * radius = diameter)
118 BOOST_CHECK_CLOSE( params.GetChordLength(), 2.0 * radius, 0.1 );
119
120 // Check sagitta (should equal radius for semicircle)
121 BOOST_CHECK_CLOSE( params.GetSagitta(), radius, 0.1 );
122
123 // Check center offset (should be 0 for semicircle since center is on the chord)
124 BOOST_CHECK_SMALL( params.GetCenterOffset(), 1.0 );
125}
126
127
132{
133 ARC_CHORD_PARAMS params;
134
135 // Create a shallow arc: 10-degree arc with radius 10mm
136 // This tests numerical stability for cases where center is far from arc
137 int radius = 10000000; // 10mm in nm
138 double arcAngleRad = 10.0 * M_PI / 180.0; // 10 degrees
139 double halfAngle = arcAngleRad / 2.0;
140
141 // Start at angle -5 degrees, end at +5 degrees (from positive x-axis)
142 VECTOR2I start( KiROUND( radius * std::cos( -halfAngle ) ),
143 KiROUND( radius * std::sin( -halfAngle ) ) );
144 VECTOR2I mid( radius, 0 ); // Midpoint at 0 degrees
145 VECTOR2I end( KiROUND( radius * std::cos( halfAngle ) ),
146 KiROUND( radius * std::sin( halfAngle ) ) );
147
148 BOOST_REQUIRE( params.Compute( start, mid, end ) );
149 BOOST_CHECK( params.IsValid() );
150
151 // Check radius (allow 0.5% tolerance for shallow arc)
152 BOOST_CHECK_CLOSE( params.GetRadius(), radius, 0.5 );
153
154 // Check arc angle
155 BOOST_CHECK_CLOSE( params.GetArcAngle(), arcAngleRad, 1.0 );
156
157 // Center should be at origin
158 VECTOR2D center = params.GetCenterPoint();
159 BOOST_CHECK_SMALL( center.x, 100.0 ); // Allow more tolerance for shallow arc
160 BOOST_CHECK_SMALL( center.y, 100.0 );
161}
162
163
167BOOST_AUTO_TEST_CASE( StartEndAngles )
168{
169 ARC_CHORD_PARAMS params;
170
171 // Create arc from (1000, 0) through (0, 1000) to (-1000, 0)
172 // This is a semicircle with center at origin
173 int radius = 1000000;
174 VECTOR2I start( radius, 0 );
175 VECTOR2I mid( 0, radius );
176 VECTOR2I end( -radius, 0 );
177
178 BOOST_REQUIRE( params.Compute( start, mid, end ) );
179
180 // Start angle should be 0 degrees (pointing right from center)
181 EDA_ANGLE startAngle = params.GetStartAngle();
182 BOOST_CHECK_CLOSE( startAngle.AsDegrees(), 0.0, 1.0 );
183
184 // End angle should be 180 degrees (pointing left from center)
185 EDA_ANGLE endAngle = params.GetEndAngle();
186 BOOST_CHECK_CLOSE( std::abs( endAngle.AsDegrees() ), 180.0, 1.0 );
187}
188
189
193BOOST_AUTO_TEST_CASE( OffsetCenter )
194{
195 ARC_CHORD_PARAMS params;
196
197 // Create quarter circle with center at (1000000, 1000000)
198 int cx = 1000000;
199 int cy = 1000000;
200 int radius = 500000;
201
202 VECTOR2I start( cx + radius, cy );
203 int midCoord = KiROUND( radius * std::sqrt( 2.0 ) / 2.0 );
204 VECTOR2I mid( cx + midCoord, cy + midCoord );
205 VECTOR2I end( cx, cy + radius );
206
207 BOOST_REQUIRE( params.Compute( start, mid, end ) );
208 BOOST_CHECK( params.IsValid() );
209
210 // Check radius
211 BOOST_CHECK_CLOSE( params.GetRadius(), radius, 0.1 );
212
213 // Check center point
214 VECTOR2D center = params.GetCenterPoint();
215 BOOST_CHECK_CLOSE( center.x, cx, 0.1 );
216 BOOST_CHECK_CLOSE( center.y, cy, 0.1 );
217}
218
219
223BOOST_AUTO_TEST_CASE( ArcDirection )
224{
225 ARC_CHORD_PARAMS paramsCCW;
226 ARC_CHORD_PARAMS paramsCW;
227
228 int radius = 1000000;
229
230 // CCW arc: start at right, mid at top, end at left
231 VECTOR2I start( radius, 0 );
232 VECTOR2I midCCW( 0, radius );
233 VECTOR2I end( -radius, 0 );
234
235 BOOST_REQUIRE( paramsCCW.Compute( start, midCCW, end ) );
236
237 // CW arc: same start and end, but mid at bottom
238 VECTOR2I midCW( 0, -radius );
239
240 BOOST_REQUIRE( paramsCW.Compute( start, midCW, end ) );
241
242 // Both should have same radius
243 BOOST_CHECK_CLOSE( paramsCCW.GetRadius(), radius, 0.1 );
244 BOOST_CHECK_CLOSE( paramsCW.GetRadius(), radius, 0.1 );
245
246 // Both should have same arc angle magnitude (PI)
247 BOOST_CHECK_CLOSE( paramsCCW.GetArcAngle(), M_PI, 0.1 );
248 BOOST_CHECK_CLOSE( paramsCW.GetArcAngle(), M_PI, 0.1 );
249
250 // Centers should be on opposite sides of the chord
251 VECTOR2D centerCCW = paramsCCW.GetCenterPoint();
252 VECTOR2D centerCW = paramsCW.GetCenterPoint();
253
254 // CCW center should be above the chord (positive y)
255 // CW center should be below the chord (negative y)
256 // For semicircles, centers are on the chord, so we check the normal direction
257 VECTOR2D normalCCW = paramsCCW.GetNormalUnitVec();
258 VECTOR2D normalCW = paramsCW.GetNormalUnitVec();
259
260 // Normals should point in opposite directions (toward their respective centers)
261 BOOST_CHECK_CLOSE( normalCCW.y, -normalCW.y, 0.1 );
262}
263
264
268BOOST_AUTO_TEST_CASE( ChordMidpoint )
269{
270 ARC_CHORD_PARAMS params;
271
272 VECTOR2I start( 0, 0 );
273 VECTOR2I mid( 500000, 500000 );
274 VECTOR2I end( 1000000, 0 );
275
276 BOOST_REQUIRE( params.Compute( start, mid, end ) );
277
278 VECTOR2D chordMid = params.GetChordMidpoint();
279
280 // Chord midpoint should be average of start and end
281 BOOST_CHECK_CLOSE( chordMid.x, 500000.0, 0.001 );
282 BOOST_CHECK_CLOSE( chordMid.y, 0.0, 0.001 );
283}
284
285
289BOOST_AUTO_TEST_CASE( UnitVectorsNormalized )
290{
291 ARC_CHORD_PARAMS params;
292
293 VECTOR2I start( 123456, 789012 );
294 VECTOR2I mid( 500000, 900000 );
295 VECTOR2I end( 876543, 210987 );
296
297 BOOST_REQUIRE( params.Compute( start, mid, end ) );
298
299 // Check chord unit vector is normalized
300 VECTOR2D u = params.GetChordUnitVec();
301 double uLen = std::sqrt( u.x * u.x + u.y * u.y );
302 BOOST_CHECK_CLOSE( uLen, 1.0, 0.0001 );
303
304 // Check normal unit vector is normalized
305 VECTOR2D n = params.GetNormalUnitVec();
306 double nLen = std::sqrt( n.x * n.x + n.y * n.y );
307 BOOST_CHECK_CLOSE( nLen, 1.0, 0.0001 );
308
309 // Check u and n are perpendicular (dot product = 0)
310 double dot = u.x * n.x + u.y * n.y;
311 BOOST_CHECK_SMALL( dot, 0.0001 );
312}
313
314
319{
320 ARC_CHORD_PARAMS params;
321
322 // Create a 270-degree arc (major arc)
323 // Start at (1000, 0), go CCW through (0, -1000) to (-1000, 0)
324 // The "mid" point is at 135 degrees from start, which is at (-707, -707)
325 int radius = 1000000;
326 int midCoord = KiROUND( radius * std::sqrt( 2.0 ) / 2.0 );
327
328 VECTOR2I start( radius, 0 );
329 VECTOR2I mid( -midCoord, -midCoord ); // At 225 degrees (halfway through 270-degree arc)
330 VECTOR2I end( 0, radius );
331
332 BOOST_REQUIRE( params.Compute( start, mid, end ) );
333 BOOST_CHECK( params.IsValid() );
334
335 // Check radius
336 BOOST_CHECK_CLOSE( params.GetRadius(), radius, 0.5 );
337
338 // Check arc angle (should be 270 degrees = 3*PI/2 radians)
339 BOOST_CHECK_CLOSE( params.GetArcAngle(), 3.0 * M_PI / 2.0, 1.0 );
340}
341
342
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
Arc geometry computed from a chord-based coordinate system.
double GetRadius() const
Get the arc radius.
EDA_ANGLE GetEndAngle() const
Get the angle from arc center to the end point.
double GetChordLength() const
Get the chord length (distance from start to end).
double GetSagitta() const
Get the sagitta (perpendicular distance from chord midpoint to arc).
VECTOR2D GetNormalUnitVec() const
Get the unit vector perpendicular to the chord, pointing toward the arc center.
VECTOR2D GetChordMidpoint() const
Get the chord midpoint coordinates.
bool Compute(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Compute arc geometry from three points defining the arc.
bool IsValid() const
Check if the parameters represent a valid arc.
double GetCenterOffset() const
Get the distance from chord midpoint to arc center along the normal.
VECTOR2D GetCenterPoint() const
Get the arc center point.
double GetArcAngle() const
Get the arc angle (total angle swept by the arc) in radians.
VECTOR2D GetChordUnitVec() const
Get the unit vector along the chord (from start to end).
EDA_ANGLE GetStartAngle() const
Get the angle from arc center to the start point.
double AsDegrees() const
Definition eda_angle.h:116
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_CASE(DegenerateCases)
Test that degenerate cases (collinear points) return invalid.
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
VECTOR2I center
int radius
VECTOR2I end
#define M_PI
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695
VECTOR2< double > VECTOR2D
Definition vector2d.h:694