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