KiCad PCB EDA Suite
test_fillet.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) 2018 KiCad Developers, see CHANGELOG.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 <boost/test/unit_test.hpp>
25 
27 
30 
31 #include <algorithm>
32 
33 #include "geom_test_utils.h"
34 
36 {
37 };
38 
42 BOOST_FIXTURE_TEST_SUITE( Fillet, FilletFixture )
43 
44 /*
45  * @brief check that a single segment of a fillet complies with the geometric
46  * constraint:
47  *
48  * 1: The end points are radius from the centre point
49  * 2: The mid point error is acceptable
50  * 3: The segment midpoints are perpendicular to the radius
51  */
52 void TestFilletSegmentConstraints( const SEG& aSeg, VECTOR2I aRadCentre,
53  int aRadius, int aError )
54 {
55  const auto diffA = aRadCentre - aSeg.A;
56  const auto diffB = aRadCentre - aSeg.B;
57  const auto diffC = aRadCentre - aSeg.Center();
58 
59  // Check 1: radii (error of 1 for rounding)
60  BOOST_CHECK_PREDICATE(
61  KI_TEST::IsWithinAndBelow<int>, ( diffA.EuclideanNorm() )( aRadius )( 1 ) );
62  BOOST_CHECK_PREDICATE(
63  KI_TEST::IsWithinAndBelow<int>, ( diffB.EuclideanNorm() )( aRadius )( 1 ) );
64 
65  // Check 2: Mid-point error
66  BOOST_CHECK_PREDICATE(
67  KI_TEST::IsWithinAndBelow<int>, ( diffC.EuclideanNorm() )( aRadius )( aError + 1 ) );
68 
69  // Check 3: Mid-point -> radius centre perpendicular
70  const auto perpendularityMaxError = ( M_PI / 2 ) / 10;
71  BOOST_CHECK_PREDICATE( GEOM_TEST::ArePerpendicular<int>,
72  ( diffC )( aSeg.A - aSeg.B )( perpendularityMaxError ) );
73 }
74 
75 
79 void TestSquareFillet( int aSquareSize, int aRadius, int aError )
80 {
81  using namespace GEOM_TEST;
82 
83  SHAPE_POLY_SET squarePolySet;
84 
85  squarePolySet.AddOutline( MakeSquarePolyLine(aSquareSize, VECTOR2I(0, 0) ) );
86 
87  SHAPE_POLY_SET filleted = FilletPolySet(squarePolySet, aRadius, aError);
88 
89  // expect a single filleted polygon
90  BOOST_CHECK_EQUAL( filleted.OutlineCount(), 1 );
91 
92  auto segIter = filleted.IterateSegments();
93 
94  const VECTOR2I radCentre { aSquareSize / 2 - aRadius,
95  aSquareSize / 2 - aRadius };
96 
97  int checked = 0;
98 
99  for( ; segIter; segIter++ )
100  {
101  // Only check the first Quadrant
102  if ( SegmentCompletelyInQuadrant( *segIter, QUADRANT::Q1 ) )
103  {
104  TestFilletSegmentConstraints( *segIter, radCentre, aRadius, aError );
105  checked++;
106  }
107  }
108 
109  // we expect there to be at least one segment in the fillet
110  BOOST_CHECK( checked > 0 );
111 }
112 
113 
117 void TestConcaveSquareFillet( int aSquareSize, int aRadius, int aError )
118 {
119  using namespace GEOM_TEST;
120 
121  SHAPE_POLY_SET polySet;
122  SHAPE_LINE_CHAIN polyLine;
123 
124  /*
125  * L-shape:
126  * ----
127  * | |
128  * ---- |
129  * | |
130  * --------
131  */
132 
133  polyLine.Append( VECTOR2I{ 0, 0 } );
134  polyLine.Append( VECTOR2I{ 0, aSquareSize / 2 } );
135  polyLine.Append( VECTOR2I{ aSquareSize / 2 , aSquareSize / 2 } );
136  polyLine.Append( VECTOR2I{ aSquareSize / 2 , aSquareSize } );
137  polyLine.Append( VECTOR2I{ aSquareSize, aSquareSize } );
138  polyLine.Append( VECTOR2I{ aSquareSize, 0 } );
139 
140  polyLine.SetClosed( true );
141 
142  polySet.AddOutline( polyLine );
143 
144  SHAPE_POLY_SET filleted = FilletPolySet(polySet, aRadius, aError);
145 
146  // expect a single filleted polygon
147  BOOST_CHECK_EQUAL( filleted.OutlineCount(), 1 );
148 
149  auto segIter = filleted.IterateSegments();
150 
151  const VECTOR2I radCentre { aSquareSize / 2 - aRadius,
152  aSquareSize / 2 + aRadius };
153 
154  int checked = 0;
155 
156  for( ; segIter; segIter++ )
157  {
158  // Only check segments around the concave corner
159  if ( SegmentCompletelyWithinRadius( *segIter, radCentre, aRadius + 1) )
160  {
161  TestFilletSegmentConstraints( *segIter, radCentre, aRadius, aError );
162  checked++;
163  }
164  }
165 
166  // we expect there to be at least one segment in the fillet
167  BOOST_CHECK( checked > 0 );
168 }
169 
170 
172 {
174  int radius;
175  int error;
176 };
177 
178 const std::vector<SquareFilletTestCase> squareFilletCases {
179  { 1000, 120, 10 },
180  { 1000, 10, 1 },
181 
182  /* Large error relative to fillet */
183  { 1000, 10, 5 },
184 
185  /* Very small error relative to fillet(many segments in interpolation) */
186  { 70000, 1000, 1 },
187 };
188 
193 BOOST_AUTO_TEST_CASE( SquareFillet )
194 {
195  for ( const auto& testCase : squareFilletCases )
196  {
197  TestSquareFillet( testCase.squareSize, testCase.radius, testCase.error );
198  }
199 }
200 
201 BOOST_AUTO_TEST_CASE( SquareConcaveFillet )
202 {
203  for ( const auto& testCase : squareFilletCases )
204  {
205  TestConcaveSquareFillet( testCase.squareSize, testCase.radius, testCase.error );
206  }
207 }
208 
209 
210 BOOST_AUTO_TEST_SUITE_END()
void TestSquareFillet(int aSquareSize, int aRadius, int aError)
: Create a square, fillet it, and check a corner for correctness
Definition: test_fillet.cpp:79
int OutlineCount() const
Returns the number of outlines in the set
SHAPE_POLY_SET FilletPolySet(SHAPE_POLY_SET &aPolySet, int aRadius, int aError)
VECTOR2 defines a general 2D-vector/point.
Definition: vector2d.h:61
void TestConcaveSquareFillet(int aSquareSize, int aRadius, int aError)
: Create a square concave corner, fillet and check correctness
VECTOR2< int > VECTOR2I
Definition: vector2d.h:594
void Append(int aX, int aY, bool aAllowDuplication=false)
Function Append()
void SetClosed(bool aClosed)
Function SetClosed()
bool SegmentCompletelyWithinRadius(const SEG &aSeg, const VECTOR2I &aPt, const int aRadius)
SHAPE_POLY_SET.
SHAPE_LINE_CHAIN MakeSquarePolyLine(int aSize, const VECTOR2I &aCentre)
construct a square polygon of given size width and centre
BOOST_AUTO_TEST_CASE(SquareFillet)
Tests the SHAPE_POLY_SET::FilletPolygon method against certain geometric constraints.
void TestFilletSegmentConstraints(const SEG &aSeg, VECTOR2I aRadCentre, int aRadius, int aError)
Declares the FilletFixture struct as the boost test fixture.
Definition: test_fillet.cpp:52
Utility functions for testing geometry functions.
Definition: seg.h:39
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index
SHAPE_LINE_CHAIN.
bool SegmentCompletelyInQuadrant(const SEG &aSeg, QUADRANT aQuadrant)
const std::vector< SquareFilletTestCase > squareFilletCases
SEGMENT_ITERATOR IterateSegments(int aFirst, int aLast, bool aIterateHoles=false)
Returns an iterator object, for iterating between aFirst and aLast outline, with or without holes (de...