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