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 (C) 2019-2021 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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 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
24#include <geometry/shape_arc.h>
26#include <trigo.h>
27
29#include <qa_utils/numeric.h>
31
32#include "geom_test_utils.h"
33
39{
53
60
62 {
63 ArcCircle = SHAPE_ARC( VECTOR2I( 183450000, 128360000 ),
64 VECTOR2I( 183850000, 128360000 ),
65 VECTOR2I( 183450000, 128360000 ), 0 );
66
67 Arc0a = SHAPE_ARC( VECTOR2I( 183450000, 128360000 ),
68 VECTOR2I( 183650000, 128560000 ),
69 VECTOR2I( 183850000, 128360000 ), 0 );
70
71 Arc0b = SHAPE_ARC( VECTOR2I( 183850000, 128360000 ),
72 VECTOR2I( 183650000, 128160000 ),
73 VECTOR2I( 183450000, 128360000 ), 0 );
74
75 Arc1 = SHAPE_ARC( VECTOR2I( 183850000, 128360000 ),
76 VECTOR2I( 183638550, 128640305 ),
77 VECTOR2I( 183500000, 129204974 ), 0 );
78
79 Arc2 = SHAPE_ARC( VECTOR2I( 283450000, 228360000 ),
80 VECTOR2I( 283650000, 228560000 ),
81 VECTOR2I( 283850000, 228360000 ), 0 );
82
83 Arc3 = SHAPE_ARC( VECTOR2I( 0, 0 ),
84 VECTOR2I( 24142136, 10000000 ),
85 VECTOR2I( 0, 20000000 ), 0 );
86
88 Circle1Arc.SetClosed( true );
89
92 Circle2Arcs.SetClosed( true );
93
96
99
102
104 DuplicateArcs.Append( Arc1 ); //should add a segment between end of the chain and new copy of the arc
105
107 ArcAndPoint.Append( VECTOR2I( 233450000, 228360000 ) );
108
111
112 OnePoint.Append( VECTOR2I( 233450000, 228360000 ) );
113
114 TwoPoints.Append( VECTOR2I( 233450000, 228360000 ) );
115 TwoPoints.Append( VECTOR2I( 263450000, 258360000 ) );
116
118 ThreePoints.Append( VECTOR2I( 263450000, 308360000 ) );
119
120 SegAndArcCoincident.Append( VECTOR2I( 0, 20000000 ) );
122 }
123};
124
125
126BOOST_FIXTURE_TEST_SUITE( TestShapeLineChain, SLC_CASES )
127
128
129BOOST_AUTO_TEST_CASE( ClipperConstructorCase1 )
130{
131 // Case of an arc followed by a segment
132 // The clipper path is not in order (on purpose), to simulate the typical return from clipper
133
134 ClipperLib::Path pathClipper1 = {
135 { { 125663951, 120099260, 24 }, { 125388111, 120170850, 25 }, { 125124975, 120280270, 26 },
136 { 124879705, 120425376, 27 }, { 124657110, 120603322, 28 }, { 124461556, 120810617, 29 },
137 { 124296876, 121043198, 30 }, { 124166301, 121296503, 31 }, { 124072391, 121565564, 32 },
138 { 124016988, 121845106, 33 }, { 124001177, 122129646, 34 }, { 124025270, 122413605, 35 },
139 { 124088794, 122691414, 36 }, { 124190502, 122957625, 37 }, { 124328401, 123207018, 38 },
140 { 124499787, 123434703, 39 }, { 124598846, 123537154, 40 }, { 127171000, 123786000, 4 },
141 { 127287862, 123704439, 5 }, { 127499716, 123513831, 6 }, { 127682866, 123295498, 7 },
142 { 127833720, 123053722, 8 }, { 127949321, 122793242, 9 }, { 128027402, 122519168, 10 },
143 { 128066430, 122236874, 11 }, { 128065642, 121951896, 12 }, { 128025053, 121669823, 13 },
144 { 127945457, 121396185, 14 }, { 127828417, 121136349, 15 }, { 127676227, 120895410, 16 },
145 { 127491873, 120678094, 17 }, { 127278968, 120488661, 18 }, { 127041689, 120330827, 19 },
146 { 126784688, 120207687, 20 }, { 126513005, 120121655, 21 }, { 126231968, 120074419, 22 },
147 { 125947087, 120066905, 23 } }
148 };
149 Clipper2Lib::Path64 pathClipper2 = {
150 { { 125663951, 120099260, 24 }, { 125388111, 120170850, 25 }, { 125124975, 120280270, 26 },
151 { 124879705, 120425376, 27 }, { 124657110, 120603322, 28 }, { 124461556, 120810617, 29 },
152 { 124296876, 121043198, 30 }, { 124166301, 121296503, 31 }, { 124072391, 121565564, 32 },
153 { 124016988, 121845106, 33 }, { 124001177, 122129646, 34 }, { 124025270, 122413605, 35 },
154 { 124088794, 122691414, 36 }, { 124190502, 122957625, 37 }, { 124328401, 123207018, 38 },
155 { 124499787, 123434703, 39 }, { 124598846, 123537154, 40 }, { 127171000, 123786000, 4 },
156 { 127287862, 123704439, 5 }, { 127499716, 123513831, 6 }, { 127682866, 123295498, 7 },
157 { 127833720, 123053722, 8 }, { 127949321, 122793242, 9 }, { 128027402, 122519168, 10 },
158 { 128066430, 122236874, 11 }, { 128065642, 121951896, 12 }, { 128025053, 121669823, 13 },
159 { 127945457, 121396185, 14 }, { 127828417, 121136349, 15 }, { 127676227, 120895410, 16 },
160 { 127491873, 120678094, 17 }, { 127278968, 120488661, 18 }, { 127041689, 120330827, 19 },
161 { 126784688, 120207687, 20 }, { 126513005, 120121655, 21 }, { 126231968, 120074419, 22 },
162 { 125947087, 120066905, 23 } }
163 };
164
165 std::vector<CLIPPER_Z_VALUE> z_values = {
166 { { -1, -1 }, 0 }, { { -1, -1 }, 0 }, { { -1, -1 }, 0 }, { { -1, -1 }, 0 },
167 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
168 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
169 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
170 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
171 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
172 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
173 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
174 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
175 { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 }, { { 0, -1 }, 0 },
176 { { 0, -1 }, 0 }
177 };
178
179 std::vector<SHAPE_ARC> arcs = {
180 SHAPE_ARC( { 127171000, 123786000 }, { 126231718, 120077003 }, { 124598846, 123537154 }, 0 )
181 };
182
183 SHAPE_LINE_CHAIN clipper1chain( pathClipper1, z_values, arcs );
184 SHAPE_LINE_CHAIN clipper2chain( pathClipper2, z_values, arcs );
185
186 BOOST_CHECK( GEOM_TEST::IsOutlineValid( clipper1chain ) );
187 BOOST_CHECK( GEOM_TEST::IsOutlineValid( clipper2chain ) );
188
189 BOOST_CHECK_EQUAL( clipper1chain.PointCount(), 37 );
190 BOOST_CHECK_EQUAL( clipper2chain.PointCount(), 37 );
191
192 BOOST_CHECK_EQUAL( clipper1chain.ArcCount(), 1 );
193 BOOST_CHECK_EQUAL( clipper2chain.ArcCount(), 1 );
194
195 BOOST_CHECK_EQUAL( clipper1chain.ShapeCount(), 2 );
196 BOOST_CHECK_EQUAL( clipper2chain.ShapeCount(), 2 );
197
198 BOOST_CHECK_EQUAL( clipper1chain.IsClosed(), true );
199 BOOST_CHECK_EQUAL( clipper2chain.IsClosed(), true );
200}
201
202
203BOOST_AUTO_TEST_CASE( ArcToPolyline )
204{
205 SHAPE_LINE_CHAIN base_chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 1000 ), VECTOR2I( 1000, 0 ) } );
206
207 SHAPE_LINE_CHAIN chain_insert( {
208 VECTOR2I( 0, 1500 ),
209 VECTOR2I( 1500, 1500 ),
210 VECTOR2I( 1500, 0 ),
211 } );
212
213 SHAPE_LINE_CHAIN arc_insert1( SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), ANGLE_180 ) );
214
215 SHAPE_LINE_CHAIN arc_insert2( SHAPE_ARC( VECTOR2I( 0, 500 ), VECTOR2I( 0, 400 ), ANGLE_180 ) );
216
217 BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
218 BOOST_CHECK_EQUAL( arc_insert1.CShapes().size(), arc_insert1.CPoints().size() );
219 BOOST_CHECK_EQUAL( arc_insert2.CShapes().size(), arc_insert2.CPoints().size() );
220
221 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
222 BOOST_CHECK( GEOM_TEST::IsOutlineValid( arc_insert1 ) );
223 BOOST_CHECK( GEOM_TEST::IsOutlineValid( arc_insert2 ) );
224
225 base_chain.Insert( 0, SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), ANGLE_180 ) );
226 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
227 BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
228
229 base_chain.Replace( 0, 2, chain_insert );
230 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
231 BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
232}
233
234
235// Similar test to above but with larger coordinates, so we have more than one point per arc
236BOOST_AUTO_TEST_CASE( ArcToPolylineLargeCoords )
237{
238 SHAPE_LINE_CHAIN base_chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( 100000, 0 ) } );
239
240 SHAPE_LINE_CHAIN chain_insert( {
241 VECTOR2I( 0, 1500000 ),
242 VECTOR2I( 1500000, 1500000 ),
243 VECTOR2I( 1500000, 0 ),
244 } );
245
246 base_chain.Append( SHAPE_ARC( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 100000 ), ANGLE_180 ) );
247
248 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
249 BOOST_CHECK_EQUAL( base_chain.PointCount(), 11 );
250
251 base_chain.Insert( 9, VECTOR2I( 250000, 0 ) );
252 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
253 BOOST_CHECK_EQUAL( base_chain.PointCount(), 12 );
254 BOOST_CHECK_EQUAL( base_chain.ArcCount(), 2 ); // Should have two arcs after the split
255
256 base_chain.Replace( 5, 6, chain_insert );
257 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
258 BOOST_CHECK_EQUAL( base_chain.PointCount(), 13 ); // Adding 3 points, removing 2
259 BOOST_CHECK_EQUAL( base_chain.ArcCount(), 3 ); // Should have three arcs after the split
260
261 base_chain.Replace( 4, 6, VECTOR2I( 550000, 0 ) );
262 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
263 BOOST_CHECK_EQUAL( base_chain.PointCount(), 11 ); // Adding 1 point, removing 3
264 BOOST_CHECK_EQUAL( base_chain.ArcCount(), 3 ); // Should still have three arcs
265
266 // Test ClearArcs
267 base_chain.SetClosed( true );
268 double areaPriorToArcRemoval = base_chain.Area();
269 base_chain.ClearArcs();
270
271 BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
272 BOOST_CHECK_EQUAL( base_chain.CPoints().size(), base_chain.CShapes().size() );
273 BOOST_CHECK_EQUAL( base_chain.PointCount(), 11 ); // We should have the same number of points
274 BOOST_CHECK_EQUAL( base_chain.ArcCount(), 0 ); // All arcs should have been removed
275 BOOST_CHECK_EQUAL( base_chain.Area(), areaPriorToArcRemoval ); // Area should not have changed
276}
277
278// Test that duplicate point gets removed when line is set to be closed and added where required
279BOOST_AUTO_TEST_CASE( SetClosedDuplicatePoint )
280{
281 // Test from issue #9843
282 SHAPE_LINE_CHAIN chain;
283
284 chain.Append(
285 SHAPE_ARC( { -859598, 2559876 }, { -1632771, 1022403 }, { -3170244, 249230 }, 0 ) );
286
287 chain.Append(
288 SHAPE_ARC( { -3170244, -1657832 }, { -292804, -317564 }, { 1047464, 2559876 }, 0 ) );
289
290 chain.Append( VECTOR2I( -859598, 2559876 ) ); // add point that is equal to first arc start
291
292 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
293 BOOST_CHECK_EQUAL( chain.PointCount(), 31 );
294
295 // CLOSED CHAIN
296 chain.SetClosed( true );
297 BOOST_CHECK_EQUAL( chain.CPoints().size(), chain.CShapes().size() );
298 BOOST_CHECK_EQUAL( chain.PointCount(), 30 ); // (-1) should have removed coincident points
299 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
300
301 // Special case: arc wrapping around to start (e.g. circle)
302 BOOST_CHECK( GEOM_TEST::IsOutlineValid( Circle2Arcs ) );
303 BOOST_CHECK_EQUAL( Circle2Arcs.IsClosed(), true );
304 BOOST_CHECK_EQUAL( Circle2Arcs.PointCount(), 16 );
305 BOOST_CHECK_EQUAL( Circle2Arcs.IsArcSegment( 15 ), true );
306 BOOST_CHECK_EQUAL( Circle2Arcs.ShapeCount(), 2 );
307 Circle2Arcs.SetClosed( false );
308 BOOST_CHECK( GEOM_TEST::IsOutlineValid( Circle2Arcs ) );
309 BOOST_CHECK_EQUAL( Circle2Arcs.IsClosed(), false );
310 BOOST_CHECK_EQUAL( Circle2Arcs.PointCount(), 17 );
311 BOOST_CHECK_EQUAL( Circle2Arcs.IsArcSegment( 15 ), true );
312 BOOST_CHECK_EQUAL( Circle2Arcs.IsArcSegment( 16 ), false ); // last point doesn't join up
313}
314
316{
317 std::string m_ctx_name;
324};
325
326static const std::vector<CLOSE_TOGGLE_SHAPE_CASE> close_toggle_shape_cases =
327 {
328 { "Circle1Arc", SLC_CASES().Circle1Arc, true, 1, 15, 1, 16 },
329 { "Circle2Arcs", SLC_CASES().Circle2Arcs, true, 2, 16, 2, 17 },
330 { "ArcsCoincident", SLC_CASES().ArcsCoincident, false, 2, 14, 3, 14 },
331 { "ArcsCoincidentClosed", SLC_CASES().ArcsCoincidentClosed, true, 3, 14, 2, 14 },
332 { "ArcsIndependent", SLC_CASES().ArcsIndependent, false, 3, 18, 4, 18 },
333 // SegAndArcCoincident will remove the segment after SetClosed(true) and SetClosed(false)
334 // disable test for now
335 //{ "SegAndArcCoincident", SLC_CASES().SegAndArcCoincident, false, 2, 92, 2, 91 },
336 { "DuplicateArcs", SLC_CASES().DuplicateArcs, false, 4, 20, 5, 20 },
337 { "ArcAndPoint", SLC_CASES().ArcAndPoint, false, 2, 10, 3, 10 },
338 { "ArcsAndSegMixed", SLC_CASES().ArcsAndSegMixed, false, 4, 19, 5, 19 },
339 { "OnePoint", SLC_CASES().OnePoint, false, 0, 1, 0, 1 }, // no shapes
340 { "TwoPoints", SLC_CASES().TwoPoints, false, 1, 2, 2, 2 }, // there and back
341 { "ThreePoints", SLC_CASES().ThreePoints, false, 2, 3, 3, 3 },
342 };
343
344BOOST_AUTO_TEST_CASE( ToggleClosed )
345{
347 {
348 BOOST_TEST_CONTEXT( c.m_ctx_name )
349 {
350 SHAPE_LINE_CHAIN slc_case = c.m_chain; // make a copy to edit
351 BOOST_CHECK( GEOM_TEST::IsOutlineValid( slc_case ) );
352 BOOST_CHECK_EQUAL( slc_case.IsClosed(), c.m_closed );
353 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
354 BOOST_CHECK_EQUAL( slc_case.PointCount(), c.m_point_count );
355 slc_case.SetClosed( !c.m_closed );
356 BOOST_CHECK( GEOM_TEST::IsOutlineValid( slc_case ) );
357 BOOST_CHECK_EQUAL( slc_case.IsClosed(), !c.m_closed );
358 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_expected_shape_count );
359 BOOST_CHECK_EQUAL( slc_case.PointCount(), c.m_expected_point_count );
360 slc_case.SetClosed( c.m_closed ); // toggle back to normal
361 BOOST_CHECK( GEOM_TEST::IsOutlineValid( slc_case ) );
362 BOOST_CHECK_EQUAL( slc_case.IsClosed(), c.m_closed );
363 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
364 BOOST_CHECK_EQUAL( slc_case.PointCount(), c.m_point_count );
365 }
366 }
367}
368
369
370BOOST_AUTO_TEST_CASE( PointInPolygon )
371{
372 SHAPE_LINE_CHAIN outline1( { { 1316455, 913576 }, { 1316455, 901129 }, { 1321102, 901129 },
373 { 1322152, 901191 }, { 1323055, 901365 }, { 1323830, 901639 },
374 { 1324543, 902036 }, { 1325121, 902521 }, { 1325581, 903100 },
375 { 1325914, 903759 }, { 1326120, 904516 }, { 1326193, 905390 },
376 { 1326121, 906253 }, { 1325915, 907005 }, { 1325581, 907667 },
377 { 1325121, 908248 }, { 1324543, 908735 }, { 1323830, 909132 },
378 { 1323055, 909406 }, { 1322153, 909579 }, { 1321102, 909641 },
379 { 1317174, 909641 }, { 1317757, 909027 }, { 1317757, 913576 } } );
380 SHAPE_LINE_CHAIN outline2( { { 1297076, 916244 }, { 1284629, 916244 }, { 1284629, 911597 },
381 { 1284691, 910547 }, { 1284865, 909644 }, { 1285139, 908869 },
382 { 1285536, 908156 }, { 1286021, 907578 }, { 1286600, 907118 },
383 { 1287259, 906785 }, { 1288016, 906579 }, { 1288890, 906506 },
384 { 1289753, 906578 }, { 1290505, 906784 }, { 1291167, 907118 },
385 { 1291748, 907578 }, { 1292235, 908156 }, { 1292632, 908869 },
386 { 1292906, 909644 }, { 1293079, 910546 }, { 1293141, 911597 },
387 { 1293141, 915525 }, { 1292527, 914942 }, { 1297076, 914942 } } );
388
389 // Test a point inside the polygon
390 VECTOR2I point1( 1317757, 909133 );
391 VECTOR2I point2( 1292633, 914942 );
392
393 outline1.SetClosed( true );
394 outline2.SetClosed( true );
395
396 BOOST_CHECK( outline1.PointInside( point1, 0, false ) );
397 BOOST_CHECK( outline2.PointInside( point2, 0, false ) );
398}
399
400// Test that duplicate point gets removed when we call simplify
401BOOST_AUTO_TEST_CASE( SimplifyDuplicatePoint )
402{
403 SHAPE_LINE_CHAIN chain;
404
405 chain.Append( { 100, 100 } );
406 chain.Append( { 100, 100 }, true ); //duplicate point to simplify
407 chain.Append( { 200, 100 } );
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(), 2 ); // (-1) should have removed coincident points
416 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
417}
418
419
420// Test that duplicate point gets removed when we call simplify
421BOOST_AUTO_TEST_CASE( SimplifyKeepEndPoint )
422{
423 SHAPE_LINE_CHAIN chain;
424
425 chain.Append( { 114772424, 90949410 } );
426 chain.Append( { 114767360, 90947240 } );
427 chain.Append( { 114772429, 90947228 } );
428 chain.SetClosed( true );
429
430 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
431 BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
432
433 chain.Simplify();
434
435 BOOST_CHECK_EQUAL( chain.CPoints().size(), chain.CShapes().size() );
436 BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
437 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
438}
439
440
441BOOST_AUTO_TEST_CASE( SimplifyPNSChain )
442{
443 SHAPE_LINE_CHAIN chain;
444 chain.Append( VECTOR2I( 157527820, 223074385 ) );
445 chain.Append( VECTOR2I( 186541122, 159990156 ) );
446 chain.Append( VECTOR2I( 186528624, 159977658 ) );
447 chain.Append( VECTOR2I( 186528624, 159770550 ) );
448 chain.Append( VECTOR2I( 186528625, 159366691 ) );
449 chain.Append( VECTOR2I( 186541122, 159354195 ) );
450 chain.Append( VECTOR2I( 186541122, 155566877 ) );
451 chain.Append( VECTOR2I( 187291125, 154816872 ) );
452 chain.Append( VECTOR2I( 187291125, 147807837 ) );
453 chain.Append( VECTOR2I( 189301788, 145797175 ) );
454 chain.Append( VECTOR2I( 194451695, 145797175 ) );
455 chain.Append( VECTOR2I( 195021410, 146366890 ) );
456
457 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
458 BOOST_CHECK_EQUAL( chain.PointCount(), 12 );
459
460 // The chain should be open, so the points should not be simplified
461 // between the begining and the end.
462 chain.Simplify( 10 );
463
464 BOOST_CHECK_EQUAL( chain.PointCount(), 11 );
465}
466
467
468BOOST_AUTO_TEST_CASE( SimplifyComplexChain )
469{
470 SHAPE_LINE_CHAIN chain;
471
472 // Append points
473 chain.Append( { 130000, 147320 } );
474 chain.Append( { 125730, 147320 } );
475 chain.Append( { 125730, 150630 } );
476 chain.Append( { 128800, 153700 } );
477 chain.Append( { 150300, 153700 } );
478 chain.Append( { 151500, 152500 } );
479 chain.Append( { 151500, 148900 } );
480 chain.Append( { 149920, 147320 } );
481 chain.Append( { 140000, 147320 } );
482
483 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
484 BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
485
486 // The chain should be open, so the points should not be simplified
487 // between the begining and the end.
488 chain.Simplify();
489
490 BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
491
492 chain.SetClosed( true );
493 chain.Simplify();
494
495 BOOST_CHECK_EQUAL( chain.PointCount(), 8 );
496}
497
499{
500 std::string m_ctx_name;
507};
508
509static const std::vector<REMOVE_SHAPE_CASE> remove_shape_cases =
510 {
511 { "Circle1Arc - 1st arc - index on start", SLC_CASES().Circle1Arc, 1, 1, 0, 0, 0 },
512 { "Circle1Arc - 1st arc - index on mid", SLC_CASES().Circle1Arc, 1, 1, 8, 0, 0 },
513 { "Circle1Arc - 1st arc - index on end", SLC_CASES().Circle1Arc, 1, 1, 14, 0, 0 },
514 { "Circle1Arc - 1st arc - index on -1", SLC_CASES().Circle1Arc, 1, 1, -1, 0, 0 },
515 { "Circle1Arc - invalid index", SLC_CASES().Circle1Arc, 1, 1, 15, 1, 1 },
516
517 { "Circle2Arcs - 1st arc - index on start", SLC_CASES().Circle2Arcs, 2, 2, 0, 2, 1 },
518 { "Circle2Arcs - 1st arc - index on mid", SLC_CASES().Circle2Arcs, 2, 2, 3, 2, 1 },
519 { "Circle2Arcs - 1st arc - index on end", SLC_CASES().Circle2Arcs, 2, 2, 7, 2, 1 },
520 { "Circle2Arcs - 2nd arc - index on start", SLC_CASES().Circle2Arcs, 2, 2, 8, 2, 1 },
521 { "Circle2Arcs - 2nd arc - index on mid", SLC_CASES().Circle2Arcs, 2, 2, 11, 2, 1 },
522 { "Circle2Arcs - 2nd arc - index on end", SLC_CASES().Circle2Arcs, 2, 2, 15, 2, 1 },
523 { "Circle2Arcs - 2nd arc - index on -1", SLC_CASES().Circle2Arcs, 2, 2, -1, 2, 1 },
524 { "Circle2Arcs - invalid index", SLC_CASES().Circle2Arcs, 2, 2, 16, 2, 2 },
525
526 { "ArcsCoinc. - 1st arc - idx on start", SLC_CASES().ArcsCoincident, 2, 2, 0, 1, 1 },
527 { "ArcsCoinc. - 1st arc - idx on mid", SLC_CASES().ArcsCoincident, 2, 2, 3, 1, 1 },
528 { "ArcsCoinc. - 1st arc - idx on end", SLC_CASES().ArcsCoincident, 2, 2, 7, 1, 1 },
529 { "ArcsCoinc. - 2nd arc - idx on start", SLC_CASES().ArcsCoincident, 2, 2, 8, 1, 1 },
530 { "ArcsCoinc. - 2nd arc - idx on mid", SLC_CASES().ArcsCoincident, 2, 2, 10, 1, 1 },
531 { "ArcsCoinc. - 2nd arc - idx on end", SLC_CASES().ArcsCoincident, 2, 2, 13, 1, 1 },
532 { "ArcsCoinc. - 2nd arc - idx on -1", SLC_CASES().ArcsCoincident, 2, 2, -1, 1, 1 },
533 { "ArcsCoinc. - invalid idx", SLC_CASES().ArcsCoincident, 2, 2, 14, 2, 2 },
534 { "ArcsCoinc. - 1st arc - idx on start", SLC_CASES().ArcsCoincident, 2, 2, 0, 1, 1 },
535
536 { "A.Co.Closed - 1st arc - idx on start", SLC_CASES().ArcsCoincidentClosed, 3, 2, 1, 2, 1 },
537 { "A.Co.Closed - 1st arc - idx on mid", SLC_CASES().ArcsCoincidentClosed, 3, 2, 3, 2, 1 },
538 { "A.Co.Closed - 1st arc - idx on end", SLC_CASES().ArcsCoincidentClosed, 3, 2, 7, 2, 1 },
539 { "A.Co.Closed - 2nd arc - idx on start", SLC_CASES().ArcsCoincidentClosed, 3, 2, 8, 2, 1 },
540 { "A.Co.Closed - 2nd arc - idx on mid", SLC_CASES().ArcsCoincidentClosed, 3, 2, 10, 2, 1 },
541 { "A.Co.Closed - 2nd arc - idx on end", SLC_CASES().ArcsCoincidentClosed, 3, 2, 13, 2, 1 },
542 { "A.Co.Closed - 2nd arc - idx on -1", SLC_CASES().ArcsCoincidentClosed, 3, 2, -1, 2, 1 },
543 { "A.Co.Closed - invalid idx", SLC_CASES().ArcsCoincidentClosed, 3, 2, 14, 3, 2 },
544
545 { "ArcsIndep. - 1st arc - idx on start", SLC_CASES().ArcsIndependent, 3, 2, 0, 1, 1 },
546 { "ArcsIndep. - 1st arc - idx on mid", SLC_CASES().ArcsIndependent, 3, 2, 3, 1, 1 },
547 { "ArcsIndep. - 1st arc - idx on end", SLC_CASES().ArcsIndependent, 3, 2, 8, 1, 1 },
548 { "ArcsIndep. - 2nd arc - idx on start", SLC_CASES().ArcsIndependent, 3, 2, 9, 1, 1 },
549 { "ArcsIndep. - 2nd arc - idx on mid", SLC_CASES().ArcsIndependent, 3, 2, 12, 1, 1 },
550 { "ArcsIndep. - 2nd arc - idx on end", SLC_CASES().ArcsIndependent, 3, 2, 17, 1, 1 },
551 { "ArcsIndep. - 2nd arc - idx on -1", SLC_CASES().ArcsIndependent, 3, 2, -1, 1, 1 },
552 { "ArcsIndep. - invalid idx", SLC_CASES().ArcsIndependent, 3, 2, 18, 3, 2 },
553
554 { "Dup.Arcs - 1st arc - idx on start", SLC_CASES().DuplicateArcs, 4, 3, 0, 3, 2 },
555 { "Dup.Arcs - 1st arc - idx on mid", SLC_CASES().DuplicateArcs, 4, 3, 3, 3, 2 },
556 { "Dup.Arcs - 1st arc - idx on end", SLC_CASES().DuplicateArcs, 4, 3, 7, 3, 2 },
557 { "Dup.Arcs - 2nd arc - idx on start", SLC_CASES().DuplicateArcs, 4, 3, 8, 3, 2 },
558 { "Dup.Arcs - 2nd arc - idx on mid", SLC_CASES().DuplicateArcs, 4, 3, 10, 3, 2 },
559 { "Dup.Arcs - 2nd arc - idx on end", SLC_CASES().DuplicateArcs, 4, 3, 13, 3, 2 },
560 { "Dup.Arcs - 3rd arc - idx on start", SLC_CASES().DuplicateArcs, 4, 3, 14, 2, 2 },
561 { "Dup.Arcs - 3rd arc - idx on mid", SLC_CASES().DuplicateArcs, 4, 3, 17, 2, 2 },
562 { "Dup.Arcs - 3rd arc - idx on end", SLC_CASES().DuplicateArcs, 4, 3, 19, 2, 2 },
563 { "Dup.Arcs - 3rd arc - idx on -1", SLC_CASES().DuplicateArcs, 4, 3, -1, 2, 2 },
564 { "Dup.Arcs - invalid idx", SLC_CASES().DuplicateArcs, 4, 3, 20, 4, 3 },
565
566 { "Arcs Mixed - 1st arc - idx on start", SLC_CASES().ArcsAndSegMixed, 4, 2, 0, 2, 1 },
567 { "Arcs Mixed - 1st arc - idx on mid", SLC_CASES().ArcsAndSegMixed, 4, 2, 3, 2, 1 },
568 { "Arcs Mixed - 1st arc - idx on end", SLC_CASES().ArcsAndSegMixed, 4, 2, 8, 2, 1 },
569 { "Arcs Mixed - Straight segment", SLC_CASES().ArcsAndSegMixed, 4, 2, 9, 3, 2 },
570 { "Arcs Mixed - 2nd arc - idx on start", SLC_CASES().ArcsAndSegMixed, 4, 2, 10, 2, 1 },
571 { "Arcs Mixed - 2nd arc - idx on mid", SLC_CASES().ArcsAndSegMixed, 4, 2, 14, 2, 1 },
572 { "Arcs Mixed - 2nd arc - idx on end", SLC_CASES().ArcsAndSegMixed, 4, 2, 18, 2, 1 },
573 { "Arcs Mixed - 2nd arc - idx on -1", SLC_CASES().ArcsAndSegMixed, 4, 2, -1, 2, 1 },
574 { "Arcs Mixed - invalid idx", SLC_CASES().ArcsAndSegMixed, 4, 2, 19, 4, 2 }
575 };
576
577
579{
580 for( const REMOVE_SHAPE_CASE& c : remove_shape_cases )
581 {
582 BOOST_TEST_CONTEXT( c.m_ctx_name )
583 {
584 SHAPE_LINE_CHAIN slc_case = c.m_chain; // make a copy to edit
585 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
586 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_arc_count );
587 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
588 slc_case.RemoveShape( c.m_remove_index );
589 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_expected_shape_count );
590 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_expected_arc_count );
591 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
592 }
593 }
594}
595
596
597BOOST_AUTO_TEST_CASE( RemoveShapeAfterSimplify )
598{
599 for( const REMOVE_SHAPE_CASE& c : remove_shape_cases )
600 {
601 BOOST_TEST_CONTEXT( c.m_ctx_name )
602 {
603 SHAPE_LINE_CHAIN slc_case = c.m_chain; // make a copy to edit
604 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
605 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
606 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_arc_count );
607 slc_case.Simplify();
608 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
609 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
610 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_arc_count );
611 slc_case.RemoveShape( c.m_remove_index );
612 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
613 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_expected_shape_count );
614 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_expected_arc_count );
615 }
616 }
617}
618
619
621{
622 BOOST_CHECK_EQUAL( Circle1Arc.ShapeCount(), 1 );
623 BOOST_CHECK_EQUAL( Circle2Arcs.ShapeCount(), 2 );
624 BOOST_CHECK_EQUAL( ArcsCoincident.ShapeCount(), 2 );
625 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.ShapeCount(), 3 );
626 BOOST_CHECK_EQUAL( DuplicateArcs.ShapeCount(), 4 );
627 BOOST_CHECK_EQUAL( ArcAndPoint.ShapeCount(), 2 );
628 BOOST_CHECK_EQUAL( ArcsAndSegMixed.ShapeCount(), 4 );
629 BOOST_CHECK_EQUAL( SegAndArcCoincident.ShapeCount(), 2 );
630 BOOST_CHECK_EQUAL( EmptyChain.ShapeCount(), 0 );
631 BOOST_CHECK_EQUAL( OnePoint.ShapeCount(), 0 );
632 BOOST_CHECK_EQUAL( TwoPoints.ShapeCount(), 1 );
633 BOOST_CHECK_EQUAL( ThreePoints.ShapeCount(), 2 );
634}
635
636
638{
639 BOOST_CHECK_EQUAL( Circle1Arc.NextShape( 0 ), -1 ); //only one arc
640
641 BOOST_CHECK_EQUAL( Circle2Arcs.NextShape( 0 ), 8 ); // next shape "Arc0b"
642 BOOST_CHECK_EQUAL( Circle2Arcs.NextShape( 8 ), -1 ); //no more shapes (last point joins with first, part of arc)
643
644 BOOST_CHECK_EQUAL( ArcsCoincident.NextShape( 0 ), 8 ); // next shape "Arc1"
645 BOOST_CHECK_EQUAL( ArcsCoincident.NextShape( 8 ), -1 ); //no more shapes
646
647 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.NextShape( 0 ), 8 ); // next shape "Arc1"
648 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.NextShape( 8 ), 13 ); //next shape is hidden segment joining last/first
649 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.NextShape( 13 ), -1 ); //no more shapes
650
651 BOOST_CHECK_EQUAL( ArcsIndependent.NextShape( 0 ), 8 ); // next shape straight seg
652 BOOST_CHECK_EQUAL( ArcsIndependent.NextShape( 8 ), 9 ); //next shape second arc
653 BOOST_CHECK_EQUAL( ArcsIndependent.NextShape( 9 ), -1 ); //no more shapes
654
655 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 0 ), 8 ); // next shape "Arc1"
656 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 8 ), 13 ); // next shape hidden segment joining the 2 duplicate arcs
657 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 13 ), 14 ); // next shape "Arc1" (duplicate)
658 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 14 ), -1 ); //no more shapes
659
660 BOOST_CHECK_EQUAL( ArcAndPoint.NextShape( 0 ), 8 ); // next shape straight segment (end of arc->point)
661 BOOST_CHECK_EQUAL( ArcAndPoint.NextShape( 8 ), -1 ); //no more shapes
662
663 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 0 ), 8 ); // next shape straight segment (end of arc->point)
664 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 8 ), 9 ); // next shape straight segment (point->begining of arc)
665 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 9 ), 10 ); //next shape second arc
666 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 10 ), -1 ); //no more shapes
667 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 20 ), -1 ); //invalid indices should still work
668 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( -50 ), -1 ); //invalid indices should still work
669
670 BOOST_CHECK_EQUAL( SegAndArcCoincident.NextShape( 0 ), 1 ); // next shape Arc3
671 BOOST_CHECK_EQUAL( SegAndArcCoincident.NextShape( 1 ), -1 ); //no more shapes
672
673 BOOST_CHECK_EQUAL( EmptyChain.NextShape( 0 ), -1 );
674 BOOST_CHECK_EQUAL( EmptyChain.NextShape( 1 ), -1 ); //invalid indices should still work
675 BOOST_CHECK_EQUAL( EmptyChain.NextShape( 2 ), -1 ); //invalid indices should still work
676 BOOST_CHECK_EQUAL( EmptyChain.NextShape( -2 ), -1 ); //invalid indices should still work
677
678 BOOST_CHECK_EQUAL( OnePoint.NextShape( 0 ), -1 );
679 BOOST_CHECK_EQUAL( OnePoint.NextShape( -1 ), -1 );
680 BOOST_CHECK_EQUAL( OnePoint.NextShape( 1 ), -1 ); //invalid indices should still work
681 BOOST_CHECK_EQUAL( OnePoint.NextShape( 2 ), -1 ); //invalid indices should still work
682 BOOST_CHECK_EQUAL( OnePoint.NextShape( -2 ), -1 ); //invalid indices should still work
683
684 BOOST_CHECK_EQUAL( TwoPoints.NextShape( 0 ), -1 );
685 BOOST_CHECK_EQUAL( TwoPoints.NextShape( 1 ), -1 );
686 BOOST_CHECK_EQUAL( TwoPoints.NextShape( -1 ), -1 );
687
688 BOOST_CHECK_EQUAL( ThreePoints.NextShape( 0 ), 1 );
689 BOOST_CHECK_EQUAL( ThreePoints.NextShape( 1 ), -1 );
690 BOOST_CHECK_EQUAL( ThreePoints.NextShape( 2 ), -1 );
691 BOOST_CHECK_EQUAL( ThreePoints.NextShape( -1 ), -1 );
692}
693
694
695
697{
698 BOOST_TEST_CONTEXT( "Case 1: Arc mid point nearly collinear" )
699 {
700 SHAPE_ARC arc( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 2499 ), VECTOR2I( -100000, 0 ), 0 );
701 SHAPE_LINE_CHAIN chain;
702 chain.Append( arc, 5000 );
703 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
704 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
705 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
706 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 100000, 0 ) ); //arc start
707 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( -100000, 0 ) ); //arc end
708 BOOST_CHECK_EQUAL( chain.GetPoint( -1 ), VECTOR2I( -100000, 0 ) ); //arc end
709 }
710
711 BOOST_TEST_CONTEXT( "Case 2: Arc = Large Circle" )
712 {
713 SHAPE_ARC arc( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 0 ), VECTOR2I( 100000, 0 ), 0 );
714 SHAPE_LINE_CHAIN chain;
715 chain.Append( arc, 5000 );
716 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
717 BOOST_CHECK_EQUAL( chain.ArcCount(), 1 );
718 BOOST_CHECK_EQUAL( chain.PointCount(), 10 );
719 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 100000, 0 ) ); //arc start
720 BOOST_CHECK_EQUAL( chain.GetPoint( 9 ), VECTOR2I( 100000, 0 ) ); //arc end
721 BOOST_CHECK_EQUAL( chain.GetPoint( -1 ), VECTOR2I( 100000, 0 ) ); //arc end
722 }
723
724 BOOST_TEST_CONTEXT( "Case 3: Arc = Small Circle (approximate to point)" )
725 {
726 SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 0, 0 ), VECTOR2I( 2499, 0 ), 0 );
727 SHAPE_LINE_CHAIN chain;
728 chain.Append( arc, 5000 );
729 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
730 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
731 BOOST_CHECK_EQUAL( chain.PointCount(), 1 );
732 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
733 }
734
735 BOOST_TEST_CONTEXT( "Case 3: Small Arc (approximate to segment)" )
736 {
737 SHAPE_ARC arc( VECTOR2I( 1767, 0 ), VECTOR2I( 2499, 2499 ), VECTOR2I( 0, 1767 ), 0 );
738 SHAPE_LINE_CHAIN chain;
739 chain.Append( arc, 5000 );
740 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
741 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
742 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
743 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 1767, 0 ) ); //arc start
744 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 0, 1767 ) ); //arc end
745 }
746
747 BOOST_TEST_CONTEXT( "Case 4: Arc = null arc (all points coincident)" )
748 {
749 SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 2499, 0 ), VECTOR2I( 2499, 0 ), 0 );
750 SHAPE_LINE_CHAIN chain;
751 chain.Append( arc, 5000 );
752 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
753 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
754 BOOST_CHECK_EQUAL( chain.PointCount(), 1 );
755 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
756 }
757
758 BOOST_TEST_CONTEXT( "Case 5: Arc = infinite radius (all points very close)" )
759 {
760 SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 2500, 0 ), VECTOR2I( 2501, 0 ), 0 );
761 SHAPE_LINE_CHAIN chain;
762 chain.Append( arc, 5000 );
763 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
764 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
765 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
766 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
767 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 2501, 0 ) ); //arc end
768 }
769
770 BOOST_TEST_CONTEXT( "Case 6: Arc = large radius (all points very close)" )
771 {
772 SHAPE_ARC arc( VECTOR2I( -100000, 0 ), VECTOR2I( 0, 1 ), VECTOR2I( 100000, 0 ), 0 );
773 SHAPE_LINE_CHAIN chain;
774 chain.Append( arc, 5000 );
775 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
776 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
777 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
778 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( -100000, 0 ) ); //arc start
779 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 100000, 0 ) ); //arc end
780 }
781}
782
783
784// Test special case where the last arc in the chain has a shared point with the first arc
785BOOST_AUTO_TEST_CASE( ArcWrappingToStartSharedPoints )
786{
787 // represent a circle with two semicircular arcs
788 SHAPE_ARC arc1( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( -100000, 0 ), 0 );
789 SHAPE_ARC arc2( VECTOR2I( -100000, 0 ), VECTOR2I( 0, -100000 ), VECTOR2I( 100000, 0 ), 0 );
790
791 // Start a chain with the two arcs
792 SHAPE_LINE_CHAIN chain;
793 chain.Append( arc1 );
794 chain.Append( arc2 );
795 BOOST_CHECK_EQUAL( chain.PointCount(), 13 );
796 //BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
797
798 // OPEN CHAIN
799 // Start of the chain is not yet a shared point, so can't be an arc end either
800 BOOST_CHECK_EQUAL( chain.IsSharedPt( 0 ), false );
801 BOOST_CHECK_EQUAL( chain.IsArcEnd( 0 ), false );
802 BOOST_CHECK_EQUAL( chain.IsArcStart( 0 ), true );
803
804 // Index 6 is the shared point between the two arcs in the middle of the chain
805 BOOST_CHECK_EQUAL( chain.IsSharedPt( 6 ), true );
806 BOOST_CHECK_EQUAL( chain.IsArcEnd( 6 ), true );
807 BOOST_CHECK_EQUAL( chain.IsArcStart( 6 ), true );
808
809 // End index is not yet a shared point
810 int endIndex = chain.PointCount() - 1;
811 BOOST_CHECK_EQUAL( chain.IsSharedPt( endIndex ), false );
812 BOOST_CHECK_EQUAL( chain.IsArcEnd( endIndex ), true );
813 BOOST_CHECK_EQUAL( chain.IsArcStart( endIndex ), false );
814
815 for( int i = 0; i < chain.PointCount(); i++ )
816 {
817 BOOST_CHECK_EQUAL( chain.IsPtOnArc( i ), true ); // all points in the chain are arcs
818 }
819
820 // CLOSED CHAIN
821 chain.SetClosed( true );
822 BOOST_CHECK_EQUAL( chain.PointCount(), 12 ); // (-1) should have removed coincident points
823 //BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
824
825 // Start of the chain should be a shared point now, so can't be an arc end either
826 BOOST_CHECK_EQUAL( chain.IsSharedPt( 0 ), true );
827 BOOST_CHECK_EQUAL( chain.IsArcEnd( 0 ), true );
828 BOOST_CHECK_EQUAL( chain.IsArcStart( 0 ), true );
829
830 // Index 6 is the shared point between the two arcs in the middle of the chain
831 BOOST_CHECK_EQUAL( chain.IsSharedPt( 6 ), true );
832 BOOST_CHECK_EQUAL( chain.IsArcEnd( 6 ), true );
833 BOOST_CHECK_EQUAL( chain.IsArcStart( 6 ), true );
834
835 // End index is in the middle of an arc, so not an end point or shared point
836 endIndex = chain.PointCount() - 1;
837 BOOST_CHECK_EQUAL( chain.IsSharedPt( endIndex ), false );
838 BOOST_CHECK_EQUAL( chain.IsArcEnd( endIndex ), false );
839 BOOST_CHECK_EQUAL( chain.IsArcStart( endIndex ), false );
840}
841
842// Test SHAPE_LINE_CHAIN::Split()
844{
845 SEG seg1( VECTOR2I( 0, 100000 ), VECTOR2I( 50000, 0 ) );
846 SEG seg2( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
847 SHAPE_ARC arc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), ANGLE_180 );
848
849 // Start a chain with 2 points (seg1)
850 SHAPE_LINE_CHAIN chain( { seg1.A, seg1.B } );
851 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
852 // Add first arc
853 chain.Append( arc );
854 BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
855 // Add two points (seg2)
856 chain.Append( seg2.A );
857 chain.Append( seg2.B );
858 BOOST_CHECK_EQUAL( chain.PointCount(), 11 );
859 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
860
861 BOOST_TEST_CONTEXT( "Case 1: Point not in the chain" )
862 {
863 SHAPE_LINE_CHAIN chainCopy = chain;
864 BOOST_CHECK_EQUAL( chainCopy.Split( VECTOR2I( 400000, 0 ) ), -1 );
865 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
866 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
867 }
868
869 BOOST_TEST_CONTEXT( "Case 2: Point close to start of a segment" )
870 {
871 SHAPE_LINE_CHAIN chainCopy = chain;
872 VECTOR2I splitPoint = seg1.A + VECTOR2I( 5, -10 );
873 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 1 );
874 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
875 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 1 ), splitPoint );
876 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() + 1 ); // new point added
877 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
878 }
879
880 BOOST_TEST_CONTEXT( "Case 3: Point exactly on the segment" )
881 {
882 SHAPE_LINE_CHAIN chainCopy = chain;
883 VECTOR2I splitPoint = seg1.B;
884 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 1 );
885 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
886 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 1 ), splitPoint );
887 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
888 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
889 }
890
891 BOOST_TEST_CONTEXT( "Case 4: Point at start of arc" )
892 {
893 SHAPE_LINE_CHAIN chainCopy = chain;
894 VECTOR2I splitPoint = arc.GetP0();
895 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 2 );
896 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
897 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 2 ), splitPoint );
898 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
899 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
900 }
901
902 BOOST_TEST_CONTEXT( "Case 5: Point close to start of arc" )
903 {
904 SHAPE_LINE_CHAIN chainCopy = chain;
905 VECTOR2I splitPoint = arc.GetP0() + VECTOR2I( -10, 130 );
906 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 3 );
907 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
908 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 3 ), splitPoint );
909 BOOST_CHECK_EQUAL( chainCopy.IsSharedPt( 3 ), true ); // must be a shared point
910 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() + 1 ); // new point added
911 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() + 1 ); // new arc should have been created
912 }
913}
914
915
916// Test SHAPE_LINE_CHAIN::Slice()
918{
919 SEG targetSegment( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
920 SHAPE_ARC firstArc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), ANGLE_180 );
921 SHAPE_ARC secondArc( VECTOR2I( -200000, -200000 ), VECTOR2I( -300000, -100000 ), -ANGLE_180 );
922 int tol = SHAPE_ARC::DefaultAccuracyForPCB(); // Tolerance for arc collisions
923
924 // Start a chain with 3 points
925 SHAPE_LINE_CHAIN chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( 100000, 0 ) } );
926 BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
927 // Add first arc
928 chain.Append( firstArc );
929 BOOST_CHECK_EQUAL( chain.PointCount(), 10 );
930 // Add two points (target segment)
931 chain.Append( targetSegment.A );
932 chain.Append( targetSegment.B );
933 BOOST_CHECK_EQUAL( chain.PointCount(), 12 );
934 // Add a second arc
935 chain.Append( secondArc );
936 BOOST_CHECK_EQUAL( chain.PointCount(), 20 );
937 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
938
942 BOOST_TEST_CONTEXT( "Case 1: Start at arc endpoint, finish middle of arc" )
943 {
944 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 9, 18 );
945 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
946
947 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
948 SHAPE_ARC expectedSliceArc0;
949 expectedSliceArc0.ConstructFromStartEndCenter( secondArc.GetP0(), chain.GetPoint( 18 ),
950 secondArc.GetCenter(),
951 secondArc.IsClockwise() );
952
953 BOOST_CHECK_EQUAL( sliceResult.Arc( 0 ).GetP0(), expectedSliceArc0.GetP0() ); // equal arc start points
954 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetArcMid(), tol ) );
955 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetP1(), tol ) );
956
957 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 10 );
958 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), firstArc.GetP1() ); // equal to arc end
959 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 1 ), targetSegment.A );
960 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 2 ), targetSegment.B );
961 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 3 ), expectedSliceArc0.GetP0() ); // equal to arc start
962 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 3 ), true );
963
964 for( int i = 4; i <= 8; i++ )
965 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
966
967 for( int i = 3; i <= 7; i++ )
968 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
969
970 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 9 ), true );
971 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 9 ), expectedSliceArc0.GetP1() ); // equal to arc end
972 }
973
977 BOOST_TEST_CONTEXT( "Case 2: Start at middle of an arc, finish at arc startpoint" )
978 {
979 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 5, 12 );
980 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
981
982 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
983 SHAPE_ARC expectedSliceArc0;
984 expectedSliceArc0.ConstructFromStartEndCenter( chain.GetPoint( 5 ), firstArc.GetP1(),
985 firstArc.GetCenter(),
986 firstArc.IsClockwise() );
987
988 BOOST_CHECK_EQUAL( sliceResult.Arc( 0 ).GetP1(),
989 expectedSliceArc0.GetP1() ); // equal arc end points
990 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetArcMid(), tol ) );
991 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetP0(), tol ) );
992
993 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 8 );
994 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ),
995 expectedSliceArc0.GetP0() ); // equal to arc start
996 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
997
998 for( int i = 1; i <= 4; i++ )
999 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
1000
1001 for( int i = 0; i <= 3; i++ )
1002 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
1003
1004 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 4 ), true );
1005 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 4 ),
1006 expectedSliceArc0.GetP1() ); // equal to arc end
1007
1008 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 5 ), targetSegment.A );
1009 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), targetSegment.B );
1010 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 7 ), secondArc.GetP0() );
1011 }
1012
1016 BOOST_TEST_CONTEXT( "Case 3: Full arc, nothing else" )
1017 {
1018 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 3, 9 );
1019 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
1020
1021 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
1022 SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
1023
1024 // Equal arc to original inserted arc
1025 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
1026 BOOST_CHECK_EQUAL( firstArc.GetArcMid(), sliceArc0.GetArcMid() );
1027 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
1028
1029 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 7 );
1030 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), sliceArc0.GetP0() ); // equal to arc start
1031 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
1032
1033 for( int i = 1; i <= 6; i++ )
1034 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
1035
1036 for( int i = 0; i <= 5; i++ )
1037 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
1038
1039 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 6 ), true );
1040 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), sliceArc0.GetP1() ); // equal to arc end
1041 }
1042
1046 BOOST_TEST_CONTEXT( "Case 4: Full arc, and straight segments to next arc start" )
1047 {
1048 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 3, 12 );
1049 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
1050
1051 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
1052 SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
1053
1054 // Equal arc to original inserted arc
1055 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
1056 BOOST_CHECK_EQUAL( firstArc.GetArcMid(), sliceArc0.GetArcMid() );
1057 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
1058
1059 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 10 );
1060 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), sliceArc0.GetP0() ); // equal to arc start
1061 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
1062
1063 for( int i = 1; i <= 6; i++ )
1064 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
1065
1066 for( int i = 0; i <= 5; i++ )
1067 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
1068
1069 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 6 ), true );
1070 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), sliceArc0.GetP1() ); // equal to arc end
1071
1072 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 7 ), targetSegment.A );
1073 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 8 ), targetSegment.B );
1074 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 9 ), secondArc.GetP0() );
1075 }
1076
1077 BOOST_TEST_CONTEXT( "Case 5: Chain ends in arc and point" )
1078 {
1079 SHAPE_LINE_CHAIN chainCopy = chain;
1080 chainCopy.Append( VECTOR2I( 400000, 400000 ) );
1081
1082 SHAPE_LINE_CHAIN sliceResult = chainCopy.Slice( 11, -1 );
1083 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
1084 BOOST_CHECK_EQUAL( sliceResult.GetPoint( -1 ), VECTOR2I( 400000, 400000 ) );
1085 }
1086
1087 BOOST_TEST_CONTEXT( "Case 6: Start to end, chain with one point" )
1088 {
1089 SHAPE_LINE_CHAIN chainCopy = SLC_CASES().OnePoint;
1090
1091 SHAPE_LINE_CHAIN sliceResult = chainCopy.Slice( 0, -1 );
1092 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 1 );
1093 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), VECTOR2I( 233450000, 228360000 ) );
1094 BOOST_CHECK_EQUAL( sliceResult.GetPoint( -1 ), VECTOR2I( 233450000, 228360000 ) ); // Same as index 0
1095 }
1096
1097 BOOST_TEST_CONTEXT( "Case 7: Start to end, chain with two points" )
1098 {
1099 SHAPE_LINE_CHAIN chainCopy = SLC_CASES().TwoPoints;
1100
1101 SHAPE_LINE_CHAIN sliceResult = chainCopy.Slice( 0, -1 );
1102 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 2 );
1103 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), VECTOR2I( 233450000, 228360000 ) );
1104 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 1 ), VECTOR2I( 263450000, 258360000 ) );
1105 BOOST_CHECK_EQUAL( sliceResult.GetPoint( -1 ), VECTOR2I( 263450000, 258360000 ) ); // Same as index 1
1106 }
1107
1108 BOOST_TEST_CONTEXT( "Case 8: Full 2nd arc, nothing else" )
1109 {
1110 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 12, 19 );
1111 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
1112
1113 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
1114 SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
1115
1116 // Equal arc to original inserted arc
1117 BOOST_CHECK_EQUAL( secondArc.GetP1(), sliceArc0.GetP1() );
1118 BOOST_CHECK_EQUAL( secondArc.GetArcMid(), sliceArc0.GetArcMid() );
1119 BOOST_CHECK_EQUAL( secondArc.GetP1(), sliceArc0.GetP1() );
1120
1121 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 8 );
1122 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), sliceArc0.GetP0() ); // equal to arc start
1123 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
1124
1125 for( int i = 1; i <= 7; i++ )
1126 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
1127
1128 for( int i = 0; i <= 6; i++ )
1129 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
1130
1131 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 7 ), true );
1132 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 7 ), sliceArc0.GetP1() ); // equal to arc end
1133 }
1134
1135 BOOST_TEST_CONTEXT( "Case 9: Start at middle of a 2nd arc, finish at end" )
1136 {
1137 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 16, 19 );
1138 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
1139
1140 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
1141
1142 SHAPE_ARC expectedSliceArc0;
1143 expectedSliceArc0.ConstructFromStartEndCenter( chain.GetPoint( 16 ), secondArc.GetP1(),
1144 secondArc.GetCenter(),
1145 secondArc.IsClockwise() );
1146
1147 BOOST_CHECK_EQUAL( sliceResult.Arc( 0 ).GetP1(),
1148 expectedSliceArc0.GetP1() ); // equal arc end points
1149 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetArcMid(), tol ) );
1150 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetP0(), tol ) );
1151
1152 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 4 );
1153 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ),
1154 expectedSliceArc0.GetP0() ); // equal to arc start
1155 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
1156
1157 for( int i = 1; i <= 3; i++ )
1158 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
1159
1160 for( int i = 0; i <= 2; i++ )
1161 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
1162
1163 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 3 ), true );
1164 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 3 ),
1165 expectedSliceArc0.GetP1() ); // equal to arc end
1166 }
1167
1168 BOOST_TEST_CONTEXT( "Case 10: New chain, start at arc middle, finish at end" )
1169 {
1170 SHAPE_LINE_CHAIN chain10;
1171 chain10.Append( firstArc );
1172
1173 SHAPE_LINE_CHAIN sliceResult = chain10.Slice( 3, 6 );
1174 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
1175
1176 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
1177
1178 SHAPE_ARC expectedSliceArc0;
1179 expectedSliceArc0.ConstructFromStartEndCenter( chain10.GetPoint( 3 ), firstArc.GetP1(),
1180 firstArc.GetCenter(),
1181 firstArc.IsClockwise() );
1182
1183 BOOST_CHECK_EQUAL( sliceResult.Arc( 0 ).GetP1(),
1184 expectedSliceArc0.GetP1() ); // equal arc end points
1185 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetArcMid(), tol ) );
1186 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetP0(), tol ) );
1187
1188 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 4 );
1189 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ),
1190 expectedSliceArc0.GetP0() ); // equal to arc start
1191 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
1192
1193 for( int i = 1; i <= 3; i++ )
1194 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
1195
1196 for( int i = 0; i <= 2; i++ )
1197 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
1198
1199 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 3 ), true );
1200 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 3 ),
1201 expectedSliceArc0.GetP1() ); // equal to arc end
1202 }
1203}
1204
1205
1206// Test SHAPE_LINE_CHAIN::NearestPoint( VECTOR2I )
1207BOOST_AUTO_TEST_CASE( NearestPointPt )
1208{
1209 SEG seg1( VECTOR2I( 0, 100000 ), VECTOR2I( 50000, 0 ) );
1210 SEG seg2( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
1211 SHAPE_ARC arc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), ANGLE_180 );
1212
1213 // Start a chain with 2 points (seg1)
1214 SHAPE_LINE_CHAIN chain( { seg1.A, seg1.B } );
1215 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
1216 // Add first arc
1217 chain.Append( arc );
1218 BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
1219 // Add two points (seg2)
1220 chain.Append( seg2.A );
1221 chain.Append( seg2.B );
1222 BOOST_CHECK_EQUAL( chain.PointCount(), 11 );
1223 BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
1224
1225 VECTOR2I ptOnArcCloseToStart( 297553, 31697 ); //should be index 3 in chain
1226 VECTOR2I ptOnArcCloseToEnd( 139709, 82983 ); //should be index 6 in chain
1227
1228 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToStart, true ), ptOnArcCloseToStart );
1229 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToStart, false ), arc.GetP0() );
1230
1231 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToEnd, true ), ptOnArcCloseToEnd );
1232 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToEnd, false ), arc.GetP1() );
1233}
1234
1235
1236// Test SHAPE_LINE_CHAIN::Replace( SHAPE_LINE_CHAIN )
1238{
1239 BOOST_TEST_INFO( "8949 crash" );
1240
1241 std::vector<VECTOR2I> linePts = {
1242 { 206000000, 140110000 }, { 192325020, 140110000 }, { 192325020, 113348216 },
1243 { 192251784, 113274980 }, { 175548216, 113274980 }, { 175474980, 113348216 },
1244 { 175474980, 136694980 }, { 160774511, 121994511 }, { 160774511, 121693501 },
1245 { 160086499, 121005489 }, { 159785489, 121005489 }, { 159594511, 120814511 },
1246 { 160086499, 120814511 }, { 160774511, 120126499 }, { 160774511, 119153501 },
1247 { 160086499, 118465489 }, { 159113501, 118465489 }, { 158425489, 119153501 },
1248 { 158425489, 119645489 }, { 157325020, 118545020 }, { 157325020, 101925020 },
1249 { 208674980, 101925020 }, { 208674980, 145474980 }, { 192325020, 145474980 },
1250 { 192325020, 140110000 }
1251 };
1252
1253 SHAPE_LINE_CHAIN baseChain( linePts, false );
1254 baseChain.SetWidth( 250000 );
1255 BOOST_CHECK_EQUAL( baseChain.PointCount(), linePts.size() );
1256
1257 SHAPE_LINE_CHAIN replaceChain( { VECTOR2I( 192325020, 140110000 ) }, false );
1258 BOOST_CHECK_EQUAL( replaceChain.PointCount(), 1 );
1259
1260 baseChain.Replace( 1, 23, replaceChain );
1261
1262 BOOST_CHECK_EQUAL( baseChain.PointCount(), linePts.size() - ( 23 - 1 ) );
1263
1264 // Replacing the last point in a chain is special-cased
1265 baseChain.Replace( baseChain.PointCount() - 1, baseChain.PointCount() - 1, VECTOR2I( -1, -1 ) );
1266
1267 BOOST_CHECK_EQUAL( baseChain.CLastPoint(), VECTOR2I( -1, -1 ) );
1268}
1269
1270
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:116
bool IsClockwise() const
Definition: shape_arc.h:273
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.
Definition: shape_arc.cpp:212
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,...
Definition: shape_arc.cpp:244
static double DefaultAccuracyForPCB()
Definition: shape_arc.h:232
const VECTOR2I & GetP0() const
Definition: shape_arc.h:114
const VECTOR2I & GetCenter() const
Definition: shape_arc.cpp:523
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsPtOnArc(size_t aPtIndex) const
const SHAPE_ARC & Arc(size_t aArc) const
bool IsClosed() const override
virtual const VECTOR2I GetPoint(int aIndex) const override
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.
void Simplify(int aMaxError=0)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
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.
const SHAPE_LINE_CHAIN Slice(int aStartIndex, int aEndIndex=-1) const
Return a subset of this line chain containing the [start_index, end_index] range of points.
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
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
void SetWidth(int aWidth)
Set the width of all segments in the chain.
bool IsSharedPt(size_t aIndex) const
Test if a point is shared between multiple shapes.
const std::vector< VECTOR2I > & CPoints() const
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
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_SUITE_END()
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
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691