KiCad PCB EDA Suite
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 
38 BOOST_AUTO_TEST_SUITE( ShapeLineChain )
39 
40 BOOST_AUTO_TEST_CASE( ArcToPolyline )
41 {
42  SHAPE_LINE_CHAIN base_chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 1000 ), VECTOR2I( 1000, 0 ) } );
43 
44  SHAPE_LINE_CHAIN chain_insert( {
45  VECTOR2I( 0, 1500 ),
46  VECTOR2I( 1500, 1500 ),
47  VECTOR2I( 1500, 0 ),
48  } );
49 
50  SHAPE_LINE_CHAIN arc_insert1( SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), 180.0 ) );
51 
52  SHAPE_LINE_CHAIN arc_insert2( SHAPE_ARC( VECTOR2I( 0, 500 ), VECTOR2I( 0, 400 ), 180.0 ) );
53 
54  BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
55  BOOST_CHECK_EQUAL( arc_insert1.CShapes().size(), arc_insert1.CPoints().size() );
56  BOOST_CHECK_EQUAL( arc_insert2.CShapes().size(), arc_insert2.CPoints().size() );
57 
58  BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
59  BOOST_CHECK( GEOM_TEST::IsOutlineValid( arc_insert1 ) );
60  BOOST_CHECK( GEOM_TEST::IsOutlineValid( arc_insert2 ) );
61 
62  base_chain.Insert( 0, SHAPE_ARC( VECTOR2I( 0, -100 ), VECTOR2I( 0, -200 ), 1800 ) );
63  BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
64  BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
65 
66  base_chain.Replace( 0, 2, chain_insert );
67  BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
68  BOOST_CHECK_EQUAL( base_chain.CShapes().size(), base_chain.CPoints().size() );
69 }
70 
71 
72 // Similar test to above but with larger coordinates, so we have more than one point per arc
73 BOOST_AUTO_TEST_CASE( ArcToPolylineLargeCoords )
74 {
75  SHAPE_LINE_CHAIN base_chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( 100000, 0 ) } );
76 
77  SHAPE_LINE_CHAIN chain_insert( {
78  VECTOR2I( 0, 1500000 ),
79  VECTOR2I( 1500000, 1500000 ),
80  VECTOR2I( 1500000, 0 ),
81  } );
82 
83  base_chain.Append( SHAPE_ARC( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 100000 ), 180.0 ) );
84 
85  BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
86  BOOST_CHECK_EQUAL( base_chain.PointCount(), 11 );
87 
88  base_chain.Insert( 9, VECTOR2I( 250000, 0 ) );
89  BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
90  BOOST_CHECK_EQUAL( base_chain.PointCount(), 12 );
91  BOOST_CHECK_EQUAL( base_chain.ArcCount(), 2 ); // Should have two arcs after the split
92 
93  base_chain.Replace( 5, 6, chain_insert );
94  BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
95  BOOST_CHECK_EQUAL( base_chain.PointCount(), 13 ); // Adding 3 points, removing 2
96  BOOST_CHECK_EQUAL( base_chain.ArcCount(), 3 ); // Should have three arcs after the split
97 
98  base_chain.Replace( 4, 6, VECTOR2I( 550000, 0 ) );
99  BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
100  BOOST_CHECK_EQUAL( base_chain.PointCount(), 11 ); // Adding 1 point, removing 3
101  BOOST_CHECK_EQUAL( base_chain.ArcCount(), 3 ); // Should still have three arcs
102 
103  // Test ClearArcs
104  base_chain.SetClosed( true );
105  double areaPriorToArcRemoval = base_chain.Area();
106  base_chain.ClearArcs();
107 
108  BOOST_CHECK( GEOM_TEST::IsOutlineValid( base_chain ) );
109  BOOST_CHECK_EQUAL( base_chain.PointCount(), 11 ); // We should have the same number of points
110  BOOST_CHECK_EQUAL( base_chain.ArcCount(), 0 ); // All arcs should have been removed
111  BOOST_CHECK_EQUAL( base_chain.Area(), areaPriorToArcRemoval ); // Area should not have changed
112 }
113 
114 // Test that duplicate point gets removed when line is set to be closed
115 BOOST_AUTO_TEST_CASE( SetClosedDuplicatePoint )
116 {
117  // Test from issue #9843
118  SHAPE_LINE_CHAIN chain;
119 
120  chain.Append(
121  SHAPE_ARC( { -859598, 2559876 }, { -1632771, 1022403 }, { -3170244, 249230 }, 0 ) );
122 
123  chain.Append(
124  SHAPE_ARC( { -3170244, -1657832 }, { -292804, -317564 }, { 1047464, 2559876 }, 0 ) );
125 
126  chain.Append( VECTOR2I( -859598, 2559876 ) ); // add point that is equal to first arc start
127 
129  BOOST_CHECK_EQUAL( chain.PointCount(), 31 );
130 
131  // CLOSED CHAIN
132  chain.SetClosed( true );
133  BOOST_CHECK_EQUAL( chain.PointCount(), 30 ); // (-1) should have removed coincident points
135 }
136 
137 
138 // Test special case where the last arc in the chain has a shared point with the first arc
139 BOOST_AUTO_TEST_CASE( ArcWrappingToStartSharedPoints )
140 {
141  // represent a circle with two semicircular arcs
142  SHAPE_ARC arc1( VECTOR2I( 100000, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( -100000, 0 ), 0 );
143  SHAPE_ARC arc2( VECTOR2I( -100000, 0 ), VECTOR2I( 0, -100000 ), VECTOR2I( 100000, 0 ), 0 );
144 
145  // Start a chain with the two arcs
146  SHAPE_LINE_CHAIN chain;
147  chain.Append( arc1 );
148  chain.Append( arc2 );
149  BOOST_CHECK_EQUAL( chain.PointCount(), 13 );
150  //BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
151 
152  // OPEN CHAIN
153  // Start of the chain is not yet a shared point, so can't be an arc end either
154  BOOST_CHECK_EQUAL( chain.IsSharedPt( 0 ), false );
155  BOOST_CHECK_EQUAL( chain.IsArcEnd( 0 ), false );
156  BOOST_CHECK_EQUAL( chain.IsArcStart( 0 ), true );
157 
158  // Index 6 is the shared point between the two arcs in the middle of the chain
159  BOOST_CHECK_EQUAL( chain.IsSharedPt( 6 ), true );
160  BOOST_CHECK_EQUAL( chain.IsArcEnd( 6 ), true );
161  BOOST_CHECK_EQUAL( chain.IsArcStart( 6 ), true );
162 
163  // End index is not yet a shared point
164  int endIndex = chain.PointCount() - 1;
165  BOOST_CHECK_EQUAL( chain.IsSharedPt( endIndex ), false );
166  BOOST_CHECK_EQUAL( chain.IsArcEnd( endIndex ), true );
167  BOOST_CHECK_EQUAL( chain.IsArcStart( endIndex ), false );
168 
169  for( int i = 0; i < chain.PointCount(); i++ )
170  {
171  BOOST_CHECK_EQUAL( chain.IsPtOnArc( i ), true ); // all points in the chain are arcs
172  }
173 
174  // CLOSED CHAIN
175  chain.SetClosed( true );
176  BOOST_CHECK_EQUAL( chain.PointCount(), 12 ); // (-1) should have removed coincident points
177  //BOOST_CHECK( GEOM_TEST::IsOutlineValid( chain ) );
178 
179  // Start of the chain should be a shared point now, so can't be an arc end either
180  BOOST_CHECK_EQUAL( chain.IsSharedPt( 0 ), true );
181  BOOST_CHECK_EQUAL( chain.IsArcEnd( 0 ), true );
182  BOOST_CHECK_EQUAL( chain.IsArcStart( 0 ), true );
183 
184  // Index 6 is the shared point between the two arcs in the middle of the chain
185  BOOST_CHECK_EQUAL( chain.IsSharedPt( 6 ), true );
186  BOOST_CHECK_EQUAL( chain.IsArcEnd( 6 ), true );
187  BOOST_CHECK_EQUAL( chain.IsArcStart( 6 ), true );
188 
189  // End index is in the middle of an arc, so not an end point or shared point
190  endIndex = chain.PointCount() - 1;
191  BOOST_CHECK_EQUAL( chain.IsSharedPt( endIndex ), false );
192  BOOST_CHECK_EQUAL( chain.IsArcEnd( endIndex ), false );
193  BOOST_CHECK_EQUAL( chain.IsArcStart( endIndex ), false );
194 }
195 
196 // Test SHAPE_LINE_CHAIN::Split()
198 {
199  SEG seg1( VECTOR2I( 0, 100000 ), VECTOR2I( 50000, 0 ) );
200  SEG seg2( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
201  SHAPE_ARC arc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), 180.0 );
202 
203  // Start a chain with 2 points (seg1)
204  SHAPE_LINE_CHAIN chain( { seg1.A, seg1.B } );
205  BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
206  // Add first arc
207  chain.Append( arc );
208  BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
209  // Add two points (seg2)
210  chain.Append( seg2.A );
211  chain.Append( seg2.B );
212  BOOST_CHECK_EQUAL( chain.PointCount(), 11 );
214 
215  BOOST_TEST_CONTEXT( "Case 1: Point not in the chain" )
216  {
217  SHAPE_LINE_CHAIN chainCopy = chain;
218  BOOST_CHECK_EQUAL( chainCopy.Split( VECTOR2I( 400000, 0 ) ), -1 );
219  BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
220  BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
221  }
222 
223  BOOST_TEST_CONTEXT( "Case 2: Point close to start of a segment" )
224  {
225  SHAPE_LINE_CHAIN chainCopy = chain;
226  VECTOR2I splitPoint = seg1.A + VECTOR2I( 5, -10 );
227  BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 1 );
228  BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
229  BOOST_CHECK_EQUAL( chainCopy.GetPoint( 1 ), splitPoint );
230  BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() + 1 ); // new point added
231  BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
232  }
233 
234  BOOST_TEST_CONTEXT( "Case 3: Point exactly on the segment" )
235  {
236  SHAPE_LINE_CHAIN chainCopy = chain;
237  VECTOR2I splitPoint = seg1.B;
238  BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 1 );
239  BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
240  BOOST_CHECK_EQUAL( chainCopy.GetPoint( 1 ), splitPoint );
241  BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
242  BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
243  }
244 
245  BOOST_TEST_CONTEXT( "Case 4: Point at start of arc" )
246  {
247  SHAPE_LINE_CHAIN chainCopy = chain;
248  VECTOR2I splitPoint = arc.GetP0();
249  BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 2 );
250  BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
251  BOOST_CHECK_EQUAL( chainCopy.GetPoint( 2 ), splitPoint );
252  BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() );
253  BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() );
254  }
255 
256  BOOST_TEST_CONTEXT( "Case 5: Point close to start of arc" )
257  {
258  SHAPE_LINE_CHAIN chainCopy = chain;
259  VECTOR2I splitPoint = arc.GetP0() + VECTOR2I( -10, 130 );
260  BOOST_CHECK_EQUAL( chainCopy.Split( splitPoint ), 3 );
261  BOOST_CHECK( GEOM_TEST::IsOutlineValid( chainCopy ) );
262  BOOST_CHECK_EQUAL( chainCopy.GetPoint( 3 ), splitPoint );
263  BOOST_CHECK_EQUAL( chainCopy.IsSharedPt( 3 ), true ); // must be a shared point
264  BOOST_CHECK_EQUAL( chainCopy.PointCount(), chain.PointCount() + 1 ); // new point added
265  BOOST_CHECK_EQUAL( chainCopy.ArcCount(), chain.ArcCount() + 1 ); // new arc should have been created
266  }
267 }
268 
269 
270 // Test SHAPE_LINE_CHAIN::Slice()
272 {
273  SEG targetSegment( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
274  SHAPE_ARC firstArc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), 180.0 );
275  SHAPE_ARC secondArc( VECTOR2I( -200000, -200000 ), VECTOR2I( -300000, -100000 ), -180.0 );
276  int tol = SHAPE_ARC::DefaultAccuracyForPCB(); // Tolerance for arc collisions
277 
278  // Start a chain with 3 points
279  SHAPE_LINE_CHAIN chain( { VECTOR2I( 0, 0 ), VECTOR2I( 0, 100000 ), VECTOR2I( 100000, 0 ) } );
280  BOOST_CHECK_EQUAL( chain.PointCount(), 3 );
281  // Add first arc
282  chain.Append( firstArc );
283  BOOST_CHECK_EQUAL( chain.PointCount(), 10 );
284  // Add two points (target segment)
285  chain.Append( targetSegment.A );
286  chain.Append( targetSegment.B );
287  BOOST_CHECK_EQUAL( chain.PointCount(), 12 );
288  // Add a second arc
289  chain.Append( secondArc );
290  BOOST_CHECK_EQUAL( chain.PointCount(), 20 );
292 
296  BOOST_TEST_CONTEXT( "Case 1: Start at arc endpoint, finish middle of arc" )
297  {
298  SHAPE_LINE_CHAIN sliceResult = chain.Slice( 9, 18 );
299  BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
300 
301  BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
302  SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
303  BOOST_CHECK_EQUAL( secondArc.GetP0(), sliceArc0.GetP0() ); // equal arc start points
304  BOOST_CHECK( secondArc.Collide( sliceArc0.GetArcMid(), tol ) );
305  BOOST_CHECK( secondArc.Collide( sliceArc0.GetP1(), tol ) );
306 
307  BOOST_CHECK_EQUAL( sliceResult.PointCount(), 10 );
308  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), firstArc.GetP1() ); // equal to arc end
309  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 1 ), targetSegment.A );
310  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 2 ), targetSegment.B );
311  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 3 ), sliceArc0.GetP0() ); // equal to arc start
312  BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 3 ), true );
313 
314  for( int i = 4; i <= 8; i++ )
315  BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
316 
317  for( int i = 3; i <= 7; i++ )
318  BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
319 
320  BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 9 ), true );
321  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 9 ), sliceArc0.GetP1() ); // equal to arc end
322  }
323 
327  BOOST_TEST_CONTEXT( "Case 2: Start at middle of an arc, finish at arc startpoint" )
328  {
329  SHAPE_LINE_CHAIN sliceResult = chain.Slice( 5, 12 );
330  BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
331 
332  BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
333  SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
334  BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() ); // equal arc end points
335  BOOST_CHECK( firstArc.Collide( sliceArc0.GetArcMid(), tol ) );
336  BOOST_CHECK( firstArc.Collide( sliceArc0.GetP0(), tol ) );
337 
338  BOOST_CHECK_EQUAL( sliceResult.PointCount(), 8 );
339  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), sliceArc0.GetP0() );// equal to arc start
340  BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
341 
342  for( int i = 1; i <= 4; i++ )
343  BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
344 
345  for( int i = 0; i <= 3; i++ )
346  BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
347 
348  BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 4 ), true );
349  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 4 ), sliceArc0.GetP1() ); // equal to arc end
350 
351  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 5 ), targetSegment.A );
352  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), targetSegment.B );
353  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 7 ), secondArc.GetP0() );
354  }
355 
359  BOOST_TEST_CONTEXT( "Case 3: Full arc, nothing else" )
360  {
361  SHAPE_LINE_CHAIN sliceResult = chain.Slice( 3, 9 );
362  BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
363 
364  BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
365  SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
366 
367  // Equal arc to original inserted arc
368  BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
369  BOOST_CHECK_EQUAL( firstArc.GetArcMid(), sliceArc0.GetArcMid() );
370  BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
371 
372  BOOST_CHECK_EQUAL( sliceResult.PointCount(), 7 );
373  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), sliceArc0.GetP0() ); // equal to arc start
374  BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
375 
376  for( int i = 1; i <= 6; i++ )
377  BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
378 
379  for( int i = 0; i <= 5; i++ )
380  BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
381 
382  BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 6 ), true );
383  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), sliceArc0.GetP1() ); // equal to arc end
384  }
385 
389  BOOST_TEST_CONTEXT( "Case 4: Full arc, and straight segments to next arc start" )
390  {
391  SHAPE_LINE_CHAIN sliceResult = chain.Slice( 3, 12 );
392  BOOST_CHECK( GEOM_TEST::IsOutlineValid( sliceResult ) );
393 
394  BOOST_CHECK_EQUAL( sliceResult.ArcCount(), 1 );
395  SHAPE_ARC sliceArc0 = sliceResult.Arc( 0 );
396 
397  // Equal arc to original inserted arc
398  BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
399  BOOST_CHECK_EQUAL( firstArc.GetArcMid(), sliceArc0.GetArcMid() );
400  BOOST_CHECK_EQUAL( firstArc.GetP1(), sliceArc0.GetP1() );
401 
402  BOOST_CHECK_EQUAL( sliceResult.PointCount(), 10 );
403  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 0 ), sliceArc0.GetP0() ); // equal to arc start
404  BOOST_CHECK_EQUAL( sliceResult.IsArcStart( 0 ), true );
405 
406  for( int i = 1; i <= 6; i++ )
407  BOOST_CHECK_EQUAL( sliceResult.IsArcStart( i ), false );
408 
409  for( int i = 0; i <= 5; i++ )
410  BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( i ), false );
411 
412  BOOST_CHECK_EQUAL( sliceResult.IsArcEnd( 6 ), true );
413  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 6 ), sliceArc0.GetP1() ); // equal to arc end
414 
415  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 7 ), targetSegment.A );
416  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 8 ), targetSegment.B );
417  BOOST_CHECK_EQUAL( sliceResult.GetPoint( 9 ), secondArc.GetP0() );
418  }
419 
420  BOOST_TEST_CONTEXT( "Case 5: Chain ends in arc and point" )
421  {
422  SHAPE_LINE_CHAIN chainCopy = chain;
423  chainCopy.Append( VECTOR2I( 400000, 400000 ) );
424 
425  SHAPE_LINE_CHAIN sliceResult = chainCopy.Slice( 11, -1 );
426  BOOST_CHECK_EQUAL( sliceResult.GetPoint( -1 ), VECTOR2I( 400000, 400000 ) );
427  }
428 }
429 
430 
431 // Test SHAPE_LINE_CHAIN::NearestPoint( VECTOR2I )
432 BOOST_AUTO_TEST_CASE( NearestPointPt )
433 {
434  SEG seg1( VECTOR2I( 0, 100000 ), VECTOR2I( 50000, 0 ) );
435  SEG seg2( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ) );
436  SHAPE_ARC arc( VECTOR2I( 200000, 0 ), VECTOR2I( 300000, 0 ), 180.0 );
437 
438  // Start a chain with 2 points (seg1)
439  SHAPE_LINE_CHAIN chain( { seg1.A, seg1.B } );
440  BOOST_CHECK_EQUAL( chain.PointCount(), 2 );
441  // Add first arc
442  chain.Append( arc );
443  BOOST_CHECK_EQUAL( chain.PointCount(), 9 );
444  // Add two points (seg2)
445  chain.Append( seg2.A );
446  chain.Append( seg2.B );
447  BOOST_CHECK_EQUAL( chain.PointCount(), 11 );
449 
450  VECTOR2I ptOnArcCloseToStart( 297553, 31697 ); //should be index 3 in chain
451  VECTOR2I ptOnArcCloseToEnd( 139709, 82983 ); //should be index 6 in chain
452 
453  BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToStart, true ), ptOnArcCloseToStart );
454  BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToStart, false ), arc.GetP0() );
455 
456  BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToEnd, true ), ptOnArcCloseToEnd );
457  BOOST_CHECK_EQUAL( chain.NearestPoint( ptOnArcCloseToEnd, false ), arc.GetP1() );
458 }
459 
460 
461 // Test SHAPE_LINE_CHAIN::Replace( SHAPE_LINE_CHAIN )
462 BOOST_AUTO_TEST_CASE( ReplaceChain )
463 {
464  BOOST_TEST_INFO( "8949 crash" );
465 
466  std::vector<VECTOR2I> linePts = {
467  { 206000000, 140110000 }, { 192325020, 140110000 }, { 192325020, 113348216 },
468  { 192251784, 113274980 }, { 175548216, 113274980 }, { 175474980, 113348216 },
469  { 175474980, 136694980 }, { 160774511, 121994511 }, { 160774511, 121693501 },
470  { 160086499, 121005489 }, { 159785489, 121005489 }, { 159594511, 120814511 },
471  { 160086499, 120814511 }, { 160774511, 120126499 }, { 160774511, 119153501 },
472  { 160086499, 118465489 }, { 159113501, 118465489 }, { 158425489, 119153501 },
473  { 158425489, 119645489 }, { 157325020, 118545020 }, { 157325020, 101925020 },
474  { 208674980, 101925020 }, { 208674980, 145474980 }, { 192325020, 145474980 },
475  { 192325020, 140110000 }
476  };
477 
478  SHAPE_LINE_CHAIN baseChain( linePts, false );
479  baseChain.SetWidth( 250000 );
480  BOOST_CHECK_EQUAL( baseChain.PointCount(), linePts.size() );
481 
482  SHAPE_LINE_CHAIN replaceChain( { VECTOR2I( 192325020, 140110000 ) }, false );
483  BOOST_CHECK_EQUAL( replaceChain.PointCount(), 1 );
484 
485  baseChain.Replace( 1, 23, replaceChain );
486 
487  BOOST_CHECK_EQUAL( baseChain.PointCount(), linePts.size() - ( 23 - 1 ) );
488 
489  // Replacing the last point in a chain is special-cased
490  baseChain.Replace( baseChain.PointCount() - 1, baseChain.PointCount() - 1, VECTOR2I( -1, -1 ) );
491 
492  BOOST_CHECK_EQUAL( baseChain.CLastPoint(), VECTOR2I( -1, -1 ) );
493 }
494 
495 
496 BOOST_AUTO_TEST_SUITE_END()
const std::vector< std::pair< ssize_t, ssize_t > > & CShapes() const
const SHAPE_ARC & Arc(size_t aArc) const
int Split(const VECTOR2I &aP)
Insert the point aP belonging to one of the our segments, splitting the adjacent segment in two.
bool IsSharedPt(size_t aIndex) const
Test if a point is shared between multiple shapes.
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:229
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.
Define a general 2D-vector/point.
Definition: vector2d.h:61
size_t ArcCount() const
static double DefaultAccuracyForPCB()
Definition: shape_arc.h:220
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
int PointCount() const
Return the number of points (vertices) in this line chain.
BOOST_CHECK(v2.Cross(v1)==1)
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
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...
bool IsArcStart(size_t aIndex) const
void SetWidth(int aWidth)
Set the width of all segments in the chain.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
const std::vector< VECTOR2I > & CPoints() const
const VECTOR2I & GetP0() const
Definition: shape_arc.h:111
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:113
#define BOOST_TEST_CONTEXT(A)
virtual const VECTOR2I GetPoint(int aIndex) const override
bool IsPtOnArc(size_t aPtIndex) const
Definition: seg.h:40
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
BOOST_AUTO_TEST_CASE(ArcToPolyline)
NOTE: Collision of SHAPE_LINE_CHAIN with arcs is tested in test_shape_arc.cpp.
VECTOR2I A
Definition: seg.h:48
bool IsArcEnd(size_t aIndex) const
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
Numerical test predicates.
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 VECTOR2I & GetP1() const
Definition: shape_arc.h:112
#define BOOST_TEST_INFO(A)
If HAVE_EXPECTED_FAILURES is defined, this means that boost::unit_test::expected_failures is availabl...
VECTOR2I B
Definition: seg.h:49