KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_shape_line_chain.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 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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <geometry/shape_arc.h>
22#include <trigo.h>
23
25#include <qa_utils/numeric.h>
27
28#include "geom_test_utils.h"
29
33
35{
49
56
58 {
59 ArcCircle = SHAPE_ARC( VECTOR2I( 183450000, 128360000 ),
60 VECTOR2I( 183850000, 128360000 ),
61 VECTOR2I( 183450000, 128360000 ), 0 );
62
63 Arc0a = SHAPE_ARC( VECTOR2I( 183450000, 128360000 ),
64 VECTOR2I( 183650000, 128560000 ),
65 VECTOR2I( 183850000, 128360000 ), 0 );
66
67 Arc0b = SHAPE_ARC( VECTOR2I( 183850000, 128360000 ),
68 VECTOR2I( 183650000, 128160000 ),
69 VECTOR2I( 183450000, 128360000 ), 0 );
70
71 Arc1 = SHAPE_ARC( VECTOR2I( 183850000, 128360000 ),
72 VECTOR2I( 183638550, 128640305 ),
73 VECTOR2I( 183500000, 129204974 ), 0 );
74
75 Arc2 = SHAPE_ARC( VECTOR2I( 283450000, 228360000 ),
76 VECTOR2I( 283650000, 228560000 ),
77 VECTOR2I( 283850000, 228360000 ), 0 );
78
79 Arc3 = SHAPE_ARC( VECTOR2I( 0, 0 ),
80 VECTOR2I( 24142136, 10000000 ),
81 VECTOR2I( 0, 20000000 ), 0 );
82
84 Circle1Arc.SetClosed( true );
85
88 Circle2Arcs.SetClosed( true );
89
92
94 ArcsCoincidentClosed.SetClosed( true );
95
98
100 DuplicateArcs.Append( Arc1, ARC_HIGH_DEF ); // should add a segment between end of the chain
101 // and new copy of the arc
102
103 ArcAndPoint.Append( Arc0a, ARC_HIGH_DEF );
104 ArcAndPoint.Append( VECTOR2I( 233450000, 228360000 ) );
105
108
109 OnePoint.Append( VECTOR2I( 233450000, 228360000 ) );
110
111 TwoPoints.Append( VECTOR2I( 233450000, 228360000 ) );
112 TwoPoints.Append( VECTOR2I( 263450000, 258360000 ) );
113
115 ThreePoints.Append( VECTOR2I( 263450000, 308360000 ) );
116
117 SegAndArcCoincident.Append( VECTOR2I( 0, 20000000 ) );
119 }
120};
121
122
123BOOST_FIXTURE_TEST_SUITE( TestShapeLineChain, SLC_CASES )
124
125
126BOOST_AUTO_TEST_CASE( ClipperConstructorCase1 )
127{
128 // Case of an arc followed by a segment
129 // The clipper path is not in order (on purpose), to simulate the typical return from clipper
130
131 Clipper2Lib::Path64 pathClipper2 = {
132 { { 125663951, 120099260, 24 }, { 125388111, 120170850, 25 }, { 125124975, 120280270, 26 },
133 { 124879705, 120425376, 27 }, { 124657110, 120603322, 28 }, { 124461556, 120810617, 29 },
134 { 124296876, 121043198, 30 }, { 124166301, 121296503, 31 }, { 124072391, 121565564, 32 },
135 { 124016988, 121845106, 33 }, { 124001177, 122129646, 34 }, { 124025270, 122413605, 35 },
136 { 124088794, 122691414, 36 }, { 124190502, 122957625, 37 }, { 124328401, 123207018, 38 },
137 { 124499787, 123434703, 39 }, { 124598846, 123537154, 40 }, { 127171000, 123786000, 4 },
138 { 127287862, 123704439, 5 }, { 127499716, 123513831, 6 }, { 127682866, 123295498, 7 },
139 { 127833720, 123053722, 8 }, { 127949321, 122793242, 9 }, { 128027402, 122519168, 10 },
140 { 128066430, 122236874, 11 }, { 128065642, 121951896, 12 }, { 128025053, 121669823, 13 },
141 { 127945457, 121396185, 14 }, { 127828417, 121136349, 15 }, { 127676227, 120895410, 16 },
142 { 127491873, 120678094, 17 }, { 127278968, 120488661, 18 }, { 127041689, 120330827, 19 },
143 { 126784688, 120207687, 20 }, { 126513005, 120121655, 21 }, { 126231968, 120074419, 22 },
144 { 125947087, 120066905, 23 } }
145 };
146
147 std::vector<CLIPPER_Z_VALUE> z_values = {
148 { { -1, -1 }, 0 }, { { -1, -1 }, 0 }, { { -1, -1 }, 0 }, { { -1, -1 }, 0 },
149 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
150 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
151 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
152 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
153 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
154 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
155 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
156 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
157 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
158 { { 0, -1 }, 0 }
159 };
160
161 std::vector<SHAPE_ARC> arcs = {
162 SHAPE_ARC( { 127171000, 123786000 }, { 126231718, 120077003 }, { 124598846, 123537154 }, 0 )
163 };
164
165 SHAPE_LINE_CHAIN clipper2chain( pathClipper2, z_values, arcs );
166
167 BOOST_CHECK( GEOM_TEST::IsOutlineValid( clipper2chain ) );
168
169 BOOST_CHECK_EQUAL( clipper2chain.PointCount(), 37 );
170
171 BOOST_CHECK_EQUAL( clipper2chain.ArcCount(), 1 );
172
173 BOOST_CHECK_EQUAL( clipper2chain.ShapeCount(), 2 );
174
175 BOOST_CHECK_EQUAL( clipper2chain.IsClosed(), true );
176}
177
178
179BOOST_AUTO_TEST_CASE( ArcToPolyline )
180{
181 SHAPE_LINE_CHAIN base_chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 1000 ), VECTOR2I( 1000, 0 ) } );
182
183 SHAPE_LINE_CHAIN chain_insert( {
184 VECTOR2I( 0, 1500 ),
185 VECTOR2I( 1500, 1500 ),
186 VECTOR2I( 1500, 0 ),
187 } );
188
189 SHAPE_LINE_CHAIN arc_insert1( SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), ANGLE_180 ),
190 false, ARC_HIGH_DEF );
191
192 SHAPE_LINE_CHAIN arc_insert2( SHAPE_ARC( VECTOR2I( 0, 500 ), VECTOR2I( 0, 400 ), ANGLE_180 ),
193 false, ARC_HIGH_DEF );
194
195 BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
196 BOOST_CHECK_EQUAL( arc_insert1.CShapes().size(), arc_insert1.CPoints().size() );
197 BOOST_CHECK_EQUAL( arc_insert2.CShapes().size(), arc_insert2.CPoints().size() );
198
199 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
200 BOOST_CHECK( GEOM_TEST::IsOutlineValid( arc_insert1 ) );
201 BOOST_CHECK( GEOM_TEST::IsOutlineValid( arc_insert2 ) );
202
203 base_chain.Insert( 0, SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), ANGLE_180 ), ARC_HIGH_DEF );
204 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
205 BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
206
207 base_chain.Replace( 0, 2, chain_insert );
208 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
209 BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
210}
211
212
213// Similar test to above but with larger coordinates, so we have more than one point per arc
214BOOST_AUTO_TEST_CASE( ArcToPolylineLargeCoords )
215{
216 SHAPE_LINE_CHAIN base_chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( 100000, 0 ) } );
217
218 SHAPE_LINE_CHAIN chain_insert( {
219 VECTOR2I( 0, 1500000 ),
220 VECTOR2I( 1500000, 1500000 ),
221 VECTOR2I( 1500000, 0 ),
222 } );
223
224 base_chain.Append( SHAPE_ARC( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 100000 ), ANGLE_180 ),
225 ARC_HIGH_DEF );
226
227 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
228 BOOST_CHECK_EQUAL( base_chain.PointCount(), 11 );
229
230 base_chain.Insert( 9, VECTOR2I( 250000, 0 ) );
231 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
232 BOOST_CHECK_EQUAL( base_chain.PointCount(), 12 );
233 BOOST_CHECK_EQUAL( base_chain.ArcCount(), 2 ); // Should have two arcs after the split
234
235 base_chain.Replace( 5, 6, chain_insert );
236 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
237 BOOST_CHECK_EQUAL( base_chain.PointCount(), 13 ); // Adding 3 points, removing 2
238 BOOST_CHECK_EQUAL( base_chain.ArcCount(), 3 ); // Should have three arcs after the split
239
240 base_chain.Replace( 4, 6, VECTOR2I( 550000, 0 ) );
241 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
242 BOOST_CHECK_EQUAL( base_chain.PointCount(), 11 ); // Adding 1 point, removing 3
243 BOOST_CHECK_EQUAL( base_chain.ArcCount(), 3 ); // Should still have three arcs
244
245 // Test ClearArcs
246 base_chain.SetClosed( true );
247 double areaPriorToArcRemoval = base_chain.Area();
248 base_chain.ClearArcs();
249
250 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
251 BOOST_CHECK_EQUAL( base_chain.CPoints().size(), base_chain.CShapes().size() );
252 BOOST_CHECK_EQUAL( base_chain.PointCount(), 11 ); // We should have the same number of points
253 BOOST_CHECK_EQUAL( base_chain.ArcCount(), 0 ); // All arcs should have been removed
254 BOOST_CHECK_EQUAL( base_chain.Area(), areaPriorToArcRemoval ); // Area should not have changed
255}
256
257// Test that duplicate point gets removed when line is set to be closed and added where required
258BOOST_AUTO_TEST_CASE( SetClosedDuplicatePoint )
259{
260 // Test from issue #9843
262
263 chain.Append( SHAPE_ARC( { -859598, 2559876 }, { -1632771, 1022403 }, { -3170244, 249230 }, 0 ),
264 ARC_HIGH_DEF );
265
266 chain.Append( SHAPE_ARC( { -3170244, -1657832 }, { -292804, -317564 }, { 1047464, 2559876 }, 0 ),
267 ARC_HIGH_DEF );
268
269 chain.Append( VECTOR2I( -859598, 2559876 ) ); // add point that is equal to first arc start
270
271 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
272 BOOST_CHECK_EQUAL( chain.PointCount(), 31 );
273
274 // CLOSED CHAIN
275 chain.SetClosed( true );
276 BOOST_CHECK_EQUAL( chain.CPoints().size(), chain.CShapes().size() );
277 BOOST_CHECK_EQUAL( chain.PointCount(), 30 ); // (-1) should have removed coincident points
278 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
279
280 // Special case: arc wrapping around to start (e.g. circle)
281 BOOST_CHECK( GEOM_TEST::IsOutlineValid( Circle2Arcs ) );
282 BOOST_CHECK_EQUAL( Circle2Arcs.IsClosed(), true );
283 BOOST_CHECK_EQUAL( Circle2Arcs.PointCount(), 16 );
284 BOOST_CHECK_EQUAL( Circle2Arcs.IsArcSegment( 15 ), true );
285 BOOST_CHECK_EQUAL( Circle2Arcs.ShapeCount(), 2 );
286 Circle2Arcs.SetClosed( false );
287 BOOST_CHECK( GEOM_TEST::IsOutlineValid( Circle2Arcs ) );
288 BOOST_CHECK_EQUAL( Circle2Arcs.IsClosed(), false );
289 BOOST_CHECK_EQUAL( Circle2Arcs.PointCount(), 17 );
290 BOOST_CHECK_EQUAL( Circle2Arcs.IsArcSegment( 15 ), true );
291 BOOST_CHECK_EQUAL( Circle2Arcs.IsArcSegment( 16 ), false ); // last point doesn't join up
292}
293
304
305static const std::vector<CLOSE_TOGGLE_SHAPE_CASE> close_toggle_shape_cases =
306 {
307 { "Circle1Arc", SLC_CASES().Circle1Arc, true, 1, 15, 1, 16 },
308 { "Circle2Arcs", SLC_CASES().Circle2Arcs, true, 2, 16, 2, 17 },
309 { "ArcsCoincident", SLC_CASES().ArcsCoincident, false, 2, 14, 3, 14 },
310 { "ArcsCoincidentClosed", SLC_CASES().ArcsCoincidentClosed, true, 3, 14, 2, 14 },
311 { "ArcsIndependent", SLC_CASES().ArcsIndependent, false, 3, 18, 4, 18 },
312 // SegAndArcCoincident will remove the segment after SetClosed(true) and SetClosed(false)
313 // disable test for now
314 //{ "SegAndArcCoincident", SLC_CASES().SegAndArcCoincident, false, 2, 92, 2, 91 },
315 { "DuplicateArcs", SLC_CASES().DuplicateArcs, false, 4, 20, 5, 20 },
316 { "ArcAndPoint", SLC_CASES().ArcAndPoint, false, 2, 10, 3, 10 },
317 { "ArcsAndSegMixed", SLC_CASES().ArcsAndSegMixed, false, 4, 19, 5, 19 },
318 { "OnePoint", SLC_CASES().OnePoint, false, 0, 1, 0, 1 }, // no shapes
319 { "TwoPoints", SLC_CASES().TwoPoints, false, 1, 2, 2, 2 }, // there and back
320 { "ThreePoints", SLC_CASES().ThreePoints, false, 2, 3, 3, 3 },
321 };
322
323BOOST_AUTO_TEST_CASE( ToggleClosed )
324{
326 {
327 BOOST_TEST_CONTEXT( c.m_ctx_name )
328 {
329 SHAPE_LINE_CHAIN slc_case = c.m_chain; // make a copy to edit
330 BOOST_CHECK( GEOM_TEST::IsOutlineValid( slc_case ) );
331 BOOST_CHECK_EQUAL( slc_case.IsClosed(), c.m_closed );
332 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
333 BOOST_CHECK_EQUAL( slc_case.PointCount(), c.m_point_count );
334 slc_case.SetClosed( !c.m_closed );
335 BOOST_CHECK( GEOM_TEST::IsOutlineValid( slc_case ) );
336 BOOST_CHECK_EQUAL( slc_case.IsClosed(), !c.m_closed );
337 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_expected_shape_count );
338 BOOST_CHECK_EQUAL( slc_case.PointCount(), c.m_expected_point_count );
339 slc_case.SetClosed( c.m_closed ); // toggle back to normal
340 BOOST_CHECK( GEOM_TEST::IsOutlineValid( slc_case ) );
341 BOOST_CHECK_EQUAL( slc_case.IsClosed(), c.m_closed );
342 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
343 BOOST_CHECK_EQUAL( slc_case.PointCount(), c.m_point_count );
344 }
345 }
346}
347
348
349BOOST_AUTO_TEST_CASE( PointInPolygon )
350{
351 SHAPE_LINE_CHAIN outline1( { { 1316455, 913576 }, { 1316455, 901129 }, { 1321102, 901129 },
352 { 1322152, 901191 }, { 1323055, 901365 }, { 1323830, 901639 },
353 { 1324543, 902036 }, { 1325121, 902521 }, { 1325581, 903100 },
354 { 1325914, 903759 }, { 1326120, 904516 }, { 1326193, 905390 },
355 { 1326121, 906253 }, { 1325915, 907005 }, { 1325581, 907667 },
356 { 1325121, 908248 }, { 1324543, 908735 }, { 1323830, 909132 },
357 { 1323055, 909406 }, { 1322153, 909579 }, { 1321102, 909641 },
358 { 1317174, 909641 }, { 1317757, 909027 }, { 1317757, 913576 } } );
359 SHAPE_LINE_CHAIN outline2( { { 1297076, 916244 }, { 1284629, 916244 }, { 1284629, 911597 },
360 { 1284691, 910547 }, { 1284865, 909644 }, { 1285139, 908869 },
361 { 1285536, 908156 }, { 1286021, 907578 }, { 1286600, 907118 },
362 { 1287259, 906785 }, { 1288016, 906579 }, { 1288890, 906506 },
363 { 1289753, 906578 }, { 1290505, 906784 }, { 1291167, 907118 },
364 { 1291748, 907578 }, { 1292235, 908156 }, { 1292632, 908869 },
365 { 1292906, 909644 }, { 1293079, 910546 }, { 1293141, 911597 },
366 { 1293141, 915525 }, { 1292527, 914942 }, { 1297076, 914942 } } );
367
368 // Test a point inside the polygon
369 VECTOR2I point1( 1317757, 909133 );
370 VECTOR2I point2( 1292633, 914942 );
371
372 outline1.SetClosed( true );
373 outline2.SetClosed( true );
374
375 BOOST_CHECK( outline1.PointInside( point1, 0, false ) );
376 BOOST_CHECK( outline2.PointInside( point2, 0, false ) );
377}
378
379// Test that duplicate point gets removed when we call simplify
380BOOST_AUTO_TEST_CASE( SimplifyDuplicatePoint )
381{
383
384 chain.Append( { 100, 100 } );
385 chain.Append( { 100, 100 }, true ); //duplicate point to simplify
386 chain.Append( { 200, 100 } );
387
388 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
389 BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
390
391 chain.Simplify();
392
393 BOOST_CHECK_EQUAL( chain.CPoints().size(), chain.CShapes().size() );
394 BOOST_CHECK_EQUAL( chain.PointCount(), 2 ); // (-1) should have removed coincident points
395 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
396}
397
398
399// Test that duplicate point gets removed when we call simplify
400BOOST_AUTO_TEST_CASE( SimplifyKeepEndPoint )
401{
403
404 chain.Append( { 114772424, 90949410 } );
405 chain.Append( { 114767360, 90947240 } );
406 chain.Append( { 114772429, 90947228 } );
407 chain.SetClosed( true );
408
409 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
410 BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
411
412 chain.Simplify();
413
414 BOOST_CHECK_EQUAL( chain.CPoints().size(), chain.CShapes().size() );
415 BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
416 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
417}
418
419
420BOOST_AUTO_TEST_CASE( SimplifyPNSChain )
421{
423 chain.Append( VECTOR2I( 157527820, 223074385 ) );
424 chain.Append( VECTOR2I( 186541122, 159990156 ) );
425 chain.Append( VECTOR2I( 186528624, 159977658 ) );
426 chain.Append( VECTOR2I( 186528624, 159770550 ) );
427 chain.Append( VECTOR2I( 186528625, 159366691 ) );
428 chain.Append( VECTOR2I( 186541122, 159354195 ) );
429 chain.Append( VECTOR2I( 186541122, 155566877 ) );
430 chain.Append( VECTOR2I( 187291125, 154816872 ) );
431 chain.Append( VECTOR2I( 187291125, 147807837 ) );
432 chain.Append( VECTOR2I( 189301788, 145797175 ) );
433 chain.Append( VECTOR2I( 194451695, 145797175 ) );
434 chain.Append( VECTOR2I( 195021410, 146366890 ) );
435
436 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
437 BOOST_CHECK_EQUAL( chain.PointCount(), 12 );
438
439 // The chain should be open, so the points should not be simplified
440 // between the begining and the end.
441 chain.Simplify( 10 );
442
443 BOOST_CHECK_EQUAL( chain.PointCount(), 11 );
444}
445
446
447BOOST_AUTO_TEST_CASE( SimplifyComplexChain )
448{
450
451 // Append points
452 chain.Append( { 130000, 147320 } );
453 chain.Append( { 125730, 147320 } );
454 chain.Append( { 125730, 150630 } );
455 chain.Append( { 128800, 153700 } );
456 chain.Append( { 150300, 153700 } );
457 chain.Append( { 151500, 152500 } );
458 chain.Append( { 151500, 148900 } );
459 chain.Append( { 149920, 147320 } );
460 chain.Append( { 140000, 147320 } );
461
462 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
463 BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
464
465 // The chain should be open, so the points should not be simplified
466 // between the begining and the end.
467 chain.Simplify();
468
469 BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
470
471 chain.SetClosed( true );
472 chain.Simplify();
473
474 BOOST_CHECK_EQUAL( chain.PointCount(), 8 );
475}
476
487
488static const std::vector<REMOVE_SHAPE_CASE> remove_shape_cases =
489 {
490 { "Circle1Arc - 1st arc - index on start", SLC_CASES().Circle1Arc, 1, 1, 0, 0, 0 },
491 { "Circle1Arc - 1st arc - index on mid", SLC_CASES().Circle1Arc, 1, 1, 8, 0, 0 },
492 { "Circle1Arc - 1st arc - index on end", SLC_CASES().Circle1Arc, 1, 1, 14, 0, 0 },
493 { "Circle1Arc - 1st arc - index on -1", SLC_CASES().Circle1Arc, 1, 1, -1, 0, 0 },
494 { "Circle1Arc - invalid index", SLC_CASES().Circle1Arc, 1, 1, 15, 1, 1 },
495
496 { "Circle2Arcs - 1st arc - index on start", SLC_CASES().Circle2Arcs, 2, 2, 0, 2, 1 },
497 { "Circle2Arcs - 1st arc - index on mid", SLC_CASES().Circle2Arcs, 2, 2, 3, 2, 1 },
498 { "Circle2Arcs - 1st arc - index on end", SLC_CASES().Circle2Arcs, 2, 2, 7, 2, 1 },
499 { "Circle2Arcs - 2nd arc - index on start", SLC_CASES().Circle2Arcs, 2, 2, 8, 2, 1 },
500 { "Circle2Arcs - 2nd arc - index on mid", SLC_CASES().Circle2Arcs, 2, 2, 11, 2, 1 },
501 { "Circle2Arcs - 2nd arc - index on end", SLC_CASES().Circle2Arcs, 2, 2, 15, 2, 1 },
502 { "Circle2Arcs - 2nd arc - index on -1", SLC_CASES().Circle2Arcs, 2, 2, -1, 2, 1 },
503 { "Circle2Arcs - invalid index", SLC_CASES().Circle2Arcs, 2, 2, 16, 2, 2 },
504
505 { "ArcsCoinc. - 1st arc - idx on start", SLC_CASES().ArcsCoincident, 2, 2, 0, 1, 1 },
506 { "ArcsCoinc. - 1st arc - idx on mid", SLC_CASES().ArcsCoincident, 2, 2, 3, 1, 1 },
507 { "ArcsCoinc. - 1st arc - idx on end", SLC_CASES().ArcsCoincident, 2, 2, 7, 1, 1 },
508 { "ArcsCoinc. - 2nd arc - idx on start", SLC_CASES().ArcsCoincident, 2, 2, 8, 1, 1 },
509 { "ArcsCoinc. - 2nd arc - idx on mid", SLC_CASES().ArcsCoincident, 2, 2, 10, 1, 1 },
510 { "ArcsCoinc. - 2nd arc - idx on end", SLC_CASES().ArcsCoincident, 2, 2, 13, 1, 1 },
511 { "ArcsCoinc. - 2nd arc - idx on -1", SLC_CASES().ArcsCoincident, 2, 2, -1, 1, 1 },
512 { "ArcsCoinc. - invalid idx", SLC_CASES().ArcsCoincident, 2, 2, 14, 2, 2 },
513 { "ArcsCoinc. - 1st arc - idx on start", SLC_CASES().ArcsCoincident, 2, 2, 0, 1, 1 },
514
515 { "A.Co.Closed - 1st arc - idx on start", SLC_CASES().ArcsCoincidentClosed, 3, 2, 1, 2, 1 },
516 { "A.Co.Closed - 1st arc - idx on mid", SLC_CASES().ArcsCoincidentClosed, 3, 2, 3, 2, 1 },
517 { "A.Co.Closed - 1st arc - idx on end", SLC_CASES().ArcsCoincidentClosed, 3, 2, 7, 2, 1 },
518 { "A.Co.Closed - 2nd arc - idx on start", SLC_CASES().ArcsCoincidentClosed, 3, 2, 8, 2, 1 },
519 { "A.Co.Closed - 2nd arc - idx on mid", SLC_CASES().ArcsCoincidentClosed, 3, 2, 10, 2, 1 },
520 { "A.Co.Closed - 2nd arc - idx on end", SLC_CASES().ArcsCoincidentClosed, 3, 2, 13, 2, 1 },
521 { "A.Co.Closed - 2nd arc - idx on -1", SLC_CASES().ArcsCoincidentClosed, 3, 2, -1, 2, 1 },
522 { "A.Co.Closed - invalid idx", SLC_CASES().ArcsCoincidentClosed, 3, 2, 14, 3, 2 },
523
524 { "ArcsIndep. - 1st arc - idx on start", SLC_CASES().ArcsIndependent, 3, 2, 0, 1, 1 },
525 { "ArcsIndep. - 1st arc - idx on mid", SLC_CASES().ArcsIndependent, 3, 2, 3, 1, 1 },
526 { "ArcsIndep. - 1st arc - idx on end", SLC_CASES().ArcsIndependent, 3, 2, 8, 1, 1 },
527 { "ArcsIndep. - 2nd arc - idx on start", SLC_CASES().ArcsIndependent, 3, 2, 9, 1, 1 },
528 { "ArcsIndep. - 2nd arc - idx on mid", SLC_CASES().ArcsIndependent, 3, 2, 12, 1, 1 },
529 { "ArcsIndep. - 2nd arc - idx on end", SLC_CASES().ArcsIndependent, 3, 2, 17, 1, 1 },
530 { "ArcsIndep. - 2nd arc - idx on -1", SLC_CASES().ArcsIndependent, 3, 2, -1, 1, 1 },
531 { "ArcsIndep. - invalid idx", SLC_CASES().ArcsIndependent, 3, 2, 18, 3, 2 },
532
533 { "Dup.Arcs - 1st arc - idx on start", SLC_CASES().DuplicateArcs, 4, 3, 0, 3, 2 },
534 { "Dup.Arcs - 1st arc - idx on mid", SLC_CASES().DuplicateArcs, 4, 3, 3, 3, 2 },
535 { "Dup.Arcs - 1st arc - idx on end", SLC_CASES().DuplicateArcs, 4, 3, 7, 3, 2 },
536 { "Dup.Arcs - 2nd arc - idx on start", SLC_CASES().DuplicateArcs, 4, 3, 8, 3, 2 },
537 { "Dup.Arcs - 2nd arc - idx on mid", SLC_CASES().DuplicateArcs, 4, 3, 10, 3, 2 },
538 { "Dup.Arcs - 2nd arc - idx on end", SLC_CASES().DuplicateArcs, 4, 3, 13, 3, 2 },
539 { "Dup.Arcs - 3rd arc - idx on start", SLC_CASES().DuplicateArcs, 4, 3, 14, 2, 2 },
540 { "Dup.Arcs - 3rd arc - idx on mid", SLC_CASES().DuplicateArcs, 4, 3, 17, 2, 2 },
541 { "Dup.Arcs - 3rd arc - idx on end", SLC_CASES().DuplicateArcs, 4, 3, 19, 2, 2 },
542 { "Dup.Arcs - 3rd arc - idx on -1", SLC_CASES().DuplicateArcs, 4, 3, -1, 2, 2 },
543 { "Dup.Arcs - invalid idx", SLC_CASES().DuplicateArcs, 4, 3, 20, 4, 3 },
544
545 { "Arcs Mixed - 1st arc - idx on start", SLC_CASES().ArcsAndSegMixed, 4, 2, 0, 2, 1 },
546 { "Arcs Mixed - 1st arc - idx on mid", SLC_CASES().ArcsAndSegMixed, 4, 2, 3, 2, 1 },
547 { "Arcs Mixed - 1st arc - idx on end", SLC_CASES().ArcsAndSegMixed, 4, 2, 8, 2, 1 },
548 { "Arcs Mixed - Straight segment", SLC_CASES().ArcsAndSegMixed, 4, 2, 9, 3, 2 },
549 { "Arcs Mixed - 2nd arc - idx on start", SLC_CASES().ArcsAndSegMixed, 4, 2, 10, 2, 1 },
550 { "Arcs Mixed - 2nd arc - idx on mid", SLC_CASES().ArcsAndSegMixed, 4, 2, 14, 2, 1 },
551 { "Arcs Mixed - 2nd arc - idx on end", SLC_CASES().ArcsAndSegMixed, 4, 2, 18, 2, 1 },
552 { "Arcs Mixed - 2nd arc - idx on -1", SLC_CASES().ArcsAndSegMixed, 4, 2, -1, 2, 1 },
553 { "Arcs Mixed - invalid idx", SLC_CASES().ArcsAndSegMixed, 4, 2, 19, 4, 2 }
554 };
555
556
558{
559 for( const REMOVE_SHAPE_CASE& c : remove_shape_cases )
560 {
561 BOOST_TEST_CONTEXT( c.m_ctx_name )
562 {
563 SHAPE_LINE_CHAIN slc_case = c.m_chain; // make a copy to edit
564 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
565 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_arc_count );
566 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
567 slc_case.RemoveShape( c.m_remove_index );
568 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_expected_shape_count );
569 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_expected_arc_count );
570 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
571 }
572 }
573}
574
575
576BOOST_AUTO_TEST_CASE( RemoveShapeAfterSimplify )
577{
578 for( const REMOVE_SHAPE_CASE& c : remove_shape_cases )
579 {
580 BOOST_TEST_CONTEXT( c.m_ctx_name )
581 {
582 SHAPE_LINE_CHAIN slc_case = c.m_chain; // make a copy to edit
583 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
584 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
585 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_arc_count );
586 slc_case.Simplify();
587 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
588 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
589 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_arc_count );
590 slc_case.RemoveShape( c.m_remove_index );
591 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
592 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_expected_shape_count );
593 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_expected_arc_count );
594 }
595 }
596}
597
598
600{
601 BOOST_CHECK_EQUAL( Circle1Arc.ShapeCount(), 1 );
602 BOOST_CHECK_EQUAL( Circle2Arcs.ShapeCount(), 2 );
603 BOOST_CHECK_EQUAL( ArcsCoincident.ShapeCount(), 2 );
604 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.ShapeCount(), 3 );
605 BOOST_CHECK_EQUAL( DuplicateArcs.ShapeCount(), 4 );
606 BOOST_CHECK_EQUAL( ArcAndPoint.ShapeCount(), 2 );
607 BOOST_CHECK_EQUAL( ArcsAndSegMixed.ShapeCount(), 4 );
608 BOOST_CHECK_EQUAL( SegAndArcCoincident.ShapeCount(), 2 );
609 BOOST_CHECK_EQUAL( EmptyChain.ShapeCount(), 0 );
610 BOOST_CHECK_EQUAL( OnePoint.ShapeCount(), 0 );
611 BOOST_CHECK_EQUAL( TwoPoints.ShapeCount(), 1 );
612 BOOST_CHECK_EQUAL( ThreePoints.ShapeCount(), 2 );
613}
614
615
617{
618 BOOST_CHECK_EQUAL( Circle1Arc.NextShape( 0 ), -1 ); //only one arc
619
620 BOOST_CHECK_EQUAL( Circle2Arcs.NextShape( 0 ), 8 ); // next shape "Arc0b"
621 BOOST_CHECK_EQUAL( Circle2Arcs.NextShape( 8 ), -1 ); //no more shapes (last point joins with first, part of arc)
622
623 BOOST_CHECK_EQUAL( ArcsCoincident.NextShape( 0 ), 8 ); // next shape "Arc1"
624 BOOST_CHECK_EQUAL( ArcsCoincident.NextShape( 8 ), -1 ); //no more shapes
625
626 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.NextShape( 0 ), 8 ); // next shape "Arc1"
627 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.NextShape( 8 ), 13 ); //next shape is hidden segment joining last/first
628 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.NextShape( 13 ), -1 ); //no more shapes
629
630 BOOST_CHECK_EQUAL( ArcsIndependent.NextShape( 0 ), 8 ); // next shape straight seg
631 BOOST_CHECK_EQUAL( ArcsIndependent.NextShape( 8 ), 9 ); //next shape second arc
632 BOOST_CHECK_EQUAL( ArcsIndependent.NextShape( 9 ), -1 ); //no more shapes
633
634 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 0 ), 8 ); // next shape "Arc1"
635 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 8 ), 13 ); // next shape hidden segment joining the 2 duplicate arcs
636 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 13 ), 14 ); // next shape "Arc1" (duplicate)
637 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 14 ), -1 ); //no more shapes
638
639 BOOST_CHECK_EQUAL( ArcAndPoint.NextShape( 0 ), 8 ); // next shape straight segment (end of arc->point)
640 BOOST_CHECK_EQUAL( ArcAndPoint.NextShape( 8 ), -1 ); //no more shapes
641
642 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 0 ), 8 ); // next shape straight segment (end of arc->point)
643 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 8 ), 9 ); // next shape straight segment (point->begining of arc)
644 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 9 ), 10 ); //next shape second arc
645 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 10 ), -1 ); //no more shapes
646 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 20 ), -1 ); //invalid indices should still work
647 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( -50 ), -1 ); //invalid indices should still work
648
649 BOOST_CHECK_EQUAL( SegAndArcCoincident.NextShape( 0 ), 1 ); // next shape Arc3
650 BOOST_CHECK_EQUAL( SegAndArcCoincident.NextShape( 1 ), -1 ); //no more shapes
651
652 BOOST_CHECK_EQUAL( EmptyChain.NextShape( 0 ), -1 );
653 BOOST_CHECK_EQUAL( EmptyChain.NextShape( 1 ), -1 ); //invalid indices should still work
654 BOOST_CHECK_EQUAL( EmptyChain.NextShape( 2 ), -1 ); //invalid indices should still work
655 BOOST_CHECK_EQUAL( EmptyChain.NextShape( -2 ), -1 ); //invalid indices should still work
656
657 BOOST_CHECK_EQUAL( OnePoint.NextShape( 0 ), -1 );
658 BOOST_CHECK_EQUAL( OnePoint.NextShape( -1 ), -1 );
659 BOOST_CHECK_EQUAL( OnePoint.NextShape( 1 ), -1 ); //invalid indices should still work
660 BOOST_CHECK_EQUAL( OnePoint.NextShape( 2 ), -1 ); //invalid indices should still work
661 BOOST_CHECK_EQUAL( OnePoint.NextShape( -2 ), -1 ); //invalid indices should still work
662
663 BOOST_CHECK_EQUAL( TwoPoints.NextShape( 0 ), -1 );
664 BOOST_CHECK_EQUAL( TwoPoints.NextShape( 1 ), -1 );
665 BOOST_CHECK_EQUAL( TwoPoints.NextShape( -1 ), -1 );
666
667 BOOST_CHECK_EQUAL( ThreePoints.NextShape( 0 ), 1 );
668 BOOST_CHECK_EQUAL( ThreePoints.NextShape( 1 ), -1 );
669 BOOST_CHECK_EQUAL( ThreePoints.NextShape( 2 ), -1 );
670 BOOST_CHECK_EQUAL( ThreePoints.NextShape( -1 ), -1 );
671}
672
673
674
676{
677 BOOST_TEST_CONTEXT( "Case 1: Arc mid point nearly collinear" )
678 {
679 SHAPE_ARC arc( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 2499 ), VECTOR2I( -100000, 0 ), 0 );
681 chain.Append( arc, ARC_HIGH_DEF );
682 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
683 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
684 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
685 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 100000, 0 ) ); //arc start
686 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( -100000, 0 ) ); //arc end
687 BOOST_CHECK_EQUAL( chain.GetPoint( -1 ), VECTOR2I( -100000, 0 ) ); //arc end
688 }
689
690 BOOST_TEST_CONTEXT( "Case 2: Arc = Large Circle" )
691 {
692 SHAPE_ARC arc( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 0 ), VECTOR2I( 100000, 0 ), 0 );
694 chain.Append( arc, ARC_HIGH_DEF );
695 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
696 BOOST_CHECK_EQUAL( chain.ArcCount(), 1 );
697 BOOST_CHECK_EQUAL( chain.PointCount(), 10 );
698 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 100000, 0 ) ); //arc start
699 BOOST_CHECK_EQUAL( chain.GetPoint( 9 ), VECTOR2I( 100000, 0 ) ); //arc end
700 BOOST_CHECK_EQUAL( chain.GetPoint( -1 ), VECTOR2I( 100000, 0 ) ); //arc end
701 }
702
703 BOOST_TEST_CONTEXT( "Case 3: Arc = Small Circle (approximate to point)" )
704 {
705 SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 0, 0 ), VECTOR2I( 2499, 0 ), 0 );
707 chain.Append( arc, ARC_HIGH_DEF );
708 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
709 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
710 BOOST_CHECK_EQUAL( chain.PointCount(), 1 );
711 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
712 }
713
714 BOOST_TEST_CONTEXT( "Case 3: Small Arc (approximate to segment)" )
715 {
716 SHAPE_ARC arc( VECTOR2I( 1767, 0 ), VECTOR2I( 2499, 2499 ), VECTOR2I( 0, 1767 ), 0 );
718 chain.Append( arc, ARC_HIGH_DEF );
719 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
720 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
721 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
722 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 1767, 0 ) ); //arc start
723 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 0, 1767 ) ); //arc end
724 }
725
726 BOOST_TEST_CONTEXT( "Case 4: Arc = null arc (all points coincident)" )
727 {
728 SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 2499, 0 ), VECTOR2I( 2499, 0 ), 0 );
730 chain.Append( arc, ARC_HIGH_DEF );
731 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
732 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
733 BOOST_CHECK_EQUAL( chain.PointCount(), 1 );
734 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
735 }
736
737 BOOST_TEST_CONTEXT( "Case 5: Arc = infinite radius (all points very close)" )
738 {
739 SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 2500, 0 ), VECTOR2I( 2501, 0 ), 0 );
741 chain.Append( arc, ARC_HIGH_DEF );
742 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
743 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
744 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
745 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
746 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 2501, 0 ) ); //arc end
747 }
748
749 BOOST_TEST_CONTEXT( "Case 6: Arc = large radius (all points very close)" )
750 {
751 SHAPE_ARC arc( VECTOR2I( -100000, 0 ), VECTOR2I( 0, 1 ), VECTOR2I( 100000, 0 ), 0 );
753 chain.Append( arc, ARC_HIGH_DEF );
754 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
755 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
756 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
757 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( -100000, 0 ) ); //arc start
758 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 100000, 0 ) ); //arc end
759 }
760}
761
762
763// Test special case where the last arc in the chain has a shared point with the first arc
764BOOST_AUTO_TEST_CASE( ArcWrappingToStartSharedPoints )
765{
766 // represent a circle with two semicircular arcs
767 SHAPE_ARC arc1( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( -100000, 0 ), 0 );
768 SHAPE_ARC arc2( VECTOR2I( -100000, 0 ), VECTOR2I( 0, -100000 ), VECTOR2I( 100000, 0 ), 0 );
769
770 // Start a chain with the two arcs
772 chain.Append( arc1, ARC_HIGH_DEF );
773 chain.Append( arc2, ARC_HIGH_DEF );
774 BOOST_CHECK_EQUAL( chain.PointCount(), 13 );
775 //BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
776
777 // OPEN CHAIN
778 // Start of the chain is not yet a shared point, so can't be an arc end either
779 BOOST_CHECK_EQUAL( chain.IsSharedPt( 0 ), false );
780 BOOST_CHECK_EQUAL( chain.IsArcEnd( 0 ), false );
781 BOOST_CHECK_EQUAL( chain.IsArcStart( 0 ), true );
782
783 // Index 6 is the shared point between the two arcs in the middle of the chain
784 BOOST_CHECK_EQUAL( chain.IsSharedPt( 6 ), true );
785 BOOST_CHECK_EQUAL( chain.IsArcEnd( 6 ), true );
786 BOOST_CHECK_EQUAL( chain.IsArcStart( 6 ), true );
787
788 // End index is not yet a shared point
789 int endIndex = chain.PointCount() - 1;
790 BOOST_CHECK_EQUAL( chain.IsSharedPt( endIndex ), false );
791 BOOST_CHECK_EQUAL( chain.IsArcEnd( endIndex ), true );
792 BOOST_CHECK_EQUAL( chain.IsArcStart( endIndex ), false );
793
794 for( int i = 0; i < chain.PointCount(); i++ )
795 {
796 BOOST_CHECK_EQUAL( chain.IsPtOnArc( i ), true ); // all points in the chain are arcs
797 }
798
799 // CLOSED CHAIN
800 chain.SetClosed( true );
801 BOOST_CHECK_EQUAL( chain.PointCount(), 12 ); // (-1) should have removed coincident points
802 //BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
803
804 // Start of the chain should be a shared point now, so can't be an arc end either
805 BOOST_CHECK_EQUAL( chain.IsSharedPt( 0 ), true );
806 BOOST_CHECK_EQUAL( chain.IsArcEnd( 0 ), true );
807 BOOST_CHECK_EQUAL( chain.IsArcStart( 0 ), true );
808
809 // Index 6 is the shared point between the two arcs in the middle of the chain
810 BOOST_CHECK_EQUAL( chain.IsSharedPt( 6 ), true );
811 BOOST_CHECK_EQUAL( chain.IsArcEnd( 6 ), true );
812 BOOST_CHECK_EQUAL( chain.IsArcStart( 6 ), true );
813
814 // End index is in the middle of an arc, so not an end point or shared point
815 endIndex = chain.PointCount() - 1;
816 BOOST_CHECK_EQUAL( chain.IsSharedPt( endIndex ), false );
817 BOOST_CHECK_EQUAL( chain.IsArcEnd( endIndex ), false );
818 BOOST_CHECK_EQUAL( chain.IsArcStart( endIndex ), false );
819}
820
821// Test SHAPE_LINE_CHAIN::Split()
823{
824 SEG seg1( VECTOR2I( 0, 100000 ), VECTOR2I( 50000, 0 ) );
825 SEG seg2( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
826 SHAPE_ARC arc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), ANGLE_180 );
827
828 // Start a chain with 2 points (seg1)
829 SHAPE_LINE_CHAIN chain( { seg1.A, seg1.B } );
830 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
831 // Add first arc
832 chain.Append( arc, ARC_HIGH_DEF );
833 BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
834 // Add two points (seg2)
835 chain.Append( seg2.A );
836 chain.Append( seg2.B );
837 BOOST_CHECK_EQUAL( chain.PointCount(), 11 );
838 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
839
840 BOOST_TEST_CONTEXT( "Case 1: Point not in the chain" )
841 {
842 SHAPE_LINE_CHAIN chainCopy = chain;
843 BOOST_CHECK_EQUAL( chainCopy.Split( VECTOR2I( 400000, 0 ) ), -1 );
844 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
845 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
846 }
847
848 BOOST_TEST_CONTEXT( "Case 2: Point close to start of a segment" )
849 {
850 SHAPE_LINE_CHAIN chainCopy = chain;
851 VECTOR2I splitPoint = seg1.A + VECTOR2I( 5, -10 );
852 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 1 );
853 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
854 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 1 ), splitPoint );
855 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() + 1 ); // new point added
856 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
857 }
858
859 BOOST_TEST_CONTEXT( "Case 3: Point exactly on the segment" )
860 {
861 SHAPE_LINE_CHAIN chainCopy = chain;
862 VECTOR2I splitPoint = seg1.B;
863 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 1 );
864 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
865 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 1 ), splitPoint );
866 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
867 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
868 }
869
870 BOOST_TEST_CONTEXT( "Case 4: Point at start of arc" )
871 {
872 SHAPE_LINE_CHAIN chainCopy = chain;
873 VECTOR2I splitPoint = arc.GetP0();
874 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 2 );
875 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
876 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 2 ), splitPoint );
877 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
878 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
879 }
880
881 BOOST_TEST_CONTEXT( "Case 5: Point close to start of arc" )
882 {
883 SHAPE_LINE_CHAIN chainCopy = chain;
884 VECTOR2I splitPoint = arc.GetP0() + VECTOR2I( -10, 130 );
885 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 3 );
886 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
887 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 3 ), splitPoint );
888 BOOST_CHECK_EQUAL( chainCopy.IsSharedPt( 3 ), true ); // must be a shared point
889 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() + 1 ); // new point added
890 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() + 1 ); // new arc should have been created
891 }
892}
893
894
895// Test SHAPE_LINE_CHAIN::Slice()
897{
898 SEG targetSegment( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
899 SHAPE_ARC firstArc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), ANGLE_180 );
900 SHAPE_ARC secondArc( VECTOR2I( -200000, -200000 ), VECTOR2I( -300000, -100000 ), -ANGLE_180 );
901 int tol = SHAPE_ARC::DefaultAccuracyForPCB(); // Tolerance for arc collisions
902
903 // Start a chain with 3 points
904 SHAPE_LINE_CHAIN chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( 100000, 0 ) } );
905 BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
906 // Add first arc
907 chain.Append( firstArc, ARC_HIGH_DEF );
908 BOOST_CHECK_EQUAL( chain.PointCount(), 10 );
909 // Add two points (target segment)
910 chain.Append( targetSegment.A );
911 chain.Append( targetSegment.B );
912 BOOST_CHECK_EQUAL( chain.PointCount(), 12 );
913 // Add a second arc
914 chain.Append( secondArc, ARC_HIGH_DEF );
915 BOOST_CHECK_EQUAL( chain.PointCount(), 20 );
916 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
917
921 BOOST_TEST_CONTEXT( "Case 1: Start at arc endpoint, finish middle of arc" )
922 {
923 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 9, 18, ARC_HIGH_DEF );
924 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
925
926 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
927 SHAPE_ARC expectedSliceArc0;
928 expectedSliceArc0.ConstructFromStartEndCenter( secondArc.GetP0(), chain.GetPoint( 18 ),
929 secondArc.GetCenter(),
930 secondArc.IsClockwise() );
931
932 BOOST_CHECK_EQUAL( sliceResult.Arc( 0 ).GetP0(), expectedSliceArc0.GetP0() ); // equal arc start points
933 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetArcMid(), tol ) );
934 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetP1(), tol ) );
935
936 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 10 );
937 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), firstArc.GetP1() ); // equal to arc end
938 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 1 ), targetSegment.A );
939 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 2 ), targetSegment.B );
940 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 3 ), expectedSliceArc0.GetP0() ); // equal to arc start
941 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 3 ), true );
942
943 for( int i = 4; i <= 8; i++ )
944 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
945
946 for( int i = 3; i <= 7; i++ )
947 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
948
949 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 9 ), true );
950 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 9 ), expectedSliceArc0.GetP1() ); // equal to arc end
951 }
952
956 BOOST_TEST_CONTEXT( "Case 2: Start at middle of an arc, finish at arc startpoint" )
957 {
958 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 5, 12, ARC_HIGH_DEF );
959 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
960
961 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
962 SHAPE_ARC expectedSliceArc0;
963 expectedSliceArc0.ConstructFromStartEndCenter( chain.GetPoint( 5 ), firstArc.GetP1(),
964 firstArc.GetCenter(),
965 firstArc.IsClockwise() );
966
967 BOOST_CHECK_EQUAL( sliceResult.Arc( 0 ).GetP1(),
968 expectedSliceArc0.GetP1() ); // equal arc end points
969 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetArcMid(), tol ) );
970 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetP0(), tol ) );
971
972 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 8 );
973 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ),
974 expectedSliceArc0.GetP0() ); // equal to arc start
975 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
976
977 for( int i = 1; i <= 4; i++ )
978 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
979
980 for( int i = 0; i <= 3; i++ )
981 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
982
983 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 4 ), true );
984 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 4 ),
985 expectedSliceArc0.GetP1() ); // equal to arc end
986
987 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 5 ), targetSegment.A );
988 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), targetSegment.B );
989 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 7 ), secondArc.GetP0() );
990 }
991
995 BOOST_TEST_CONTEXT( "Case 3: Full arc, nothing else" )
996 {
997 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 3, 9, ARC_HIGH_DEF );
998 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
999
1000 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
1001 SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
1002
1003 // Equal arc to original inserted arc
1004 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
1005 BOOST_CHECK_EQUAL( firstArc.GetArcMid(), sliceArc0.GetArcMid() );
1006 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
1007
1008 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 7 );
1009 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), sliceArc0.GetP0() ); // equal to arc start
1010 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
1011
1012 for( int i = 1; i <= 6; i++ )
1013 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
1014
1015 for( int i = 0; i <= 5; i++ )
1016 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
1017
1018 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 6 ), true );
1019 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), sliceArc0.GetP1() ); // equal to arc end
1020 }
1021
1025 BOOST_TEST_CONTEXT( "Case 4: Full arc, and straight segments to next arc start" )
1026 {
1027 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 3, 12, ARC_HIGH_DEF );
1028 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
1029
1030 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
1031 SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
1032
1033 // Equal arc to original inserted arc
1034 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
1035 BOOST_CHECK_EQUAL( firstArc.GetArcMid(), sliceArc0.GetArcMid() );
1036 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
1037
1038 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 10 );
1039 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), sliceArc0.GetP0() ); // equal to arc start
1040 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
1041
1042 for( int i = 1; i <= 6; i++ )
1043 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
1044
1045 for( int i = 0; i <= 5; i++ )
1046 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
1047
1048 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 6 ), true );
1049 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), sliceArc0.GetP1() ); // equal to arc end
1050
1051 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 7 ), targetSegment.A );
1052 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 8 ), targetSegment.B );
1053 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 9 ), secondArc.GetP0() );
1054 }
1055
1056 BOOST_TEST_CONTEXT( "Case 5: Chain ends in arc and point" )
1057 {
1058 SHAPE_LINE_CHAIN chainCopy = chain;
1059 chainCopy.Append( VECTOR2I( 400000, 400000 ) );
1060
1061 SHAPE_LINE_CHAIN sliceResult = chainCopy.Slice( 11, -1, ARC_HIGH_DEF );
1062 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
1063 BOOST_CHECK_EQUAL( sliceResult.GetPoint( -1 ), VECTOR2I( 400000, 400000 ) );
1064 }
1065
1066 BOOST_TEST_CONTEXT( "Case 6: Start to end, chain with one point" )
1067 {
1068 SHAPE_LINE_CHAIN chainCopy = SLC_CASES().OnePoint;
1069
1070 SHAPE_LINE_CHAIN sliceResult = chainCopy.Slice( 0, -1, ARC_HIGH_DEF );
1071 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 1 );
1072 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), VECTOR2I( 233450000, 228360000 ) );
1073 BOOST_CHECK_EQUAL( sliceResult.GetPoint( -1 ), VECTOR2I( 233450000, 228360000 ) ); // Same as index 0
1074 }
1075
1076 BOOST_TEST_CONTEXT( "Case 7: Start to end, chain with two points" )
1077 {
1078 SHAPE_LINE_CHAIN chainCopy = SLC_CASES().TwoPoints;
1079
1080 SHAPE_LINE_CHAIN sliceResult = chainCopy.Slice( 0, -1, ARC_HIGH_DEF );
1081 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 2 );
1082 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), VECTOR2I( 233450000, 228360000 ) );
1083 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 1 ), VECTOR2I( 263450000, 258360000 ) );
1084 BOOST_CHECK_EQUAL( sliceResult.GetPoint( -1 ), VECTOR2I( 263450000, 258360000 ) ); // Same as index 1
1085 }
1086
1087 BOOST_TEST_CONTEXT( "Case 8: Full 2nd arc, nothing else" )
1088 {
1089 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 12, 19, ARC_HIGH_DEF );
1090 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
1091
1092 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
1093 SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
1094
1095 // Equal arc to original inserted arc
1096 BOOST_CHECK_EQUAL( secondArc.GetP1(), sliceArc0.GetP1() );
1097 BOOST_CHECK_EQUAL( secondArc.GetArcMid(), sliceArc0.GetArcMid() );
1098 BOOST_CHECK_EQUAL( secondArc.GetP1(), sliceArc0.GetP1() );
1099
1100 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 8 );
1101 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), sliceArc0.GetP0() ); // equal to arc start
1102 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
1103
1104 for( int i = 1; i <= 7; i++ )
1105 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
1106
1107 for( int i = 0; i <= 6; i++ )
1108 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
1109
1110 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 7 ), true );
1111 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 7 ), sliceArc0.GetP1() ); // equal to arc end
1112 }
1113
1114 BOOST_TEST_CONTEXT( "Case 9: Start at middle of a 2nd arc, finish at end" )
1115 {
1116 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 16, 19, ARC_HIGH_DEF );
1117 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
1118
1119 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
1120
1121 SHAPE_ARC expectedSliceArc0;
1122 expectedSliceArc0.ConstructFromStartEndCenter( chain.GetPoint( 16 ), secondArc.GetP1(),
1123 secondArc.GetCenter(),
1124 secondArc.IsClockwise() );
1125
1126 BOOST_CHECK_EQUAL( sliceResult.Arc( 0 ).GetP1(),
1127 expectedSliceArc0.GetP1() ); // equal arc end points
1128 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetArcMid(), tol ) );
1129 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetP0(), tol ) );
1130
1131 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 4 );
1132 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ),
1133 expectedSliceArc0.GetP0() ); // equal to arc start
1134 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
1135
1136 for( int i = 1; i <= 3; i++ )
1137 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
1138
1139 for( int i = 0; i <= 2; i++ )
1140 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
1141
1142 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 3 ), true );
1143 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 3 ),
1144 expectedSliceArc0.GetP1() ); // equal to arc end
1145 }
1146
1147 BOOST_TEST_CONTEXT( "Case 10: New chain, start at arc middle, finish at end" )
1148 {
1149 SHAPE_LINE_CHAIN chain10;
1150 chain10.Append( firstArc, ARC_HIGH_DEF );
1151
1152 SHAPE_LINE_CHAIN sliceResult = chain10.Slice( 3, 6, ARC_HIGH_DEF );
1153 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
1154
1155 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
1156
1157 SHAPE_ARC expectedSliceArc0;
1158 expectedSliceArc0.ConstructFromStartEndCenter( chain10.GetPoint( 3 ), firstArc.GetP1(),
1159 firstArc.GetCenter(),
1160 firstArc.IsClockwise() );
1161
1162 BOOST_CHECK_EQUAL( sliceResult.Arc( 0 ).GetP1(),
1163 expectedSliceArc0.GetP1() ); // equal arc end points
1164 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetArcMid(), tol ) );
1165 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetP0(), tol ) );
1166
1167 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 4 );
1168 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ),
1169 expectedSliceArc0.GetP0() ); // equal to arc start
1170 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
1171
1172 for( int i = 1; i <= 3; i++ )
1173 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
1174
1175 for( int i = 0; i <= 2; i++ )
1176 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
1177
1178 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 3 ), true );
1179 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 3 ),
1180 expectedSliceArc0.GetP1() ); // equal to arc end
1181 }
1182}
1183
1184
1185// Test SHAPE_LINE_CHAIN::NearestPoint( VECTOR2I )
1186BOOST_AUTO_TEST_CASE( NearestPointPt )
1187{
1188 SEG seg1( VECTOR2I( 0, 100000 ), VECTOR2I( 50000, 0 ) );
1189 SEG seg2( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
1190 SHAPE_ARC arc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), ANGLE_180 );
1191
1192 // Start a chain with 2 points (seg1)
1193 SHAPE_LINE_CHAIN chain( { seg1.A, seg1.B } );
1194 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
1195 // Add first arc
1196 chain.Append( arc, ARC_HIGH_DEF );
1197 BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
1198 // Add two points (seg2)
1199 chain.Append( seg2.A );
1200 chain.Append( seg2.B );
1201 BOOST_CHECK_EQUAL( chain.PointCount(), 11 );
1202 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
1203
1204 VECTOR2I ptOnArcCloseToStart( 297553, 31697 ); //should be index 3 in chain
1205 VECTOR2I ptOnArcCloseToEnd( 139709, 82983 ); //should be index 6 in chain
1206
1207 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToStart, true ), ptOnArcCloseToStart );
1208 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToStart, false ), arc.GetP0() );
1209
1210 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToEnd, true ), ptOnArcCloseToEnd );
1211 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToEnd, false ), arc.GetP1() );
1212}
1213
1214
1215// Test SHAPE_LINE_CHAIN::Replace( SHAPE_LINE_CHAIN )
1217{
1218 BOOST_TEST_INFO( "8949 crash" );
1219
1220 std::vector<VECTOR2I> linePts = {
1221 { 206000000, 140110000 }, { 192325020, 140110000 }, { 192325020, 113348216 },
1222 { 192251784, 113274980 }, { 175548216, 113274980 }, { 175474980, 113348216 },
1223 { 175474980, 136694980 }, { 160774511, 121994511 }, { 160774511, 121693501 },
1224 { 160086499, 121005489 }, { 159785489, 121005489 }, { 159594511, 120814511 },
1225 { 160086499, 120814511 }, { 160774511, 120126499 }, { 160774511, 119153501 },
1226 { 160086499, 118465489 }, { 159113501, 118465489 }, { 158425489, 119153501 },
1227 { 158425489, 119645489 }, { 157325020, 118545020 }, { 157325020, 101925020 },
1228 { 208674980, 101925020 }, { 208674980, 145474980 }, { 192325020, 145474980 },
1229 { 192325020, 140110000 }
1230 };
1231
1232 SHAPE_LINE_CHAIN baseChain( linePts, false );
1233 baseChain.SetWidth( 250000 );
1234 BOOST_CHECK_EQUAL( baseChain.PointCount(), linePts.size() );
1235
1236 SHAPE_LINE_CHAIN replaceChain( { VECTOR2I( 192325020, 140110000 ) }, false );
1237 BOOST_CHECK_EQUAL( replaceChain.PointCount(), 1 );
1238
1239 baseChain.Replace( 1, 23, replaceChain );
1240
1241 BOOST_CHECK_EQUAL( baseChain.PointCount(), linePts.size() - ( 23 - 1 ) );
1242
1243 // Replacing the last point in a chain is special-cased
1244 baseChain.Replace( baseChain.PointCount() - 1, baseChain.PointCount() - 1, VECTOR2I( -1, -1 ) );
1245
1246 BOOST_CHECK_EQUAL( baseChain.CLastPoint(), VECTOR2I( -1, -1 ) );
1247}
1248
1249
1250BOOST_AUTO_TEST_CASE( CompareGeometry )
1251{
1252 SHAPE_LINE_CHAIN chain1;
1253 chain1.Append( 0, 0 );
1254 chain1.Append( 100, 0 );
1255 chain1.Append( 100, 100 );
1256 chain1.Append( 0, 100 );
1257 chain1.SetClosed( true );
1258
1259 SHAPE_LINE_CHAIN chain2 = chain1;
1260
1261 // 1. Identical chains
1262 BOOST_CHECK( chain1.CompareGeometry( chain2 ) );
1263
1264 // 2. Different chains
1265 chain2.SetPoint( 2, VECTOR2I( 101, 101 ) );
1266 BOOST_CHECK( !chain1.CompareGeometry( chain2 ) );
1267
1268 // 3. Epsilon tolerance
1269 BOOST_CHECK( chain1.CompareGeometry( chain2, false, 2 ) );
1270
1271 // 4. Cyclical compare (chain1 but points in started at different vertex)
1272 SHAPE_LINE_CHAIN chain3;
1273 chain3.Append( 100, 0 );
1274 chain3.Append( 100, 100 );
1275 chain3.Append( 0, 100 );
1276 chain3.Append( 0, 0 );
1277 chain3.SetClosed( true );
1278
1279 BOOST_CHECK( !chain1.CompareGeometry( chain3, false ) );
1280 BOOST_CHECK( chain1.CompareGeometry( chain3, true ) );
1281
1282 // 5. Different number of points
1283 chain3.Append( 50, 50 ); // Add a point
1284 BOOST_CHECK( !chain1.CompareGeometry( chain3, true ) );
1285
1286 // 6. Simplify check (chain1 should match chain4 because CompareGeometry calls Simplify())
1287 SHAPE_LINE_CHAIN chain4;
1288 chain4.Append( 0, 0 );
1289 chain4.Append( 50, 0 ); // Collinear point
1290 chain4.Append( 100, 0 );
1291 chain4.Append( 100, 100 );
1292 chain4.Append( 0, 100 );
1293 chain4.SetClosed( true );
1294
1295 BOOST_CHECK( chain1.CompareGeometry( chain4 ) );
1296}
1297
1298
1299BOOST_AUTO_TEST_CASE( CompareGeometryReversed )
1300{
1301 // Square
1302 const std::vector<VECTOR2I> ptsA = {
1303 { 0, 0 },
1304 { 100, 0 },
1305 { 100, 100 },
1306 { 0, 100 },
1307 };
1308 // Same points, same start, reversed
1309 const std::vector<VECTOR2I> ptsB = {
1310 { 0, 0 },
1311 { 0, 100 },
1312 { 100, 100 },
1313 { 100, 0 },
1314 };
1315
1316 SHAPE_LINE_CHAIN chainA( ptsA, true );
1317 SHAPE_LINE_CHAIN chainB( ptsB, true );
1318
1319 BOOST_TEST( !chainA.CompareGeometry( chainB, false ) );
1320 BOOST_TEST( chainA.CompareGeometry( chainB, true ) );
1321}
1322
1323
1329BOOST_AUTO_TEST_CASE( SimplifyWithToleranceIssue22597 )
1330{
1332
1333 // All 164 points from the reproduction case in issue #22597.
1334 // Coordinates are in nanometers (KiCad internal units).
1335 chain.Append( VECTOR2I( 135095398, 233618441 ) );
1336 chain.Append( VECTOR2I( 135024554, 233546880 ) );
1337 chain.Append( VECTOR2I( 134887999, 233398857 ) );
1338 chain.Append( VECTOR2I( 134757514, 233245455 ) );
1339 chain.Append( VECTOR2I( 134633313, 233086923 ) );
1340 chain.Append( VECTOR2I( 134515595, 232923519 ) );
1341 chain.Append( VECTOR2I( 134404553, 232755507 ) );
1342 chain.Append( VECTOR2I( 134300366, 232583161 ) );
1343 chain.Append( VECTOR2I( 134203203, 232406759 ) );
1344 chain.Append( VECTOR2I( 134113222, 232226587 ) );
1345 chain.Append( VECTOR2I( 134030567, 232042939 ) );
1346 chain.Append( VECTOR2I( 133955377, 231856111 ) );
1347 chain.Append( VECTOR2I( 133887768, 231666408 ) );
1348 chain.Append( VECTOR2I( 133827854, 231474135 ) );
1349 chain.Append( VECTOR2I( 133775731, 231279607 ) );
1350 chain.Append( VECTOR2I( 133731482, 231083137 ) );
1351 chain.Append( VECTOR2I( 133695180, 230885045 ) );
1352 chain.Append( VECTOR2I( 133666884, 230685652 ) );
1353 chain.Append( VECTOR2I( 133646639, 230485281 ) );
1354 chain.Append( VECTOR2I( 133634480, 230284257 ) );
1355 chain.Append( VECTOR2I( 133630425, 230082907 ) );
1356 chain.Append( VECTOR2I( 133634480, 229881557 ) );
1357 chain.Append( VECTOR2I( 133646639, 229680533 ) );
1358 chain.Append( VECTOR2I( 133666884, 229480162 ) );
1359 chain.Append( VECTOR2I( 133695180, 229280769 ) );
1360 chain.Append( VECTOR2I( 133731482, 229082677 ) );
1361 chain.Append( VECTOR2I( 133775731, 228886207 ) );
1362 chain.Append( VECTOR2I( 133827854, 228691679 ) );
1363 chain.Append( VECTOR2I( 133887768, 228499406 ) );
1364 chain.Append( VECTOR2I( 133955377, 228309703 ) );
1365 chain.Append( VECTOR2I( 134030567, 228122875 ) );
1366 chain.Append( VECTOR2I( 134113222, 227939227 ) );
1367 chain.Append( VECTOR2I( 134203203, 227759055 ) );
1368 chain.Append( VECTOR2I( 134300366, 227582653 ) );
1369 chain.Append( VECTOR2I( 134404553, 227410307 ) );
1370 chain.Append( VECTOR2I( 134515595, 227242295 ) );
1371 chain.Append( VECTOR2I( 134633313, 227078891 ) );
1372 chain.Append( VECTOR2I( 134757514, 226920359 ) );
1373 chain.Append( VECTOR2I( 134887999, 226766957 ) );
1374 chain.Append( VECTOR2I( 135024554, 226618934 ) );
1375 chain.Append( VECTOR2I( 135095398, 226547373 ) );
1376 chain.Append( VECTOR2I( 148530427, 213112344 ) );
1377 chain.Append( VECTOR2I( 148601988, 213041500 ) );
1378 chain.Append( VECTOR2I( 148750011, 212904945 ) );
1379 chain.Append( VECTOR2I( 148903413, 212774460 ) );
1380 chain.Append( VECTOR2I( 149061945, 212650259 ) );
1381 chain.Append( VECTOR2I( 149225349, 212532541 ) );
1382 chain.Append( VECTOR2I( 149393361, 212421499 ) );
1383 chain.Append( VECTOR2I( 149565707, 212317312 ) );
1384 chain.Append( VECTOR2I( 149742109, 212220149 ) );
1385 chain.Append( VECTOR2I( 149922281, 212130168 ) );
1386 chain.Append( VECTOR2I( 150105929, 212047514 ) );
1387 chain.Append( VECTOR2I( 150292757, 211972323 ) );
1388 chain.Append( VECTOR2I( 150482460, 211904715 ) );
1389 chain.Append( VECTOR2I( 150674733, 211844800 ) );
1390 chain.Append( VECTOR2I( 150869261, 211792677 ) );
1391 chain.Append( VECTOR2I( 151065731, 211748428 ) );
1392 chain.Append( VECTOR2I( 151263823, 211712126 ) );
1393 chain.Append( VECTOR2I( 151463216, 211683830 ) );
1394 chain.Append( VECTOR2I( 151710655, 211863478 ) ); // Suspicious point
1395 chain.Append( VECTOR2I( 151864611, 211651426 ) );
1396 chain.Append( VECTOR2I( 152065961, 211647371 ) );
1397 chain.Append( VECTOR2I( 152267311, 211651426 ) );
1398 chain.Append( VECTOR2I( 152468335, 211663586 ) );
1399 chain.Append( VECTOR2I( 152668706, 211683830 ) );
1400 chain.Append( VECTOR2I( 152868099, 211712126 ) );
1401 chain.Append( VECTOR2I( 153066191, 211748428 ) );
1402 chain.Append( VECTOR2I( 153262661, 211792677 ) );
1403 chain.Append( VECTOR2I( 153457189, 211844800 ) );
1404 chain.Append( VECTOR2I( 153649462, 211904715 ) );
1405 chain.Append( VECTOR2I( 153839165, 211972323 ) );
1406 chain.Append( VECTOR2I( 154025993, 212047514 ) );
1407 chain.Append( VECTOR2I( 154209641, 212130168 ) );
1408 chain.Append( VECTOR2I( 154389813, 212220149 ) );
1409 chain.Append( VECTOR2I( 154566215, 212317312 ) );
1410 chain.Append( VECTOR2I( 154738561, 212421499 ) );
1411 chain.Append( VECTOR2I( 154906573, 212532541 ) );
1412 chain.Append( VECTOR2I( 155069977, 212650259 ) );
1413 chain.Append( VECTOR2I( 155228509, 212774460 ) );
1414 chain.Append( VECTOR2I( 155381911, 212904945 ) );
1415 chain.Append( VECTOR2I( 155529934, 213041500 ) );
1416 chain.Append( VECTOR2I( 155601495, 213112344 ) );
1417 chain.Append( VECTOR2I( 160551242, 218062092 ) );
1418 chain.Append( VECTOR2I( 160622086, 218133653 ) );
1419 chain.Append( VECTOR2I( 160758641, 218281676 ) );
1420 chain.Append( VECTOR2I( 160889126, 218435078 ) );
1421 chain.Append( VECTOR2I( 161013327, 218593610 ) );
1422 chain.Append( VECTOR2I( 161131045, 218757014 ) );
1423 chain.Append( VECTOR2I( 161242087, 218925026 ) );
1424 chain.Append( VECTOR2I( 161346274, 219097372 ) );
1425 chain.Append( VECTOR2I( 161443437, 219273774 ) );
1426 chain.Append( VECTOR2I( 161533418, 219453946 ) );
1427 chain.Append( VECTOR2I( 161616072, 219637594 ) );
1428 chain.Append( VECTOR2I( 161691263, 219824422 ) );
1429 chain.Append( VECTOR2I( 161758871, 220014125 ) );
1430 chain.Append( VECTOR2I( 161818786, 220206398 ) );
1431 chain.Append( VECTOR2I( 161870909, 220400926 ) );
1432 chain.Append( VECTOR2I( 161915158, 220597396 ) );
1433 chain.Append( VECTOR2I( 161951460, 220795488 ) );
1434 chain.Append( VECTOR2I( 161979756, 220994881 ) );
1435 chain.Append( VECTOR2I( 162000000, 221195252 ) );
1436 chain.Append( VECTOR2I( 162012160, 221396276 ) );
1437 chain.Append( VECTOR2I( 162016215, 221597626 ) );
1438 chain.Append( VECTOR2I( 162012160, 221798976 ) );
1439 chain.Append( VECTOR2I( 162000000, 222000000 ) );
1440 chain.Append( VECTOR2I( 161979756, 222200371 ) );
1441 chain.Append( VECTOR2I( 161951460, 222399764 ) );
1442 chain.Append( VECTOR2I( 161915158, 222597856 ) );
1443 chain.Append( VECTOR2I( 161870909, 222794326 ) );
1444 chain.Append( VECTOR2I( 161818786, 222988854 ) );
1445 chain.Append( VECTOR2I( 161758871, 223181127 ) );
1446 chain.Append( VECTOR2I( 161691263, 223370830 ) );
1447 chain.Append( VECTOR2I( 161616072, 223557658 ) );
1448 chain.Append( VECTOR2I( 161533418, 223741306 ) );
1449 chain.Append( VECTOR2I( 161443437, 223921478 ) );
1450 chain.Append( VECTOR2I( 161346274, 224097880 ) );
1451 chain.Append( VECTOR2I( 161242087, 224270226 ) );
1452 chain.Append( VECTOR2I( 161131045, 224438238 ) );
1453 chain.Append( VECTOR2I( 161013327, 224601642 ) );
1454 chain.Append( VECTOR2I( 160889126, 224760174 ) );
1455 chain.Append( VECTOR2I( 160758641, 224913576 ) );
1456 chain.Append( VECTOR2I( 160622086, 225061599 ) );
1457 chain.Append( VECTOR2I( 160551242, 225133160 ) );
1458 chain.Append( VECTOR2I( 147116213, 238568188 ) );
1459 chain.Append( VECTOR2I( 147044657, 238639037 ) );
1460 chain.Append( VECTOR2I( 146896633, 238775592 ) );
1461 chain.Append( VECTOR2I( 146743231, 238906077 ) );
1462 chain.Append( VECTOR2I( 146584699, 239030279 ) );
1463 chain.Append( VECTOR2I( 146421295, 239147996 ) );
1464 chain.Append( VECTOR2I( 146253283, 239259039 ) );
1465 chain.Append( VECTOR2I( 146080936, 239363226 ) );
1466 chain.Append( VECTOR2I( 145904534, 239460389 ) );
1467 chain.Append( VECTOR2I( 145724362, 239550371 ) );
1468 chain.Append( VECTOR2I( 145540714, 239633024 ) );
1469 chain.Append( VECTOR2I( 145353886, 239708216 ) );
1470 chain.Append( VECTOR2I( 145164182, 239775824 ) );
1471 chain.Append( VECTOR2I( 144971909, 239835739 ) );
1472 chain.Append( VECTOR2I( 144777380, 239887863 ) );
1473 chain.Append( VECTOR2I( 144580910, 239932111 ) );
1474 chain.Append( VECTOR2I( 144382818, 239968413 ) );
1475 chain.Append( VECTOR2I( 144183424, 239996709 ) );
1476 chain.Append( VECTOR2I( 143983053, 240016953 ) );
1477 chain.Append( VECTOR2I( 143782029, 240029113 ) );
1478 chain.Append( VECTOR2I( 143580679, 240033169 ) );
1479 chain.Append( VECTOR2I( 143379329, 240029113 ) );
1480 chain.Append( VECTOR2I( 143178305, 240016953 ) );
1481 chain.Append( VECTOR2I( 142977934, 239996709 ) );
1482 chain.Append( VECTOR2I( 142778540, 239968413 ) );
1483 chain.Append( VECTOR2I( 142580448, 239932111 ) );
1484 chain.Append( VECTOR2I( 142383978, 239887863 ) );
1485 chain.Append( VECTOR2I( 142189449, 239835739 ) );
1486 chain.Append( VECTOR2I( 141997176, 239775824 ) );
1487 chain.Append( VECTOR2I( 141807472, 239708216 ) );
1488 chain.Append( VECTOR2I( 141620644, 239633024 ) );
1489 chain.Append( VECTOR2I( 141436996, 239550371 ) );
1490 chain.Append( VECTOR2I( 141256824, 239460389 ) );
1491 chain.Append( VECTOR2I( 141080422, 239363226 ) );
1492 chain.Append( VECTOR2I( 140908075, 239259039 ) );
1493 chain.Append( VECTOR2I( 140740063, 239147996 ) );
1494 chain.Append( VECTOR2I( 140576659, 239030279 ) );
1495 chain.Append( VECTOR2I( 140418127, 238906077 ) );
1496 chain.Append( VECTOR2I( 140264725, 238775592 ) );
1497 chain.Append( VECTOR2I( 140116701, 238639037 ) );
1498 chain.Append( VECTOR2I( 140045145, 238568188 ) );
1499
1500 chain.SetClosed( true );
1501
1502 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
1503 int originalPointCount = chain.PointCount();
1504 BOOST_CHECK_EQUAL( originalPointCount, 164 );
1505
1506 // With 2mm tolerance (2000000 nm), the many small segments approximating arcs
1507 // should be simplified significantly. A properly working algorithm should
1508 // reduce the point count substantially.
1509 chain.Simplify( 2000000 );
1510
1511 int simplifiedCount = chain.PointCount();
1512 BOOST_TEST_MESSAGE( "Simplified point count: " << simplifiedCount );
1513
1514 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
1515 BOOST_CHECK_LT( simplifiedCount, originalPointCount );
1516
1517 // The polygon is a rotated rounded rectangle with 4 corners.
1518 // A 2mm tolerance should reduce it to approximately 4-8 points.
1519 // If it's only slightly reduced, there may be an issue.
1520 BOOST_CHECK_LE( simplifiedCount, 20 );
1521}
1522
1523
1524BOOST_AUTO_TEST_CASE( SimplifyWithArcs )
1525{
1526 BOOST_TEST_CONTEXT( "1 segment, arc, 2 collinear segments" )
1527 {
1528 SHAPE_LINE_CHAIN original;
1529 original.Append( VECTOR2I( 0, 0 ) );
1530
1531 original.Append( SHAPE_ARC( VECTOR2I( 2000000, 0 ), VECTOR2I( 2500000, 500000 ), VECTOR2I( 3000000, 0 ), 0 ),
1532 ARC_HIGH_DEF );
1533
1534 original.Append( VECTOR2I( 4000000, 0 ) );
1535 original.Append( VECTOR2I( 5000000, 0 ) );
1536
1537 BOOST_CHECK( GEOM_TEST::IsOutlineValid( original ) );
1538 int beforePoints = original.PointCount();
1539
1540 SHAPE_LINE_CHAIN simplified = original;
1541 simplified.Simplify();
1542
1543 BOOST_CHECK_EQUAL( static_cast<int>( simplified.ArcCount() ), static_cast<int>( original.ArcCount() ) );
1544 BOOST_CHECK_LT( simplified.PointCount(), beforePoints );
1545 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP0(), VECTOR2I( 2000000, 0 ) );
1546 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP1(), VECTOR2I( 3000000, 0 ) );
1547
1548 BOOST_CHECK_LT( simplified.Find( VECTOR2I( 4000000, 0 ) ), 0 );
1549 BOOST_CHECK_GE( simplified.Find( VECTOR2I( 3000000, 0 ) ), 0 );
1550 }
1551
1552 BOOST_TEST_CONTEXT( "Arc, two collinear segments" )
1553 {
1554 SHAPE_LINE_CHAIN original;
1555 original.Append( SHAPE_ARC( VECTOR2I( 0, 0 ), VECTOR2I( 1000000, 500000 ), VECTOR2I( 2000000, 0 ), 0 ),
1556 ARC_HIGH_DEF );
1557
1558 original.Append( VECTOR2I( 3000000, 0 ) );
1559 original.Append( VECTOR2I( 4000000, 0 ) );
1560
1561 BOOST_CHECK( GEOM_TEST::IsOutlineValid( original ) );
1562 int beforePoints = original.PointCount();
1563
1564 SHAPE_LINE_CHAIN simplified = original;
1565 simplified.Simplify();
1566
1567 BOOST_CHECK_EQUAL( static_cast<int>( simplified.ArcCount() ), 1 );
1568 BOOST_CHECK_LT( simplified.PointCount(), beforePoints );
1569 BOOST_CHECK( GEOM_TEST::IsOutlineValid( simplified ) );
1570
1571 BOOST_CHECK_LT( simplified.Find( VECTOR2I( 3000000, 0 ) ), 0 );
1572 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP0(), VECTOR2I( 0, 0 ) );
1573 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP1(), VECTOR2I( 2000000, 0 ) );
1574 }
1575
1576 BOOST_TEST_CONTEXT( "2 collinear segments, arc, 2 collinear segments" )
1577 {
1578 SHAPE_LINE_CHAIN original;
1579 original.Append( VECTOR2I( 0, 0 ) );
1580 original.Append( VECTOR2I( 1000000, 0 ) );
1581
1582 original.Append( SHAPE_ARC( VECTOR2I( 2000000, 0 ), VECTOR2I( 2500000, 500000 ), VECTOR2I( 3000000, 0 ), 0 ),
1583 ARC_HIGH_DEF );
1584
1585 original.Append( VECTOR2I( 4000000, 0 ) );
1586 original.Append( VECTOR2I( 5000000, 0 ) );
1587
1588 BOOST_CHECK( GEOM_TEST::IsOutlineValid( original ) );
1589 int beforePoints = original.PointCount();
1590 int beforeArcs = static_cast<int>( original.ArcCount() );
1591
1592 SHAPE_LINE_CHAIN simplified = original;
1593 simplified.Simplify();
1594
1595 BOOST_CHECK( static_cast<int>( simplified.ArcCount() ) == beforeArcs );
1596 BOOST_CHECK_LT( simplified.PointCount(), beforePoints );
1597 BOOST_CHECK( GEOM_TEST::IsOutlineValid( simplified ) );
1598
1599 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP0(), VECTOR2I( 2000000, 0 ) );
1600 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP1(), VECTOR2I( 3000000, 0 ) );
1601
1602 BOOST_CHECK_LT( simplified.Find( VECTOR2I( 4000000, 0 ) ), 0 );
1603 BOOST_CHECK_GE( simplified.Find( VECTOR2I( 5000000, 0 ) ), 0 );
1604 }
1605
1606 BOOST_TEST_CONTEXT( "2 collinear segments, arc" )
1607 {
1608 SHAPE_LINE_CHAIN original;
1609 original.Append( VECTOR2I( 0, 0 ) );
1610 original.Append( VECTOR2I( 1000000, 0 ) );
1611
1612 original.Append( SHAPE_ARC( VECTOR2I( 2000000, 0 ), VECTOR2I( 2500000, 500000 ), VECTOR2I( 3000000, 0 ), 0 ),
1613 ARC_HIGH_DEF );
1614
1615 BOOST_CHECK( GEOM_TEST::IsOutlineValid( original ) );
1616 int beforePoints = original.PointCount();
1617
1618 SHAPE_LINE_CHAIN simplified = original;
1619 simplified.Simplify();
1620
1621 BOOST_CHECK_EQUAL( static_cast<int>( simplified.ArcCount() ), 1 );
1622 BOOST_CHECK_LT( simplified.PointCount(), beforePoints );
1623 BOOST_CHECK_LT( simplified.Find( VECTOR2I( 1000000, 0 ) ), 0 );
1624 BOOST_CHECK( GEOM_TEST::IsOutlineValid( simplified ) );
1625
1626 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP0(), VECTOR2I( 2000000, 0 ) );
1627 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP1(), VECTOR2I( 3000000, 0 ) );
1628 }
1629
1630 BOOST_TEST_CONTEXT( "collinear segments before and after an arc (open chain)" )
1631 {
1632 SHAPE_LINE_CHAIN original;
1633 original.Append( VECTOR2I( 0, 0 ) );
1634 original.Append( VECTOR2I( 1000000, 0 ) );
1635
1636 original.Append( SHAPE_ARC( VECTOR2I( 2000000, 0 ), VECTOR2I( 2500000, 500000 ), VECTOR2I( 3000000, 0 ), 0 ),
1637 ARC_HIGH_DEF );
1638
1639 original.Append( VECTOR2I( 4000000, 0 ) );
1640 original.Append( VECTOR2I( 5000000, 0 ) );
1641
1642 BOOST_CHECK( GEOM_TEST::IsOutlineValid( original ) );
1643 int beforePoints = original.PointCount();
1644 int beforeArcs = static_cast<int>( original.ArcCount() );
1645
1646 SHAPE_LINE_CHAIN simplified = original;
1647 simplified.Simplify();
1648
1649 BOOST_CHECK( static_cast<int>( simplified.ArcCount() ) == beforeArcs );
1650
1651 BOOST_CHECK_LT( simplified.PointCount(), beforePoints );
1652 BOOST_CHECK( GEOM_TEST::IsOutlineValid( simplified ) );
1653
1654 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP0(), VECTOR2I( 2000000, 0 ) );
1655 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP1(), VECTOR2I( 3000000, 0 ) );
1656
1657 BOOST_CHECK_LT( simplified.Find( VECTOR2I( 4000000, 0 ) ), 0 );
1658 BOOST_CHECK_GE( simplified.Find( VECTOR2I( 5000000, 0 ) ), 0 );
1659 }
1660
1661 BOOST_TEST_CONTEXT( "arc at start, two collinear segments after arc" )
1662 {
1663 SHAPE_LINE_CHAIN original;
1664 original.Append( SHAPE_ARC( VECTOR2I( 0, 0 ), VECTOR2I( 1000000, 500000 ), VECTOR2I( 2000000, 0 ), 0 ),
1665 ARC_HIGH_DEF );
1666 original.Append( VECTOR2I( 3000000, 0 ) );
1667 original.Append( VECTOR2I( 4000000, 0 ) );
1668
1669 BOOST_CHECK( GEOM_TEST::IsOutlineValid( original ) );
1670 int beforePoints = original.PointCount();
1671 int beforeArcs = static_cast<int>( original.ArcCount() );
1672
1673 SHAPE_LINE_CHAIN simplified = original;
1674 simplified.Simplify();
1675
1676 BOOST_CHECK( static_cast<int>( simplified.ArcCount() ) == beforeArcs
1677 || static_cast<int>( simplified.ArcCount() ) == ( beforeArcs - 1 ) );
1678 BOOST_CHECK_LT( simplified.PointCount(), beforePoints );
1679 BOOST_CHECK( GEOM_TEST::IsOutlineValid( simplified ) );
1680 }
1681
1682 BOOST_TEST_CONTEXT( "tolerance semantics — zero vs small positive" )
1683 {
1684 SHAPE_LINE_CHAIN original;
1685
1686 original.Append( VECTOR2I( 0, 0 ) );
1687 // perturb the intermediate point by 1 unit so it's not exactly collinear
1688 original.Append( VECTOR2I( 1000000, 1 ) );
1689
1690 original.Append( SHAPE_ARC( VECTOR2I( 2000000, 0 ), VECTOR2I( 2500000, 500000 ), VECTOR2I( 3000000, 0 ), 0 ),
1691 ARC_HIGH_DEF );
1692
1693 original.Append( VECTOR2I( 4000000, 0 ) );
1694 original.Append( VECTOR2I( 5000000, 0 ) );
1695
1696 BOOST_CHECK( GEOM_TEST::IsOutlineValid( original ) );
1697 int beforePoints = original.PointCount();
1698
1699 SHAPE_LINE_CHAIN simplifiedZero = original;
1700 simplifiedZero.Simplify( 0 );
1701
1702 // Expect a single point removal at zero tolerance
1703 BOOST_CHECK_EQUAL( simplifiedZero.PointCount(), beforePoints - 1 );
1704
1705 SHAPE_LINE_CHAIN simplifiedOne = original;
1706 simplifiedOne.Simplify( 1 );
1707
1708 // Expect one more point to be removed with tolerance 1
1709 BOOST_CHECK_EQUAL( simplifiedOne.PointCount(), beforePoints - 2 );
1710 BOOST_CHECK_EQUAL( static_cast<int>( simplifiedOne.ArcCount() ), static_cast<int>( original.ArcCount() ) );
1711 }
1712
1713 BOOST_TEST_CONTEXT( "arc, arc (adjacent)" )
1714 {
1715 SHAPE_LINE_CHAIN original;
1716 original.Append( SHAPE_ARC( VECTOR2I( 0, 0 ), VECTOR2I( 1000000, 500000 ), VECTOR2I( 2000000, 0 ), 0 ),
1717 ARC_HIGH_DEF );
1718 original.Append( SHAPE_ARC( VECTOR2I( 2000000, 0 ), VECTOR2I( 3000000, 500000 ), VECTOR2I( 4000000, 0 ), 0 ),
1719 ARC_HIGH_DEF );
1720
1721 BOOST_CHECK( GEOM_TEST::IsOutlineValid( original ) );
1722 int beforeArcs = static_cast<int>( original.ArcCount() );
1723
1724 SHAPE_LINE_CHAIN simplified = original;
1725 simplified.Simplify();
1726
1727 BOOST_CHECK( static_cast<int>( simplified.ArcCount() ) == beforeArcs );
1728 BOOST_CHECK( GEOM_TEST::IsOutlineValid( simplified ) );
1729
1730 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP1(), simplified.Arc( 1 ).GetP0() );
1731 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP0(), VECTOR2I( 0, 0 ) );
1732 BOOST_CHECK_EQUAL( simplified.Arc( 1 ).GetP1(), VECTOR2I( 4000000, 0 ) );
1733 }
1734
1735 BOOST_TEST_CONTEXT( "2 collinear segments, arc, 2 collinear segments" )
1736 {
1737 SHAPE_LINE_CHAIN original;
1738 original.Append( VECTOR2I( -1000000, 0 ) );
1739
1740 original.Append( SHAPE_ARC( VECTOR2I( 0, 0 ), VECTOR2I( 500000, 500000 ), VECTOR2I( 1000000, 0 ), 0 ),
1741 ARC_HIGH_DEF );
1742
1743 original.Append( SHAPE_ARC( VECTOR2I( 1000000, 0 ), VECTOR2I( 1500000, -500000 ), VECTOR2I( 2000000, 0 ), 0 ),
1744 ARC_HIGH_DEF );
1745
1746 original.Append( VECTOR2I( 3000000, 0 ) );
1747 original.Append( VECTOR2I( 4000000, 0 ) );
1748
1749 BOOST_CHECK( GEOM_TEST::IsOutlineValid( original ) );
1750 int beforePoints = original.PointCount();
1751 int beforeArcs = static_cast<int>( original.ArcCount() );
1752
1753 SHAPE_LINE_CHAIN simplified = original;
1754 simplified.Simplify();
1755
1756 BOOST_CHECK( static_cast<int>( simplified.ArcCount() ) == beforeArcs );
1757 BOOST_CHECK_LT( simplified.PointCount(), beforePoints );
1758 BOOST_CHECK( GEOM_TEST::IsOutlineValid( simplified ) );
1759
1760 BOOST_CHECK_GE( simplified.Find( VECTOR2I( -1000000, 0 ) ), 0 );
1761 BOOST_CHECK_GE( simplified.Find( VECTOR2I( 4000000, 0 ) ), 0 );
1762 }
1763
1764 BOOST_TEST_CONTEXT( "arc, collinear point, arc" )
1765 {
1766 SHAPE_LINE_CHAIN original;
1767 original.Append( SHAPE_ARC( VECTOR2I( 0, 0 ), VECTOR2I( 1000000, 500000 ), VECTOR2I( 2000000, 0 ), 0 ),
1768 ARC_HIGH_DEF );
1769
1770 // collinear point
1771 original.Append( VECTOR2I( 2500000, 0 ) );
1772
1773 original.Append( SHAPE_ARC( VECTOR2I( 3000000, 0 ), VECTOR2I( 3500000, 500000 ), VECTOR2I( 4000000, 0 ), 0 ),
1774 ARC_HIGH_DEF );
1775
1776 BOOST_CHECK( GEOM_TEST::IsOutlineValid( original ) );
1777 int beforePoints = original.PointCount();
1778 int beforeArcs = static_cast<int>( original.ArcCount() );
1779
1780 SHAPE_LINE_CHAIN simplified = original;
1781 simplified.Simplify();
1782
1783 BOOST_CHECK( static_cast<int>( simplified.ArcCount() ) == beforeArcs );
1784 BOOST_CHECK_LT( simplified.PointCount(), beforePoints );
1785 BOOST_CHECK( GEOM_TEST::IsOutlineValid( simplified ) );
1786
1787 BOOST_CHECK_EQUAL( simplified.Arc( 0 ).GetP1(), VECTOR2I( 2000000, 0 ) );
1788 BOOST_CHECK_EQUAL( simplified.Arc( 1 ).GetP0(), VECTOR2I( 3000000, 0 ) );
1789 }
1790}
1791
1792
1793BOOST_AUTO_TEST_CASE( SelfIntersecting_NoIntersection_OpenChain )
1794{
1795 SHAPE_LINE_CHAIN chain( { { 0, 0 }, { 1000, 0 }, { 2000, 1000 }, { 3000, 0 } } );
1796
1797 BOOST_CHECK( !chain.SelfIntersecting() );
1798}
1799
1800
1801BOOST_AUTO_TEST_CASE( SelfIntersecting_NoIntersection_ClosedChain )
1802{
1803 SHAPE_LINE_CHAIN chain( { { 0, 0 }, { 10000, 0 }, { 10000, 10000 }, { 0, 10000 } } );
1804 chain.SetClosed( true );
1805
1806 BOOST_CHECK( !chain.SelfIntersecting() );
1807}
1808
1809
1810BOOST_AUTO_TEST_CASE( SelfIntersecting_CrossingSegments )
1811{
1812 // An X shape: two segments that cross
1813 // (0,0)-(10000,10000) and (10000,0)-(0,10000)
1814 SHAPE_LINE_CHAIN chain( { { 0, 0 }, { 10000, 10000 }, { 10000, 0 }, { 0, 10000 } } );
1815
1816 auto result = chain.SelfIntersecting();
1817 BOOST_REQUIRE( result.has_value() );
1818 BOOST_CHECK_EQUAL( result->index_our, 0 );
1819 BOOST_CHECK_EQUAL( result->index_their, 2 );
1820}
1821
1822
1823BOOST_AUTO_TEST_CASE( SelfIntersecting_ClosedFigureEight )
1824{
1825 // Closed bowtie: segments (0,0)-(10000,10000) and (10000,0)-(0,10000) cross at center
1826 SHAPE_LINE_CHAIN chain( { { 0, 0 }, { 10000, 10000 }, { 10000, 0 }, { 0, 10000 } } );
1827 chain.SetClosed( true );
1828
1829 BOOST_CHECK( chain.SelfIntersecting().has_value() );
1830}
1831
1832
1833BOOST_AUTO_TEST_CASE( SelfIntersecting_VertexOnSegment )
1834{
1835 // Third vertex lies exactly on the first segment
1836 SHAPE_LINE_CHAIN chain( { { 0, 0 }, { 20000, 0 }, { 20000, 10000 }, { 10000, 0 },
1837 { 10000, -10000 } } );
1838
1839 auto result = chain.SelfIntersecting();
1840 BOOST_REQUIRE( result.has_value() );
1841 BOOST_CHECK_EQUAL( result->p.x, 10000 );
1842 BOOST_CHECK_EQUAL( result->p.y, 0 );
1843}
1844
1845
1846BOOST_AUTO_TEST_CASE( SelfIntersecting_TwoSegments )
1847{
1848 SHAPE_LINE_CHAIN chain( { { 0, 0 }, { 10000, 0 } } );
1849
1850 BOOST_CHECK( !chain.SelfIntersecting() );
1851}
1852
1853
1854BOOST_AUTO_TEST_CASE( SelfIntersecting_SinglePoint )
1855{
1857 chain.Append( VECTOR2I( 0, 0 ) );
1858
1859 BOOST_CHECK( !chain.SelfIntersecting() );
1860}
1861
1862
1863BOOST_AUTO_TEST_CASE( SelfIntersecting_AdjacentSegmentsIgnored )
1864{
1865 // A simple zigzag where adjacent segments share endpoints but don't self-intersect
1866 SHAPE_LINE_CHAIN chain( { { 0, 0 }, { 5000, 10000 }, { 10000, 0 }, { 15000, 10000 },
1867 { 20000, 0 } } );
1868
1869 BOOST_CHECK( !chain.SelfIntersecting() );
1870}
1871
1872
1873BOOST_AUTO_TEST_CASE( SelfIntersecting_ClosedTriangle )
1874{
1875 SHAPE_LINE_CHAIN chain( { { 0, 0 }, { 10000, 0 }, { 5000, 10000 } } );
1876 chain.SetClosed( true );
1877
1878 BOOST_CHECK( !chain.SelfIntersecting() );
1879}
1880
1881
1882BOOST_AUTO_TEST_CASE( SelfIntersecting_ClosedLastFirstNotFalsePositive )
1883{
1884 // Closed rectangle. The last segment shares its endpoint with the first segment.
1885 // This must NOT be reported as a self-intersection.
1886 SHAPE_LINE_CHAIN chain( { { 0, 0 }, { 10000, 0 }, { 10000, 10000 }, { 0, 10000 } } );
1887 chain.SetClosed( true );
1888
1889 BOOST_CHECK( !chain.SelfIntersecting() );
1890}
1891
1892
1893BOOST_AUTO_TEST_CASE( SelfIntersecting_SpatiallyDistant )
1894{
1895 // Segments are far apart spatially, exercising the bbox rejection path
1896 SHAPE_LINE_CHAIN chain( { { 0, 0 }, { 1000, 0 },
1897 { 1000, 1000000 }, { 2000, 1000000 },
1898 { 2000, 2000000 }, { 3000, 2000000 } } );
1899
1900 BOOST_CHECK( !chain.SelfIntersecting() );
1901}
1902
1903
1904BOOST_AUTO_TEST_CASE( SelfIntersecting_LargeNonIntersecting )
1905{
1906 // Build a spiral-like chain with many segments that don't self-intersect
1908
1909 for( int i = 0; i < 200; i++ )
1910 chain.Append( VECTOR2I( i * 1000, ( i % 2 ) * 5000 ) );
1911
1912 BOOST_CHECK( !chain.SelfIntersecting() );
1913}
1914
1915
1916BOOST_AUTO_TEST_CASE( SelfIntersecting_LargeWithCrossing )
1917{
1918 // Many non-intersecting segments, then one that crosses back over an earlier one
1920
1921 for( int i = 0; i < 50; i++ )
1922 chain.Append( VECTOR2I( i * 1000, 0 ) );
1923
1924 // Last segment crosses back over the first few segments
1925 chain.Append( VECTOR2I( 5000, 10000 ) );
1926 chain.Append( VECTOR2I( 5000, -10000 ) );
1927
1928 BOOST_CHECK( chain.SelfIntersecting().has_value() );
1929}
1930
1931
constexpr int ARC_HIGH_DEF
Definition base_units.h:137
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I B
Definition seg.h:46
const VECTOR2I & GetArcMid() const
Definition shape_arc.h:116
bool IsClockwise() const
Definition shape_arc.h:319
SHAPE_ARC & ConstructFromStartEndCenter(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aClockwise=false, double aWidth=0)
Constructs this arc from the given start, end and center.
const VECTOR2I & GetP1() const
Definition shape_arc.h:115
bool Collide(const SEG &aSeg, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the segment aSeg than aClearance,...
static int DefaultAccuracyForPCB()
Definition shape_arc.h:279
const VECTOR2I & GetP0() const
Definition shape_arc.h:114
const VECTOR2I & GetCenter() const
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
bool IsClosed() const override
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
int Split(const VECTOR2I &aP, bool aExact=false)
Insert the point aP belonging to one of the our segments, splitting the adjacent segment in two.
int ShapeCount() const
Return the number of shapes (line segments or arcs) in this line chain.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
bool IsArcEnd(size_t aIndex) const
void Replace(int aStartIndex, int aEndIndex, const VECTOR2I &aP)
Replace points with indices in range [start_index, end_index] with a single point aP.
void Simplify(int aTolerance=0)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
void SetWidth(int aWidth) override
Set the width of all segments in the chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const std::vector< std::pair< ssize_t, ssize_t > > & CShapes() const
bool CompareGeometry(const SHAPE_LINE_CHAIN &aOther, bool aCyclicalCompare=false, int aEpsilon=0) const
Compare this line chain with another one.
const SHAPE_LINE_CHAIN Slice(int aStartIndex, int aEndIndex) const
Return a subset of this line chain containing the [start_index, end_index] range of points.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
size_t ArcCount() const
void RemoveShape(int aPointIndex)
Remove the shape at the given index from the line chain.
bool IsArcStart(size_t aIndex) const
bool IsSharedPt(size_t aIndex) const
Test if a point is shared between multiple shapes.
int Find(const VECTOR2I &aP, int aThreshold=0) const
Search for point aP.
const std::vector< VECTOR2I > & CPoints() const
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
bool IsOutlineValid(const SHAPE_LINE_CHAIN &aChain)
Verify that a SHAPE_LINE_CHAIN has been assembled correctly by ensuring that the arc start and end po...
Numerical test predicates.
NOTE: Collision of SHAPE_LINE_CHAIN with arcs is tested in test_shape_arc.cpp.
SHAPE_LINE_CHAIN EmptyChain
SHAPE_LINE_CHAIN Circle1Arc
SHAPE_LINE_CHAIN ArcsCoincident
SHAPE_ARC Arc1
start coincident with Arc0a end
SHAPE_ARC Arc2
Independent arc.
SHAPE_ARC Arc3
Arc with angle >180.
SHAPE_ARC Arc0a
First half of a circle.
SHAPE_LINE_CHAIN DuplicateArcs
SHAPE_LINE_CHAIN Circle2Arcs
SHAPE_LINE_CHAIN ArcsCoincidentClosed
SHAPE_LINE_CHAIN TwoPoints
SHAPE_LINE_CHAIN SegAndArcCoincident
SHAPE_LINE_CHAIN ArcsIndependent
SHAPE_ARC ArcCircle
Full Circle arc.
SHAPE_LINE_CHAIN ThreePoints
SHAPE_LINE_CHAIN ArcAndPoint
SHAPE_LINE_CHAIN ArcsAndSegMixed
SHAPE_LINE_CHAIN OnePoint
SHAPE_ARC Arc0b
Second half of a circle.
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_REQUIRE(intersection.has_value()==c.ExpectedIntersection.has_value())
BOOST_AUTO_TEST_SUITE_END()
BOOST_TEST(netlist.find("R_G1 ARM_OUT1 DIE_B R='0.001 / ((SW_STATE)") !=std::string::npos)
BOOST_TEST_INFO("Two-port Series .op current = "<< iDevice)
BOOST_TEST_MESSAGE("\n=== Real-World Polygon PIP Benchmark ===\n"<< formatTable(table))
SHAPE_ARC arc2(c.m_arc2.GenerateArc())
const SHAPE_LINE_CHAIN chain
BOOST_TEST_CONTEXT("Test Clearance")
static const std::vector< CLOSE_TOGGLE_SHAPE_CASE > close_toggle_shape_cases
BOOST_AUTO_TEST_CASE(ClipperConstructorCase1)
static const std::vector< REMOVE_SHAPE_CASE > remove_shape_cases
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683