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
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
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
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 );
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
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
370// Test that duplicate point gets removed when we call simplify
371BOOST_AUTO_TEST_CASE( SimplifyDuplicatePoint )
372{
373 SHAPE_LINE_CHAIN chain;
374
375 chain.Append( { 100, 100 } );
376 chain.Append( { 100, 100 }, true ); //duplicate point to simplify
377 chain.Append( { 200, 100 } );
378
380 BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
381
382 chain.Simplify();
383
384 BOOST_CHECK_EQUAL( chain.CPoints().size(), chain.CShapes().size() );
385 BOOST_CHECK_EQUAL( chain.PointCount(), 2 ); // (-1) should have removed coincident points
387}
388
389
390// Test that duplicate point gets removed when we call simplify
391BOOST_AUTO_TEST_CASE( SimplifyKeepEndPoint )
392{
393 SHAPE_LINE_CHAIN chain;
394
395 chain.Append( { 114772424, 90949410 } );
396 chain.Append( { 114767360, 90947240 } );
397 chain.Append( { 114772429, 90947228 } );
398 chain.SetClosed( true );
399
401 BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
402
403 chain.Simplify();
404
405 BOOST_CHECK_EQUAL( chain.CPoints().size(), chain.CShapes().size() );
406 BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
408}
409
410
412{
413 std::string m_ctx_name;
420};
421
422static const std::vector<REMOVE_SHAPE_CASE> remove_shape_cases =
423 {
424 { "Circle1Arc - 1st arc - index on start", SLC_CASES().Circle1Arc, 1, 1, 0, 0, 0 },
425 { "Circle1Arc - 1st arc - index on mid", SLC_CASES().Circle1Arc, 1, 1, 8, 0, 0 },
426 { "Circle1Arc - 1st arc - index on end", SLC_CASES().Circle1Arc, 1, 1, 14, 0, 0 },
427 { "Circle1Arc - 1st arc - index on -1", SLC_CASES().Circle1Arc, 1, 1, -1, 0, 0 },
428 { "Circle1Arc - invalid index", SLC_CASES().Circle1Arc, 1, 1, 15, 1, 1 },
429
430 { "Circle2Arcs - 1st arc - index on start", SLC_CASES().Circle2Arcs, 2, 2, 0, 2, 1 },
431 { "Circle2Arcs - 1st arc - index on mid", SLC_CASES().Circle2Arcs, 2, 2, 3, 2, 1 },
432 { "Circle2Arcs - 1st arc - index on end", SLC_CASES().Circle2Arcs, 2, 2, 7, 2, 1 },
433 { "Circle2Arcs - 2nd arc - index on start", SLC_CASES().Circle2Arcs, 2, 2, 8, 2, 1 },
434 { "Circle2Arcs - 2nd arc - index on mid", SLC_CASES().Circle2Arcs, 2, 2, 11, 2, 1 },
435 { "Circle2Arcs - 2nd arc - index on end", SLC_CASES().Circle2Arcs, 2, 2, 15, 2, 1 },
436 { "Circle2Arcs - 2nd arc - index on -1", SLC_CASES().Circle2Arcs, 2, 2, -1, 2, 1 },
437 { "Circle2Arcs - invalid index", SLC_CASES().Circle2Arcs, 2, 2, 16, 2, 2 },
438
439 { "ArcsCoinc. - 1st arc - idx on start", SLC_CASES().ArcsCoincident, 2, 2, 0, 1, 1 },
440 { "ArcsCoinc. - 1st arc - idx on mid", SLC_CASES().ArcsCoincident, 2, 2, 3, 1, 1 },
441 { "ArcsCoinc. - 1st arc - idx on end", SLC_CASES().ArcsCoincident, 2, 2, 7, 1, 1 },
442 { "ArcsCoinc. - 2nd arc - idx on start", SLC_CASES().ArcsCoincident, 2, 2, 8, 1, 1 },
443 { "ArcsCoinc. - 2nd arc - idx on mid", SLC_CASES().ArcsCoincident, 2, 2, 10, 1, 1 },
444 { "ArcsCoinc. - 2nd arc - idx on end", SLC_CASES().ArcsCoincident, 2, 2, 13, 1, 1 },
445 { "ArcsCoinc. - 2nd arc - idx on -1", SLC_CASES().ArcsCoincident, 2, 2, -1, 1, 1 },
446 { "ArcsCoinc. - invalid idx", SLC_CASES().ArcsCoincident, 2, 2, 14, 2, 2 },
447 { "ArcsCoinc. - 1st arc - idx on start", SLC_CASES().ArcsCoincident, 2, 2, 0, 1, 1 },
448
449 { "A.Co.Closed - 1st arc - idx on start", SLC_CASES().ArcsCoincidentClosed, 3, 2, 1, 2, 1 },
450 { "A.Co.Closed - 1st arc - idx on mid", SLC_CASES().ArcsCoincidentClosed, 3, 2, 3, 2, 1 },
451 { "A.Co.Closed - 1st arc - idx on end", SLC_CASES().ArcsCoincidentClosed, 3, 2, 7, 2, 1 },
452 { "A.Co.Closed - 2nd arc - idx on start", SLC_CASES().ArcsCoincidentClosed, 3, 2, 8, 2, 1 },
453 { "A.Co.Closed - 2nd arc - idx on mid", SLC_CASES().ArcsCoincidentClosed, 3, 2, 10, 2, 1 },
454 { "A.Co.Closed - 2nd arc - idx on end", SLC_CASES().ArcsCoincidentClosed, 3, 2, 13, 2, 1 },
455 { "A.Co.Closed - 2nd arc - idx on -1", SLC_CASES().ArcsCoincidentClosed, 3, 2, -1, 2, 1 },
456 { "A.Co.Closed - invalid idx", SLC_CASES().ArcsCoincidentClosed, 3, 2, 14, 3, 2 },
457
458 { "ArcsIndep. - 1st arc - idx on start", SLC_CASES().ArcsIndependent, 3, 2, 0, 1, 1 },
459 { "ArcsIndep. - 1st arc - idx on mid", SLC_CASES().ArcsIndependent, 3, 2, 3, 1, 1 },
460 { "ArcsIndep. - 1st arc - idx on end", SLC_CASES().ArcsIndependent, 3, 2, 8, 1, 1 },
461 { "ArcsIndep. - 2nd arc - idx on start", SLC_CASES().ArcsIndependent, 3, 2, 9, 1, 1 },
462 { "ArcsIndep. - 2nd arc - idx on mid", SLC_CASES().ArcsIndependent, 3, 2, 12, 1, 1 },
463 { "ArcsIndep. - 2nd arc - idx on end", SLC_CASES().ArcsIndependent, 3, 2, 17, 1, 1 },
464 { "ArcsIndep. - 2nd arc - idx on -1", SLC_CASES().ArcsIndependent, 3, 2, -1, 1, 1 },
465 { "ArcsIndep. - invalid idx", SLC_CASES().ArcsIndependent, 3, 2, 18, 3, 2 },
466
467 { "Dup.Arcs - 1st arc - idx on start", SLC_CASES().DuplicateArcs, 4, 3, 0, 3, 2 },
468 { "Dup.Arcs - 1st arc - idx on mid", SLC_CASES().DuplicateArcs, 4, 3, 3, 3, 2 },
469 { "Dup.Arcs - 1st arc - idx on end", SLC_CASES().DuplicateArcs, 4, 3, 7, 3, 2 },
470 { "Dup.Arcs - 2nd arc - idx on start", SLC_CASES().DuplicateArcs, 4, 3, 8, 3, 2 },
471 { "Dup.Arcs - 2nd arc - idx on mid", SLC_CASES().DuplicateArcs, 4, 3, 10, 3, 2 },
472 { "Dup.Arcs - 2nd arc - idx on end", SLC_CASES().DuplicateArcs, 4, 3, 13, 3, 2 },
473 { "Dup.Arcs - 3rd arc - idx on start", SLC_CASES().DuplicateArcs, 4, 3, 14, 2, 2 },
474 { "Dup.Arcs - 3rd arc - idx on mid", SLC_CASES().DuplicateArcs, 4, 3, 17, 2, 2 },
475 { "Dup.Arcs - 3rd arc - idx on end", SLC_CASES().DuplicateArcs, 4, 3, 19, 2, 2 },
476 { "Dup.Arcs - 3rd arc - idx on -1", SLC_CASES().DuplicateArcs, 4, 3, -1, 2, 2 },
477 { "Dup.Arcs - invalid idx", SLC_CASES().DuplicateArcs, 4, 3, 20, 4, 3 },
478
479 { "Arcs Mixed - 1st arc - idx on start", SLC_CASES().ArcsAndSegMixed, 4, 2, 0, 2, 1 },
480 { "Arcs Mixed - 1st arc - idx on mid", SLC_CASES().ArcsAndSegMixed, 4, 2, 3, 2, 1 },
481 { "Arcs Mixed - 1st arc - idx on end", SLC_CASES().ArcsAndSegMixed, 4, 2, 8, 2, 1 },
482 { "Arcs Mixed - Straight segment", SLC_CASES().ArcsAndSegMixed, 4, 2, 9, 3, 2 },
483 { "Arcs Mixed - 2nd arc - idx on start", SLC_CASES().ArcsAndSegMixed, 4, 2, 10, 2, 1 },
484 { "Arcs Mixed - 2nd arc - idx on mid", SLC_CASES().ArcsAndSegMixed, 4, 2, 14, 2, 1 },
485 { "Arcs Mixed - 2nd arc - idx on end", SLC_CASES().ArcsAndSegMixed, 4, 2, 18, 2, 1 },
486 { "Arcs Mixed - 2nd arc - idx on -1", SLC_CASES().ArcsAndSegMixed, 4, 2, -1, 2, 1 },
487 { "Arcs Mixed - invalid idx", SLC_CASES().ArcsAndSegMixed, 4, 2, 19, 4, 2 }
488 };
489
490
492{
493 for( const REMOVE_SHAPE_CASE& c : remove_shape_cases )
494 {
495 BOOST_TEST_CONTEXT( c.m_ctx_name )
496 {
497 SHAPE_LINE_CHAIN slc_case = c.m_chain; // make a copy to edit
498 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
499 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_arc_count );
500 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
501 slc_case.RemoveShape( c.m_remove_index );
502 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_expected_shape_count );
503 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_expected_arc_count );
504 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
505 }
506 }
507}
508
509
510BOOST_AUTO_TEST_CASE( RemoveShapeAfterSimplify )
511{
512 for( const REMOVE_SHAPE_CASE& c : remove_shape_cases )
513 {
514 BOOST_TEST_CONTEXT( c.m_ctx_name )
515 {
516 SHAPE_LINE_CHAIN slc_case = c.m_chain; // make a copy to edit
517 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
518 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
519 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_arc_count );
520 slc_case.Simplify();
521 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
522 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_shape_count );
523 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_arc_count );
524 slc_case.RemoveShape( c.m_remove_index );
525 BOOST_CHECK_EQUAL( GEOM_TEST::IsOutlineValid( slc_case ), true );
526 BOOST_CHECK_EQUAL( slc_case.ShapeCount(), c.m_expected_shape_count );
527 BOOST_CHECK_EQUAL( slc_case.ArcCount(), c.m_expected_arc_count );
528 }
529 }
530}
531
532
534{
535 BOOST_CHECK_EQUAL( Circle1Arc.ShapeCount(), 1 );
536 BOOST_CHECK_EQUAL( Circle2Arcs.ShapeCount(), 2 );
537 BOOST_CHECK_EQUAL( ArcsCoincident.ShapeCount(), 2 );
538 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.ShapeCount(), 3 );
539 BOOST_CHECK_EQUAL( DuplicateArcs.ShapeCount(), 4 );
540 BOOST_CHECK_EQUAL( ArcAndPoint.ShapeCount(), 2 );
541 BOOST_CHECK_EQUAL( ArcsAndSegMixed.ShapeCount(), 4 );
542 BOOST_CHECK_EQUAL( SegAndArcCoincident.ShapeCount(), 2 );
543 BOOST_CHECK_EQUAL( EmptyChain.ShapeCount(), 0 );
544 BOOST_CHECK_EQUAL( OnePoint.ShapeCount(), 0 );
545 BOOST_CHECK_EQUAL( TwoPoints.ShapeCount(), 1 );
546 BOOST_CHECK_EQUAL( ThreePoints.ShapeCount(), 2 );
547}
548
549
551{
552 BOOST_CHECK_EQUAL( Circle1Arc.NextShape( 0 ), -1 ); //only one arc
553
554 BOOST_CHECK_EQUAL( Circle2Arcs.NextShape( 0 ), 8 ); // next shape "Arc0b"
555 BOOST_CHECK_EQUAL( Circle2Arcs.NextShape( 8 ), -1 ); //no more shapes (last point joins with first, part of arc)
556
557 BOOST_CHECK_EQUAL( ArcsCoincident.NextShape( 0 ), 8 ); // next shape "Arc1"
558 BOOST_CHECK_EQUAL( ArcsCoincident.NextShape( 8 ), -1 ); //no more shapes
559
560 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.NextShape( 0 ), 8 ); // next shape "Arc1"
561 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.NextShape( 8 ), 13 ); //next shape is hidden segment joining last/first
562 BOOST_CHECK_EQUAL( ArcsCoincidentClosed.NextShape( 13 ), -1 ); //no more shapes
563
564 BOOST_CHECK_EQUAL( ArcsIndependent.NextShape( 0 ), 8 ); // next shape straight seg
565 BOOST_CHECK_EQUAL( ArcsIndependent.NextShape( 8 ), 9 ); //next shape second arc
566 BOOST_CHECK_EQUAL( ArcsIndependent.NextShape( 9 ), -1 ); //no more shapes
567
568 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 0 ), 8 ); // next shape "Arc1"
569 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 8 ), 13 ); // next shape hidden segment joining the 2 duplicate arcs
570 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 13 ), 14 ); // next shape "Arc1" (duplicate)
571 BOOST_CHECK_EQUAL( DuplicateArcs.NextShape( 14 ), -1 ); //no more shapes
572
573 BOOST_CHECK_EQUAL( ArcAndPoint.NextShape( 0 ), 8 ); // next shape straight segment (end of arc->point)
574 BOOST_CHECK_EQUAL( ArcAndPoint.NextShape( 8 ), -1 ); //no more shapes
575
576 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 0 ), 8 ); // next shape straight segment (end of arc->point)
577 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 8 ), 9 ); // next shape straight segment (point->begining of arc)
578 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 9 ), 10 ); //next shape second arc
579 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 10 ), -1 ); //no more shapes
580 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( 20 ), -1 ); //invalid indices should still work
581 BOOST_CHECK_EQUAL( ArcsAndSegMixed.NextShape( -50 ), -1 ); //invalid indices should still work
582
583 BOOST_CHECK_EQUAL( SegAndArcCoincident.NextShape( 0 ), 1 ); // next shape Arc3
584 BOOST_CHECK_EQUAL( SegAndArcCoincident.NextShape( 1 ), -1 ); //no more shapes
585
586 BOOST_CHECK_EQUAL( EmptyChain.NextShape( 0 ), -1 );
587 BOOST_CHECK_EQUAL( EmptyChain.NextShape( 1 ), -1 ); //invalid indices should still work
588 BOOST_CHECK_EQUAL( EmptyChain.NextShape( 2 ), -1 ); //invalid indices should still work
589 BOOST_CHECK_EQUAL( EmptyChain.NextShape( -2 ), -1 ); //invalid indices should still work
590
591 BOOST_CHECK_EQUAL( OnePoint.NextShape( 0 ), -1 );
592 BOOST_CHECK_EQUAL( OnePoint.NextShape( -1 ), -1 );
593 BOOST_CHECK_EQUAL( OnePoint.NextShape( 1 ), -1 ); //invalid indices should still work
594 BOOST_CHECK_EQUAL( OnePoint.NextShape( 2 ), -1 ); //invalid indices should still work
595 BOOST_CHECK_EQUAL( OnePoint.NextShape( -2 ), -1 ); //invalid indices should still work
596
597 BOOST_CHECK_EQUAL( TwoPoints.NextShape( 0 ), -1 );
598 BOOST_CHECK_EQUAL( TwoPoints.NextShape( 1 ), -1 );
599 BOOST_CHECK_EQUAL( TwoPoints.NextShape( -1 ), -1 );
600
601 BOOST_CHECK_EQUAL( ThreePoints.NextShape( 0 ), 1 );
602 BOOST_CHECK_EQUAL( ThreePoints.NextShape( 1 ), -1 );
603 BOOST_CHECK_EQUAL( ThreePoints.NextShape( 2 ), -1 );
604 BOOST_CHECK_EQUAL( ThreePoints.NextShape( -1 ), -1 );
605}
606
607
608
610{
611 BOOST_TEST_CONTEXT( "Case 1: Arc mid point nearly collinear" )
612 {
613 SHAPE_ARC arc( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 2499 ), VECTOR2I( -100000, 0 ), 0 );
614 SHAPE_LINE_CHAIN chain;
615 chain.Append( arc, 5000 );
617 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
618 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
619 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 100000, 0 ) ); //arc start
620 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( -100000, 0 ) ); //arc end
621 BOOST_CHECK_EQUAL( chain.GetPoint( -1 ), VECTOR2I( -100000, 0 ) ); //arc end
622 }
623
624 BOOST_TEST_CONTEXT( "Case 2: Arc = Large Circle" )
625 {
626 SHAPE_ARC arc( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 0 ), VECTOR2I( 100000, 0 ), 0 );
627 SHAPE_LINE_CHAIN chain;
628 chain.Append( arc, 5000 );
630 BOOST_CHECK_EQUAL( chain.ArcCount(), 1 );
631 BOOST_CHECK_EQUAL( chain.PointCount(), 10 );
632 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 100000, 0 ) ); //arc start
633 BOOST_CHECK_EQUAL( chain.GetPoint( 9 ), VECTOR2I( 100000, 0 ) ); //arc end
634 BOOST_CHECK_EQUAL( chain.GetPoint( -1 ), VECTOR2I( 100000, 0 ) ); //arc end
635 }
636
637 BOOST_TEST_CONTEXT( "Case 3: Arc = Small Circle (approximate to point)" )
638 {
639 SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 0, 0 ), VECTOR2I( 2499, 0 ), 0 );
640 SHAPE_LINE_CHAIN chain;
641 chain.Append( arc, 5000 );
643 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
644 BOOST_CHECK_EQUAL( chain.PointCount(), 1 );
645 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
646 }
647
648 BOOST_TEST_CONTEXT( "Case 3: Small Arc (approximate to segment)" )
649 {
650 SHAPE_ARC arc( VECTOR2I( 1767, 0 ), VECTOR2I( 2499, 2499 ), VECTOR2I( 0, 1767 ), 0 );
651 SHAPE_LINE_CHAIN chain;
652 chain.Append( arc, 5000 );
654 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
655 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
656 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 1767, 0 ) ); //arc start
657 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 0, 1767 ) ); //arc end
658 }
659
660 BOOST_TEST_CONTEXT( "Case 4: Arc = null arc (all points coincident)" )
661 {
662 SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 2499, 0 ), VECTOR2I( 2499, 0 ), 0 );
663 SHAPE_LINE_CHAIN chain;
664 chain.Append( arc, 5000 );
666 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
667 BOOST_CHECK_EQUAL( chain.PointCount(), 1 );
668 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
669 }
670
671 BOOST_TEST_CONTEXT( "Case 5: Arc = infinite radius (all points very close)" )
672 {
673 SHAPE_ARC arc( VECTOR2I( 2499, 0 ), VECTOR2I( 2500, 0 ), VECTOR2I( 2501, 0 ), 0 );
674 SHAPE_LINE_CHAIN chain;
675 chain.Append( arc, 5000 );
677 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
678 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
679 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( 2499, 0 ) ); //arc start
680 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 2501, 0 ) ); //arc end
681 }
682
683 BOOST_TEST_CONTEXT( "Case 6: Arc = large radius (all points very close)" )
684 {
685 SHAPE_ARC arc( VECTOR2I( -100000, 0 ), VECTOR2I( 0, 1 ), VECTOR2I( 100000, 0 ), 0 );
686 SHAPE_LINE_CHAIN chain;
687 chain.Append( arc, 5000 );
689 BOOST_CHECK_EQUAL( chain.ArcCount(), 0 );
690 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
691 BOOST_CHECK_EQUAL( chain.GetPoint( 0 ), VECTOR2I( -100000, 0 ) ); //arc start
692 BOOST_CHECK_EQUAL( chain.GetPoint( 1 ), VECTOR2I( 100000, 0 ) ); //arc end
693 }
694}
695
696
697// Test special case where the last arc in the chain has a shared point with the first arc
698BOOST_AUTO_TEST_CASE( ArcWrappingToStartSharedPoints )
699{
700 // represent a circle with two semicircular arcs
701 SHAPE_ARC arc1( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( -100000, 0 ), 0 );
702 SHAPE_ARC arc2( VECTOR2I( -100000, 0 ), VECTOR2I( 0, -100000 ), VECTOR2I( 100000, 0 ), 0 );
703
704 // Start a chain with the two arcs
705 SHAPE_LINE_CHAIN chain;
706 chain.Append( arc1 );
707 chain.Append( arc2 );
708 BOOST_CHECK_EQUAL( chain.PointCount(), 13 );
709 //BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
710
711 // OPEN CHAIN
712 // Start of the chain is not yet a shared point, so can't be an arc end either
713 BOOST_CHECK_EQUAL( chain.IsSharedPt( 0 ), false );
714 BOOST_CHECK_EQUAL( chain.IsArcEnd( 0 ), false );
715 BOOST_CHECK_EQUAL( chain.IsArcStart( 0 ), true );
716
717 // Index 6 is the shared point between the two arcs in the middle of the chain
718 BOOST_CHECK_EQUAL( chain.IsSharedPt( 6 ), true );
719 BOOST_CHECK_EQUAL( chain.IsArcEnd( 6 ), true );
720 BOOST_CHECK_EQUAL( chain.IsArcStart( 6 ), true );
721
722 // End index is not yet a shared point
723 int endIndex = chain.PointCount() - 1;
724 BOOST_CHECK_EQUAL( chain.IsSharedPt( endIndex ), false );
725 BOOST_CHECK_EQUAL( chain.IsArcEnd( endIndex ), true );
726 BOOST_CHECK_EQUAL( chain.IsArcStart( endIndex ), false );
727
728 for( int i = 0; i < chain.PointCount(); i++ )
729 {
730 BOOST_CHECK_EQUAL( chain.IsPtOnArc( i ), true ); // all points in the chain are arcs
731 }
732
733 // CLOSED CHAIN
734 chain.SetClosed( true );
735 BOOST_CHECK_EQUAL( chain.PointCount(), 12 ); // (-1) should have removed coincident points
736 //BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
737
738 // Start of the chain should be a shared point now, so can't be an arc end either
739 BOOST_CHECK_EQUAL( chain.IsSharedPt( 0 ), true );
740 BOOST_CHECK_EQUAL( chain.IsArcEnd( 0 ), true );
741 BOOST_CHECK_EQUAL( chain.IsArcStart( 0 ), true );
742
743 // Index 6 is the shared point between the two arcs in the middle of the chain
744 BOOST_CHECK_EQUAL( chain.IsSharedPt( 6 ), true );
745 BOOST_CHECK_EQUAL( chain.IsArcEnd( 6 ), true );
746 BOOST_CHECK_EQUAL( chain.IsArcStart( 6 ), true );
747
748 // End index is in the middle of an arc, so not an end point or shared point
749 endIndex = chain.PointCount() - 1;
750 BOOST_CHECK_EQUAL( chain.IsSharedPt( endIndex ), false );
751 BOOST_CHECK_EQUAL( chain.IsArcEnd( endIndex ), false );
752 BOOST_CHECK_EQUAL( chain.IsArcStart( endIndex ), false );
753}
754
755// Test SHAPE_LINE_CHAIN::Split()
757{
758 SEG seg1( VECTOR2I( 0, 100000 ), VECTOR2I( 50000, 0 ) );
759 SEG seg2( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
760 SHAPE_ARC arc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), ANGLE_180 );
761
762 // Start a chain with 2 points (seg1)
763 SHAPE_LINE_CHAIN chain( { seg1.A, seg1.B } );
764 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
765 // Add first arc
766 chain.Append( arc );
767 BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
768 // Add two points (seg2)
769 chain.Append( seg2.A );
770 chain.Append( seg2.B );
771 BOOST_CHECK_EQUAL( chain.PointCount(), 11 );
773
774 BOOST_TEST_CONTEXT( "Case 1: Point not in the chain" )
775 {
776 SHAPE_LINE_CHAIN chainCopy = chain;
777 BOOST_CHECK_EQUAL( chainCopy.Split( VECTOR2I( 400000, 0 ) ), -1 );
778 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
779 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
780 }
781
782 BOOST_TEST_CONTEXT( "Case 2: Point close to start of a segment" )
783 {
784 SHAPE_LINE_CHAIN chainCopy = chain;
785 VECTOR2I splitPoint = seg1.A + VECTOR2I( 5, -10 );
786 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 1 );
788 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 1 ), splitPoint );
789 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() + 1 ); // new point added
790 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
791 }
792
793 BOOST_TEST_CONTEXT( "Case 3: Point exactly on the segment" )
794 {
795 SHAPE_LINE_CHAIN chainCopy = chain;
796 VECTOR2I splitPoint = seg1.B;
797 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 1 );
799 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 1 ), splitPoint );
800 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
801 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
802 }
803
804 BOOST_TEST_CONTEXT( "Case 4: Point at start of arc" )
805 {
806 SHAPE_LINE_CHAIN chainCopy = chain;
807 VECTOR2I splitPoint = arc.GetP0();
808 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 2 );
810 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 2 ), splitPoint );
811 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
812 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
813 }
814
815 BOOST_TEST_CONTEXT( "Case 5: Point close to start of arc" )
816 {
817 SHAPE_LINE_CHAIN chainCopy = chain;
818 VECTOR2I splitPoint = arc.GetP0() + VECTOR2I( -10, 130 );
819 BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 3 );
821 BOOST_CHECK_EQUAL( chainCopy.GetPoint( 3 ), splitPoint );
822 BOOST_CHECK_EQUAL( chainCopy.IsSharedPt( 3 ), true ); // must be a shared point
823 BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() + 1 ); // new point added
824 BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() + 1 ); // new arc should have been created
825 }
826}
827
828
829// Test SHAPE_LINE_CHAIN::Slice()
831{
832 SEG targetSegment( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
833 SHAPE_ARC firstArc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), ANGLE_180 );
834 SHAPE_ARC secondArc( VECTOR2I( -200000, -200000 ), VECTOR2I( -300000, -100000 ), -ANGLE_180 );
835 int tol = SHAPE_ARC::DefaultAccuracyForPCB(); // Tolerance for arc collisions
836
837 // Start a chain with 3 points
838 SHAPE_LINE_CHAIN chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( 100000, 0 ) } );
839 BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
840 // Add first arc
841 chain.Append( firstArc );
842 BOOST_CHECK_EQUAL( chain.PointCount(), 10 );
843 // Add two points (target segment)
844 chain.Append( targetSegment.A );
845 chain.Append( targetSegment.B );
846 BOOST_CHECK_EQUAL( chain.PointCount(), 12 );
847 // Add a second arc
848 chain.Append( secondArc );
849 BOOST_CHECK_EQUAL( chain.PointCount(), 20 );
851
855 BOOST_TEST_CONTEXT( "Case 1: Start at arc endpoint, finish middle of arc" )
856 {
857 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 9, 18 );
858 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
859
860 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
861 SHAPE_ARC expectedSliceArc0;
862 expectedSliceArc0.ConstructFromStartEndCenter( secondArc.GetP0(), chain.GetPoint( 18 ),
863 secondArc.GetCenter(),
864 secondArc.IsClockwise() );
865
866 BOOST_CHECK_EQUAL( sliceResult.Arc( 0 ).GetP0(), expectedSliceArc0.GetP0() ); // equal arc start points
867 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetArcMid(), tol ) );
868 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetP1(), tol ) );
869
870 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 10 );
871 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), firstArc.GetP1() ); // equal to arc end
872 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 1 ), targetSegment.A );
873 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 2 ), targetSegment.B );
874 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 3 ), expectedSliceArc0.GetP0() ); // equal to arc start
875 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 3 ), true );
876
877 for( int i = 4; i <= 8; i++ )
878 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
879
880 for( int i = 3; i <= 7; i++ )
881 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
882
883 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 9 ), true );
884 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 9 ), expectedSliceArc0.GetP1() ); // equal to arc end
885 }
886
890 BOOST_TEST_CONTEXT( "Case 2: Start at middle of an arc, finish at arc startpoint" )
891 {
892 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 5, 12 );
893 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
894
895 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
896 SHAPE_ARC expectedSliceArc0;
897 expectedSliceArc0.ConstructFromStartEndCenter( chain.GetPoint( 5 ), firstArc.GetP1(),
898 firstArc.GetCenter(),
899 firstArc.IsClockwise() );
900
901 BOOST_CHECK_EQUAL( sliceResult.Arc( 0 ).GetP1(),
902 expectedSliceArc0.GetP1() ); // equal arc end points
903 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetArcMid(), tol ) );
904 BOOST_CHECK( sliceResult.Arc( 0 ).Collide( expectedSliceArc0.GetP0(), tol ) );
905
906 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 8 );
907 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ),
908 expectedSliceArc0.GetP0() ); // equal to arc start
909 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
910
911 for( int i = 1; i <= 4; i++ )
912 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
913
914 for( int i = 0; i <= 3; i++ )
915 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
916
917 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 4 ), true );
918 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 4 ),
919 expectedSliceArc0.GetP1() ); // equal to arc end
920
921 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 5 ), targetSegment.A );
922 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), targetSegment.B );
923 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 7 ), secondArc.GetP0() );
924 }
925
929 BOOST_TEST_CONTEXT( "Case 3: Full arc, nothing else" )
930 {
931 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 3, 9 );
932 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
933
934 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
935 SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
936
937 // Equal arc to original inserted arc
938 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
939 BOOST_CHECK_EQUAL( firstArc.GetArcMid(), sliceArc0.GetArcMid() );
940 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
941
942 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 7 );
943 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), sliceArc0.GetP0() ); // equal to arc start
944 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
945
946 for( int i = 1; i <= 6; i++ )
947 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
948
949 for( int i = 0; i <= 5; i++ )
950 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
951
952 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 6 ), true );
953 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), sliceArc0.GetP1() ); // equal to arc end
954 }
955
959 BOOST_TEST_CONTEXT( "Case 4: Full arc, and straight segments to next arc start" )
960 {
961 SHAPE_LINE_CHAIN sliceResult = chain.Slice( 3, 12 );
962 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
963
964 BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
965 SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
966
967 // Equal arc to original inserted arc
968 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
969 BOOST_CHECK_EQUAL( firstArc.GetArcMid(), sliceArc0.GetArcMid() );
970 BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
971
972 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 10 );
973 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), sliceArc0.GetP0() ); // equal to arc start
974 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
975
976 for( int i = 1; i <= 6; i++ )
977 BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
978
979 for( int i = 0; i <= 5; i++ )
980 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
981
982 BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 6 ), true );
983 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), sliceArc0.GetP1() ); // equal to arc end
984
985 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 7 ), targetSegment.A );
986 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 8 ), targetSegment.B );
987 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 9 ), secondArc.GetP0() );
988 }
989
990 BOOST_TEST_CONTEXT( "Case 5: Chain ends in arc and point" )
991 {
992 SHAPE_LINE_CHAIN chainCopy = chain;
993 chainCopy.Append( VECTOR2I( 400000, 400000 ) );
994
995 SHAPE_LINE_CHAIN sliceResult = chainCopy.Slice( 11, -1 );
996 BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
997 BOOST_CHECK_EQUAL( sliceResult.GetPoint( -1 ), VECTOR2I( 400000, 400000 ) );
998 }
999
1000 BOOST_TEST_CONTEXT( "Case 6: Start to end, chain with one point" )
1001 {
1002 SHAPE_LINE_CHAIN chainCopy = SLC_CASES().OnePoint;
1003
1004 SHAPE_LINE_CHAIN sliceResult = chainCopy.Slice( 0, -1 );
1005 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 1 );
1006 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), VECTOR2I( 233450000, 228360000 ) );
1007 BOOST_CHECK_EQUAL( sliceResult.GetPoint( -1 ), VECTOR2I( 233450000, 228360000 ) ); // Same as index 0
1008 }
1009
1010 BOOST_TEST_CONTEXT( "Case 7: Start to end, chain with two points" )
1011 {
1012 SHAPE_LINE_CHAIN chainCopy = SLC_CASES().TwoPoints;
1013
1014 SHAPE_LINE_CHAIN sliceResult = chainCopy.Slice( 0, -1 );
1015 BOOST_CHECK_EQUAL( sliceResult.PointCount(), 2 );
1016 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), VECTOR2I( 233450000, 228360000 ) );
1017 BOOST_CHECK_EQUAL( sliceResult.GetPoint( 1 ), VECTOR2I( 263450000, 258360000 ) );
1018 BOOST_CHECK_EQUAL( sliceResult.GetPoint( -1 ), VECTOR2I( 263450000, 258360000 ) ); // Same as index 1
1019 }
1020}
1021
1022
1023// Test SHAPE_LINE_CHAIN::NearestPoint( VECTOR2I )
1024BOOST_AUTO_TEST_CASE( NearestPointPt )
1025{
1026 SEG seg1( VECTOR2I( 0, 100000 ), VECTOR2I( 50000, 0 ) );
1027 SEG seg2( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
1028 SHAPE_ARC arc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), ANGLE_180 );
1029
1030 // Start a chain with 2 points (seg1)
1031 SHAPE_LINE_CHAIN chain( { seg1.A, seg1.B } );
1032 BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
1033 // Add first arc
1034 chain.Append( arc );
1035 BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
1036 // Add two points (seg2)
1037 chain.Append( seg2.A );
1038 chain.Append( seg2.B );
1039 BOOST_CHECK_EQUAL( chain.PointCount(), 11 );
1041
1042 VECTOR2I ptOnArcCloseToStart( 297553, 31697 ); //should be index 3 in chain
1043 VECTOR2I ptOnArcCloseToEnd( 139709, 82983 ); //should be index 6 in chain
1044
1045 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToStart, true ), ptOnArcCloseToStart );
1046 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToStart, false ), arc.GetP0() );
1047
1048 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToEnd, true ), ptOnArcCloseToEnd );
1049 BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToEnd, false ), arc.GetP1() );
1050}
1051
1052
1053// Test SHAPE_LINE_CHAIN::Replace( SHAPE_LINE_CHAIN )
1055{
1056 BOOST_TEST_INFO( "8949 crash" );
1057
1058 std::vector<VECTOR2I> linePts = {
1059 { 206000000, 140110000 }, { 192325020, 140110000 }, { 192325020, 113348216 },
1060 { 192251784, 113274980 }, { 175548216, 113274980 }, { 175474980, 113348216 },
1061 { 175474980, 136694980 }, { 160774511, 121994511 }, { 160774511, 121693501 },
1062 { 160086499, 121005489 }, { 159785489, 121005489 }, { 159594511, 120814511 },
1063 { 160086499, 120814511 }, { 160774511, 120126499 }, { 160774511, 119153501 },
1064 { 160086499, 118465489 }, { 159113501, 118465489 }, { 158425489, 119153501 },
1065 { 158425489, 119645489 }, { 157325020, 118545020 }, { 157325020, 101925020 },
1066 { 208674980, 101925020 }, { 208674980, 145474980 }, { 192325020, 145474980 },
1067 { 192325020, 140110000 }
1068 };
1069
1070 SHAPE_LINE_CHAIN baseChain( linePts, false );
1071 baseChain.SetWidth( 250000 );
1072 BOOST_CHECK_EQUAL( baseChain.PointCount(), linePts.size() );
1073
1074 SHAPE_LINE_CHAIN replaceChain( { VECTOR2I( 192325020, 140110000 ) }, false );
1075 BOOST_CHECK_EQUAL( replaceChain.PointCount(), 1 );
1076
1077 baseChain.Replace( 1, 23, replaceChain );
1078
1079 BOOST_CHECK_EQUAL( baseChain.PointCount(), linePts.size() - ( 23 - 1 ) );
1080
1081 // Replacing the last point in a chain is special-cased
1082 baseChain.Replace( baseChain.PointCount() - 1, baseChain.PointCount() - 1, VECTOR2I( -1, -1 ) );
1083
1084 BOOST_CHECK_EQUAL( baseChain.CLastPoint(), VECTOR2I( -1, -1 ) );
1085}
1086
1087
1088BOOST_AUTO_TEST_SUITE_END()
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:115
bool IsClockwise() const
Definition: shape_arc.cpp:385
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:114
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:224
const VECTOR2I & GetP0() const
Definition: shape_arc.h:113
const VECTOR2I & GetCenter() const
Definition: shape_arc.cpp:475
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:439
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_CHECK(box.ClosestPointTo(VECTOR2D(0, 0))==VECTOR2D(1, 2))
Test suite for KiCad math code.
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
#define BOOST_TEST_CONTEXT(A)
#define BOOST_TEST_INFO(A)
If HAVE_EXPECTED_FAILURES is defined, this means that boost::unit_test::expected_failures is availabl...
VECTOR2< int > VECTOR2I
Definition: vector2d.h:602