KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_shape_nearest_points.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 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <geometry/shape.h>
23#include <geometry/shape_arc.h>
26#include <geometry/shape_rect.h>
30
31#include "fixtures_geometry.h"
32
33
34BOOST_AUTO_TEST_SUITE( SHAPE_NEAREST_POINTS_TEST )
35
36// Circle to Circle tests
37BOOST_AUTO_TEST_CASE( NearestPoints_CircleToCircle_Separate )
38{
39 SHAPE_CIRCLE circleA( VECTOR2I( 0, 0 ), 5 );
40 SHAPE_CIRCLE circleB( VECTOR2I( 20, 0 ), 5 );
41
42 VECTOR2I ptA, ptB;
43 bool result = circleA.NearestPoints( &circleB, ptA, ptB );
44
45 BOOST_CHECK( result );
46 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 5, 0 ), "Expected: " << VECTOR2I( 5, 0 ) << " Actual: " << ptA );
47 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 15, 0 ), "Expected: " << VECTOR2I( 15, 0 ) << " Actual: " << ptB );
48}
49
50BOOST_AUTO_TEST_CASE( NearestPoints_CircleToCircle_Concentric )
51{
52 SHAPE_CIRCLE circleA( VECTOR2I( 0, 0 ), 5 );
53 SHAPE_CIRCLE circleB( VECTOR2I( 0, 0 ), 10 );
54
55 VECTOR2I ptA, ptB;
56 bool result = circleA.NearestPoints( &circleB, ptA, ptB );
57
58 BOOST_CHECK( result );
59 // For concentric circles, points should be on arbitrary direction (positive X by convention)
60 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 5, 0 ), "Expected: " << VECTOR2I( 5, 0 ) << " Actual: " << ptA );
61 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 10, 0 ), "Expected: " << VECTOR2I( 10, 0 ) << " Actual: " << ptB );
62}
63
64BOOST_AUTO_TEST_CASE( NearestPoints_CircleToCircle_Overlapping )
65{
66 SHAPE_CIRCLE circleA( VECTOR2I( 0, 0 ), 5 );
67 SHAPE_CIRCLE circleB( VECTOR2I( 6, 0 ), 5 );
68
69 VECTOR2I ptA, ptB;
70 bool result = circleA.NearestPoints( &circleB, ptA, ptB );
71
72 BOOST_CHECK( result );
73 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 5, 0 ), "Expected: " << VECTOR2I( 5, 0 ) << " Actual: " << ptA );
74 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 1, 0 ), "Expected: " << VECTOR2I( 1, 0 ) << " Actual: " << ptB );
75}
76
77// Circle to Rectangle tests
78BOOST_AUTO_TEST_CASE( NearestPoints_CircleToRect_Outside )
79{
80 SHAPE_CIRCLE circle( VECTOR2I( -10, 5 ), 3 );
81 SHAPE_RECT rect( VECTOR2I( 0, 0 ), VECTOR2I( 10, 10 ) );
82
83 VECTOR2I ptA, ptB;
84 bool result = circle.NearestPoints( &rect, ptA, ptB );
85
86 BOOST_CHECK( result );
87 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( -7, 5 ), "Expected: " << VECTOR2I( -7, 5 ) << " Actual: " << ptA );
88 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 0, 5 ), "Expected: " << VECTOR2I( 0, 5 ) << " Actual: " << ptB );
89}
90
91BOOST_AUTO_TEST_CASE( NearestPoints_CircleToRect_Inside )
92{
93 SHAPE_CIRCLE circle( VECTOR2I( 5, 5 ), 2 );
94 SHAPE_RECT rect( VECTOR2I( 0, 0 ), VECTOR2I( 10, 10 ) );
95
96 VECTOR2I ptA, ptB;
97 bool result = circle.NearestPoints( &rect, ptA, ptB );
98
99 BOOST_CHECK( result );
100 // Circle center is inside rectangle, should find nearest edge
101 // Center at (5,5) is equidistant from left/right edges (5 units) and top/bottom edges (5 units)
102 // Implementation should choose one consistently - likely left edge based on min comparison
103 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 3, 5 ), "Expected: " << VECTOR2I( 3, 5 ) << " Actual: " << ptA );
104 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 0, 5 ), "Expected: " << VECTOR2I( 0, 5 ) << " Actual: " << ptB );
105}
106
107// Circle to Segment tests
108BOOST_AUTO_TEST_CASE( NearestPoints_CircleToSegment )
109{
110 SHAPE_CIRCLE circle( VECTOR2I( 0, 5 ), 2 );
111 SHAPE_SEGMENT segment( VECTOR2I( -5, 0 ), VECTOR2I( 5, 0 ) );
112
113 VECTOR2I ptA, ptB;
114 bool result = circle.NearestPoints( &segment, ptA, ptB );
115
116 BOOST_CHECK( result );
117 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 0, 3 ), "Expected: " << VECTOR2I( 0, 3 ) << " Actual: " << ptA );
118 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 0, 0 ), "Expected: " << VECTOR2I( 0, 0 ) << " Actual: " << ptB );
119}
120
121BOOST_AUTO_TEST_CASE( NearestPoints_CircleToSegment_CenterOnSegment )
122{
123 SHAPE_CIRCLE circle( VECTOR2I( 0, 0 ), 3 );
124 SHAPE_SEGMENT segment( VECTOR2I( -5, 0 ), VECTOR2I( 5, 0 ) );
125
126 VECTOR2I ptA, ptB;
127 bool result = circle.NearestPoints( &segment, ptA, ptB );
128
129 BOOST_CHECK( result );
130 // When center is on segment, should pick perpendicular direction
131 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 0, 3 ), "Expected: " << VECTOR2I( 0, 3 ) << " Actual: " << ptA );
132 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 0, 0 ), "Expected: " << VECTOR2I( 0, 0 ) << " Actual: " << ptB );
133}
134
135// Rectangle to Rectangle tests
136BOOST_AUTO_TEST_CASE( NearestPoints_RectToRect_Separate )
137{
138 SHAPE_RECT rectA( VECTOR2I( 0, 0 ), VECTOR2I( 5, 5 ) );
139 SHAPE_RECT rectB( VECTOR2I( 10, 0 ), VECTOR2I( 25, 25 ) );
140
141 VECTOR2I ptA, ptB;
142 bool result = rectA.NearestPoints( &rectB, ptA, ptB );
143
144 BOOST_CHECK( result );
145 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 5, 0 ), "Expected: " << VECTOR2I( 5, 0 ) << " Actual: " << ptA );
146 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 10, 0 ), "Expected: " << VECTOR2I( 10, 0 ) << " Actual: " << ptB );
147}
148
149BOOST_AUTO_TEST_CASE( NearestPoints_RectToRect_Corner )
150{
151 SHAPE_RECT rectA( VECTOR2I( 0, 0 ), VECTOR2I( 5, 5 ) );
152 SHAPE_RECT rectB( VECTOR2I( 7, 7 ), VECTOR2I( 25, 25 ) );
153
154 VECTOR2I ptA, ptB;
155 bool result = rectA.NearestPoints( &rectB, ptA, ptB );
156
157 BOOST_CHECK( result );
158 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 5, 5 ), "Expected: " << VECTOR2I( 5, 5 ) << " Actual: " << ptA );
159 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 7, 7 ), "Expected: " << VECTOR2I( 7, 7 ) << " Actual: " << ptB );
160}
161
162// Line Chain tests
163BOOST_AUTO_TEST_CASE( NearestPoints_LineChainToLineChain )
164{
165 SHAPE_LINE_CHAIN chainA;
166 chainA.Append( VECTOR2I( 0, 0 ) );
167 chainA.Append( VECTOR2I( 10, 0 ) );
168 chainA.Append( VECTOR2I( 10, 10 ) );
169
170 SHAPE_LINE_CHAIN chainB;
171 chainB.Append( VECTOR2I( 5, 5 ) );
172 chainB.Append( VECTOR2I( 15, 5 ) );
173
174 VECTOR2I ptA, ptB;
175 bool result = chainA.NearestPoints( &chainB, ptA, ptB );
176
177 BOOST_CHECK( result );
178 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 10, 5 ), "Expected: " << VECTOR2I( 10, 5 ) << " Actual: " << ptA );
179 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 10, 5 ), "Expected: " << VECTOR2I( 10, 5 ) << " Actual: " << ptB );
180}
181
182BOOST_AUTO_TEST_CASE( NearestPoints_LineChainWithArc )
183{
184 SHAPE_LINE_CHAIN chainA;
185 chainA.Append( VECTOR2I( 0, 0 ) );
186 chainA.Append( VECTOR2I( 10, 0 ) );
187
188 SHAPE_LINE_CHAIN chainB;
189 chainB.Append( SHAPE_ARC( VECTOR2I( 5, 10 ), VECTOR2I( 10, 5 ), VECTOR2I( 15, 10 ), 0 ) );
190
191 VECTOR2I ptA, ptB;
192 bool result = chainA.NearestPoints( &chainB, ptA, ptB );
193
194 BOOST_CHECK( result );
195 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 10, 0 ), "Expected: " << VECTOR2I( 10, 0 ) << " Actual: " << ptA );
196 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 10, 10 ), "Expected: " << VECTOR2I( 10, 10 ) << " Actual: " << ptB );
197}
198
199// Arc tests
200BOOST_AUTO_TEST_CASE( NearestPoints_ArcToArc )
201{
202 SHAPE_ARC arcA( VECTOR2I( 0, 0 ), VECTOR2I( 5, 5 ), VECTOR2I( 10, 0 ), 0 );
203 SHAPE_ARC arcB( VECTOR2I( 15, 0 ), VECTOR2I( 20, 5 ), VECTOR2I( 25, 0 ), 0 );
204
205 VECTOR2I ptA, ptB;
206 int64_t distSq;
207 bool result = arcA.NearestPoints( arcB, ptA, ptB, distSq );
208
209 BOOST_CHECK( result );
210 // Points should be on the arcs closest to each other
211 BOOST_CHECK( ptA.x <= 10 && ptA.x >= 0 );
212 BOOST_CHECK( ptB.x >= 15 && ptB.x <= 25 );
213}
214
215BOOST_AUTO_TEST_CASE( NearestPoints_ArcToCircle )
216{
217 SHAPE_ARC arc( VECTOR2I( 0, 0 ), VECTOR2I( 5, 5 ), VECTOR2I( 10, 0 ), 0 );
218 SHAPE_CIRCLE circle( VECTOR2I( 5, 10 ), 3 );
219
220 VECTOR2I ptA, ptB;
221 int64_t distSq;
222 bool result = arc.NearestPoints( circle, ptA, ptB, distSq );
223
224 BOOST_CHECK( result );
225 // Arc point should be at or near the top of the arc
226 BOOST_CHECK( ptA.y >= 0 );
227 // Circle point should be at bottom of circle
228 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 5, 7 ), "Expected: " << VECTOR2I( 5, 7 ) << " Actual: " << ptB );
229}
230
231// Segment tests
232BOOST_AUTO_TEST_CASE( NearestPoints_SegmentToSegment )
233{
234 SHAPE_SEGMENT segA( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 10, 0 ) ), 2 );
235 SHAPE_SEGMENT segB( SEG( VECTOR2I( 5, 5 ), VECTOR2I( 5, 15 ) ), 2 );
236
237 VECTOR2I ptA, ptB;
238 bool result = segA.NearestPoints( &segB, ptA, ptB );
239
240 BOOST_CHECK( result );
241 // Points should account for segment width
242 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 5, 1 ), "Expected: " << VECTOR2I( 5, 1 ) << " Actual: " << ptA );
243 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 5, 4 ), "Expected: " << VECTOR2I( 5, 4 ) << " Actual: " << ptB );
244}
245
246BOOST_AUTO_TEST_CASE( NearestPoints_SegmentToCircle )
247{
248 SHAPE_SEGMENT segment( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 10, 0 ) ), 4 );
249 SHAPE_CIRCLE circle( VECTOR2I( 5, 10 ), 3 );
250
251 VECTOR2I ptA, ptB;
252 bool result = segment.NearestPoints( &circle, ptA, ptB );
253
254 BOOST_CHECK( result );
255 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 5, 2 ), "Expected: " << VECTOR2I( 5, 2 ) << " Actual: " << ptA );
256 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 5, 7 ), "Expected: " << VECTOR2I( 5, 7 ) << " Actual: " << ptB );
257}
258
259// Compound Shape tests
260BOOST_AUTO_TEST_CASE( NearestPoints_CompoundShapes )
261{
262 SHAPE_COMPOUND compoundA;
263 compoundA.AddShape( new SHAPE_CIRCLE( VECTOR2I( 0, 0 ), 5 ) );
264 compoundA.AddShape( new SHAPE_RECT( VECTOR2I( 10, 0 ), VECTOR2I( 5, 5 ) ) );
265
266 SHAPE_COMPOUND compoundB;
267 compoundB.AddShape( new SHAPE_CIRCLE( VECTOR2I( 20, 0 ), 3 ) );
268
269 VECTOR2I ptA, ptB;
270 bool result = compoundA.NearestPoints( &compoundB, ptA, ptB );
271
272 BOOST_CHECK( result );
273 // Should find nearest points between rectangle in A and circle in B
274 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 10, 0 ), "Expected: " << VECTOR2I( 10, 0 ) << " Actual: " << ptA );
275 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 17, 0 ), "Expected: " << VECTOR2I( 17, 0 ) << " Actual: " << ptB );
276}
277
278// Polygon Set tests
279BOOST_AUTO_TEST_CASE( NearestPoints_PolySetToCircle )
280{
281 SHAPE_POLY_SET polySet;
282 polySet.NewOutline();
283 polySet.Append( VECTOR2I( 0, 0 ) );
284 polySet.Append( VECTOR2I( 10, 0 ) );
285 polySet.Append( VECTOR2I( 10, 10 ) );
286 polySet.Append( VECTOR2I( 0, 10 ) );
287
288 SHAPE_CIRCLE circle( VECTOR2I( 15, 5 ), 3 );
289
290 VECTOR2I ptA, ptB;
291 bool result = polySet.NearestPoints( &circle, ptA, ptB );
292
293 BOOST_CHECK( result );
294 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 12, 5 ), "Expected: " << VECTOR2I( 12, 5 ) << " Actual: " << ptB );
295}
296
297// Missing Basic Shape Combinations
298BOOST_AUTO_TEST_CASE( NearestPoints_RectToSegment )
299{
300 SHAPE_RECT rect( VECTOR2I( 0, 0 ), VECTOR2I( 10, 10 ) );
301 SHAPE_SEGMENT segment( VECTOR2I( 15, 5 ), VECTOR2I( 25, 5 ) );
302
303 VECTOR2I ptA, ptB;
304 bool result = rect.NearestPoints( &segment, ptA, ptB );
305
306 BOOST_CHECK( result );
307 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 10, 5 ), "Expected: " << VECTOR2I( 10, 5 ) << " Actual: " << ptA );
308 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 15, 5 ), "Expected: " << VECTOR2I( 15, 5 ) << " Actual: " << ptB );
309}
310
311BOOST_AUTO_TEST_CASE( NearestPoints_RectToLineChain )
312{
313 SHAPE_RECT rect( VECTOR2I( 0, 0 ), VECTOR2I( 5, 5 ) );
314
316 chain.Append( VECTOR2I( 10, 0 ) );
317 chain.Append( VECTOR2I( 15, 5 ) );
318 chain.Append( VECTOR2I( 10, 10 ) );
319
320 VECTOR2I ptA, ptB;
321 bool result = rect.NearestPoints( &chain, ptA, ptB );
322
323 BOOST_CHECK( result );
324 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 5, 0 ), "Expected: " << VECTOR2I( 5, 0 ) << " Actual: " << ptA );
325 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 10, 0 ), "Expected: " << VECTOR2I( 10, 0 ) << " Actual: " << ptB );
326}
327
328BOOST_AUTO_TEST_CASE( NearestPoints_SegmentToLineChain )
329{
330 SHAPE_SEGMENT segment( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 100, 0 ) ), 20 );
331
333 chain.Append( VECTOR2I( 50, 100 ) );
334 chain.Append( VECTOR2I( 150, 100 ) );
335 chain.Append( VECTOR2I( 150, 50 ) );
336
337 VECTOR2I ptA, ptB;
338 bool result = segment.NearestPoints( &chain, ptA, ptB );
339
340 BOOST_CHECK( result );
341 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 107, 7 ), "Expected: " << VECTOR2I( 107, 7 ) << " Actual: " << ptA );
342 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 150, 50 ), "Expected: " << VECTOR2I( 150, 50 ) << " Actual: " << ptB );
343}
344
345BOOST_AUTO_TEST_CASE( NearestPoints_CircleToLineChain )
346{
347 SHAPE_CIRCLE circle( VECTOR2I( 0, 0 ), 3 );
348
350 chain.Append( VECTOR2I( 10, -5 ) );
351 chain.Append( VECTOR2I( 10, 0 ) );
352 chain.Append( VECTOR2I( 10, 5 ) );
353
354 VECTOR2I ptA, ptB;
355 bool result = circle.NearestPoints( &chain, ptA, ptB );
356
357 BOOST_CHECK( result );
358 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 3, 0 ), "Expected: " << VECTOR2I( 3, 0 ) << " Actual: " << ptA );
359 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 10, 0 ), "Expected: " << VECTOR2I( 10, 0 ) << " Actual: " << ptB );
360}
361
362BOOST_AUTO_TEST_CASE( NearestPoints_ArcToRect )
363{
364 SHAPE_ARC arc( VECTOR2I( 0, 0 ), VECTOR2I( 5, 5 ), VECTOR2I( 10, 0 ), 0 );
365 SHAPE_RECT rect( VECTOR2I( 150, 2 ), VECTOR2I( 50, 6 ) );
366
367 VECTOR2I ptA, ptB;
368 int64_t distSq;
369 bool result = arc.NearestPoints( rect, ptA, ptB, distSq );
370
371 BOOST_CHECK( result );
372 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 10, 0 ), "Expected: " << VECTOR2I( 10, 0 ) << " Actual: " << ptA );
373 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 50, 2 ), "Expected: " << VECTOR2I( 50, 2 ) << " Actual: " << ptB );
374
375}
376
377BOOST_AUTO_TEST_CASE( NearestPoints_ArcToSegment )
378{
379 SHAPE_ARC arc( VECTOR2I( 0, 0 ), VECTOR2I( 0, 10 ), VECTOR2I( 0, 20 ), 0 ); // Semicircle
380 SEG segment( VECTOR2I( 15, 10 ), VECTOR2I( 25, 10 ) );
381
382 VECTOR2I ptA, ptB;
383 int64_t distSq;
384 bool result = arc.NearestPoints( segment, ptA, ptB, distSq );
385
386 BOOST_CHECK( result );
387 // Arc point should be on the rightmost part of the arc
388 BOOST_CHECK( ptA.x >= 0 );
389 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 15, 10 ), "Expected: " << VECTOR2I( 15, 10 ) << " Actual: " << ptB );
390}
391
392BOOST_AUTO_TEST_CASE( NearestPoints_ArcToLineChain )
393{
394 SHAPE_ARC arc( VECTOR2I( 0, 0 ), VECTOR2I( 5, 5 ), VECTOR2I( 10, 0 ), 0 );
395
397 chain.Append( VECTOR2I( 5, 15 ) );
398 chain.Append( VECTOR2I( 5, 10 ) );
399 chain.Append( VECTOR2I( 15, 10 ) );
400
401 VECTOR2I ptA, ptB;
402 int64_t distSq;
403 bool result = chain.NearestPoints( &arc, ptB, ptA );
404
405 BOOST_CHECK( result );
406 // Arc point should be near the top of the arc
407 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 5, 5 ), "Expected: " << VECTOR2I( 5, 5 ) << " Actual: " << ptA );
408 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 5, 10 ), "Expected: " << VECTOR2I( 5, 10 ) << " Actual: " << ptB );
409}
410
411// Distance and Symmetry Validation Tests
412BOOST_AUTO_TEST_CASE( NearestPoints_DistanceValidation )
413{
414 SHAPE_CIRCLE circleA( VECTOR2I( 0, 0 ), 5 );
415 SHAPE_CIRCLE circleB( VECTOR2I( 20, 0 ), 3 );
416
417 VECTOR2I ptA, ptB;
418 bool result = circleA.NearestPoints( &circleB, ptA, ptB );
419
420 BOOST_CHECK( result );
421
422 // Calculate expected distance: center distance - both radii
423 int expectedDistance = 20 - 5 - 3; // 12
424 int actualDistance = (ptB - ptA).EuclideanNorm();
425
426 BOOST_CHECK_MESSAGE( actualDistance == expectedDistance,
427 "Expected distance: " << expectedDistance << " Actual: " << actualDistance );
428}
429
430BOOST_AUTO_TEST_CASE( NearestPoints_SymmetryTest )
431{
432 SHAPE_RECT rectA( VECTOR2I( 0, 0 ), VECTOR2I( 5, 5 ) );
433 SHAPE_CIRCLE circleB( VECTOR2I( 10, 2 ), 2 );
434
435 // Test A->B
436 VECTOR2I ptA1, ptB1;
437 bool result1 = rectA.NearestPoints( &circleB, ptA1, ptB1 );
438
439 // Test B->A
440 VECTOR2I ptA2, ptB2;
441 bool result2 = circleB.NearestPoints( &rectA, ptA2, ptB2 );
442
443 BOOST_CHECK( result1 && result2 );
444
445 // Distance should be the same both ways
446 int dist1 = (ptB1 - ptA1).EuclideanNorm();
447 int dist2 = (ptB2 - ptA2).EuclideanNorm();
448
449 BOOST_CHECK_MESSAGE( dist1 == dist2, "Distance A->B: " << dist1 << " Distance B->A: " << dist2 );
450
451 // Points should be swapped (ptA1 should equal ptB2, ptB1 should equal ptA2)
452 BOOST_CHECK_MESSAGE( ptA1 == ptB2, "Expected ptA1 == ptB2. ptA1: " << ptA1 << " ptB2: " << ptB2 );
453 BOOST_CHECK_MESSAGE( ptB1 == ptA2, "Expected ptB1 == ptA2. ptB1: " << ptB1 << " ptA2: " << ptA2 );
454}
455
456BOOST_AUTO_TEST_CASE( NearestPoints_MinimumDistanceValidation )
457{
458 SHAPE_SEGMENT segment( SEG( VECTOR2I( 0, 0 ), VECTOR2I( 10, 0 ) ), 0 );
459 SHAPE_CIRCLE circle( VECTOR2I( 5, 5 ), 2 );
460
461 VECTOR2I ptA, ptB;
462 bool result = segment.NearestPoints( &circle, ptA, ptB );
463
464 BOOST_CHECK( result );
465
466 // Verify that this is indeed the minimum distance by checking nearby points
467 int minDist = (ptB - ptA).EuclideanNorm();
468
469 // Check a few other points on the segment
470 for( int x = 0; x <= 10; x += 2 )
471 {
472 VECTOR2I testPt( x, 0 );
473 VECTOR2I circleClosest = circle.GetCenter() +
474 (testPt - circle.GetCenter()).Resize( circle.GetRadius() );
475 int testDist = (circleClosest - testPt).EuclideanNorm();
476
477 BOOST_CHECK_MESSAGE( minDist <= testDist,
478 "Found shorter distance at x=" << x << ": " << testDist << " vs " << minDist );
479 }
480}
481
482// Complex Line Chain Tests
483BOOST_AUTO_TEST_CASE( NearestPoints_LineChainWithMixedArcs )
484{
485 SHAPE_LINE_CHAIN chainA;
486 chainA.Append( VECTOR2I( 0, 0 ) );
487 chainA.Append( VECTOR2I( 10, 0 ) );
488 chainA.Append( SHAPE_ARC( VECTOR2I( 10, 0 ), VECTOR2I( 15, 5 ), VECTOR2I( 20, 0 ), 0 ) );
489 chainA.Append( VECTOR2I( 30, 0 ) );
490
491 SHAPE_CIRCLE circle( VECTOR2I( 15, 15 ), 3 );
492
493 VECTOR2I ptA, ptB;
494 bool result = chainA.NearestPoints( &circle, ptA, ptB );
495
496 BOOST_CHECK( result );
497 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 15, 12 ), "Expected: " << VECTOR2I( 15, 12 ) << " Actual: " << ptB );
498}
499
500// Degenerate Shape Tests
501BOOST_AUTO_TEST_CASE( NearestPoints_DegenerateShapes_ZeroWidthRect )
502{
503 SHAPE_RECT rectA( VECTOR2I( 0, 0 ), VECTOR2I( 0, 10 ) ); // Zero width
504 SHAPE_CIRCLE circle( VECTOR2I( 5, 5 ), 2 );
505
506 VECTOR2I ptA, ptB;
507 bool result = rectA.NearestPoints( &circle, ptA, ptB );
508
509 BOOST_CHECK( result );
510 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 0, 5 ), "Expected: " << VECTOR2I( 0, 5 ) << " Actual: " << ptA );
511 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 3, 5 ), "Expected: " << VECTOR2I( 3, 5 ) << " Actual: " << ptB );
512}
513
514BOOST_AUTO_TEST_CASE( NearestPoints_DegenerateShapes_ZeroLengthSegment )
515{
516 SHAPE_SEGMENT zeroSeg( VECTOR2I( 5, 5 ), VECTOR2I( 5, 5 ) ); // Point segment
517 SHAPE_CIRCLE circle( VECTOR2I( 10, 5 ), 3 );
518
519 VECTOR2I ptA, ptB;
520 bool result = circle.NearestPoints( &zeroSeg, ptA, ptB );
521
522 BOOST_CHECK( result );
523 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 7, 5 ), "Expected: " << VECTOR2I( 7, 5 ) << " Actual: " << ptA );
524 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 5, 5 ), "Expected: " << VECTOR2I( 5, 5 ) << " Actual: " << ptB );
525}
526
527BOOST_AUTO_TEST_CASE( NearestPoints_SinglePointLineChain )
528{
529 SHAPE_LINE_CHAIN singlePoint;
530 singlePoint.Append( VECTOR2I( 0, 0 ) );
531
532 SHAPE_CIRCLE circle( VECTOR2I( 5, 0 ), 2 );
533
534 VECTOR2I ptA, ptB;
535 bool result = singlePoint.NearestPoints( &circle, ptA, ptB );
536
537 BOOST_CHECK( !result );
538}
539
540// Negative Coordinate Tests
541BOOST_AUTO_TEST_CASE( NearestPoints_NegativeCoordinates )
542{
543 SHAPE_CIRCLE circleA( VECTOR2I( -10, -5 ), 3 );
544 SHAPE_RECT rect( VECTOR2I( 0, -2 ), VECTOR2I( 5, 4 ) );
545
546 VECTOR2I ptA, ptB;
547 bool result = circleA.NearestPoints( &rect, ptA, ptB );
548
549 BOOST_CHECK( result );
550 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( -7, -4 ), "Expected: " << VECTOR2I( -7, -4 ) << " Actual: " << ptA );
551 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 0, -2 ), "Expected: " << VECTOR2I( 0, -2 ) << " Actual: " << ptB );
552}
553
554// Edge case tests
555BOOST_AUTO_TEST_CASE( NearestPoints_IdenticalShapes )
556{
557 SHAPE_CIRCLE circleA( VECTOR2I( 5, 5 ), 3 );
558 SHAPE_CIRCLE circleB( VECTOR2I( 5, 5 ), 3 );
559
560 VECTOR2I ptA, ptB;
561 bool result = circleA.NearestPoints( &circleB, ptA, ptB );
562
563 BOOST_CHECK( result );
564 // Identical concentric circles - should return arbitrary points
565 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 8, 5 ), "Expected: " << VECTOR2I( 8, 5 ) << " Actual: " << ptA );
566 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 8, 5 ), "Expected: " << VECTOR2I( 8, 5 ) << " Actual: " << ptB );
567}
568
569BOOST_AUTO_TEST_CASE( NearestPoints_ZeroSizeShapes )
570{
571 SHAPE_CIRCLE circleA( VECTOR2I( 0, 0 ), 0 );
572 SHAPE_CIRCLE circleB( VECTOR2I( 10, 0 ), 5 );
573
574 VECTOR2I ptA, ptB;
575 bool result = circleA.NearestPoints( &circleB, ptA, ptB );
576
577 BOOST_CHECK( result );
578 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 0, 0 ), "Expected: " << VECTOR2I( 0, 0 ) << " Actual: " << ptA );
579 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 5, 0 ), "Expected: " << VECTOR2I( 5, 0 ) << " Actual: " << ptB );
580}
581
582BOOST_AUTO_TEST_CASE( NearestPoints_VeryCloseShapes )
583{
584 SHAPE_CIRCLE circleA( VECTOR2I( 0, 0 ), 5 );
585 SHAPE_CIRCLE circleB( VECTOR2I( 10, 0 ), 5 ); // Just touching
586
587 VECTOR2I ptA, ptB;
588 bool result = circleA.NearestPoints( &circleB, ptA, ptB );
589
590 BOOST_CHECK( result );
591 BOOST_CHECK_MESSAGE( ptA == VECTOR2I( 5, 0 ), "Expected: " << VECTOR2I( 5, 0 ) << " Actual: " << ptA );
592 BOOST_CHECK_MESSAGE( ptB == VECTOR2I( 5, 0 ), "Expected: " << VECTOR2I( 5, 0 ) << " Actual: " << ptB );
593}
594
Definition seg.h:38
bool NearestPoints(const SHAPE_ARC &aArc, VECTOR2I &aPtA, VECTOR2I &aPtB, int64_t &aDistSq) const
Compute closest points between this arc and aArc.
void AddShape(SHAPE *aShape)
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
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.
bool NearestPoints(const SHAPE *aOther, VECTOR2I &aPtThis, VECTOR2I &aPtOther) const
Return the two points that mark the closest distance between this shape and aOther.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_MESSAGE(totalMismatches==0, std::to_string(totalMismatches)+" board(s) with strategy disagreements")
const SHAPE_LINE_CHAIN chain
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
BOOST_AUTO_TEST_CASE(NearestPoints_CircleToCircle_Separate)
wxString result
Test unit parsing edge cases and error handling.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683