KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The 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 <boost/test/unit_test.hpp>
25#include <boost/test/data/test_case.hpp>
26
29
32
33#include <algorithm>
34
35#include "geom_test_utils.h"
36
37
39
40/*
41 * @brief check that a single segment of a fillet complies with the geometric
42 * constraint:
43 *
44 * 1: The end points are radius from the centre point
45 * 2: The mid point error is acceptable
46 * 3: The segment midpoints are perpendicular to the radius
47 */
48void TestFilletSegmentConstraints( const SEG& aSeg, VECTOR2I aRadCentre,
49 int aRadius, int aError )
50{
51 const auto diffA = aRadCentre - aSeg.A;
52 const auto diffB = aRadCentre - aSeg.B;
53 const auto diffC = aRadCentre - aSeg.Center();
54
55 // Check 1: radii (error of 1 for rounding)
57 KI_TEST::IsWithinAndBelow<int>, ( diffA.EuclideanNorm() )( aRadius )( 1 ) );
59 KI_TEST::IsWithinAndBelow<int>, ( diffB.EuclideanNorm() )( aRadius )( 1 ) );
60
61 // Check 2: Mid-point error
63 KI_TEST::IsWithinAndBelow<int>, ( diffC.EuclideanNorm() )( aRadius )( aError + 1 ) );
64
65 // Check 3: Mid-point -> radius centre perpendicular
66 const EDA_ANGLE perpendularityMaxError = ANGLE_90 / 10;
67 BOOST_CHECK_PREDICATE( GEOM_TEST::ArePerpendicular<int>,
68 ( diffC )( aSeg.A - aSeg.B )( perpendularityMaxError ) );
69}
70
71
75void TestSquareFillet( int aSquareSize, int aRadius, int aError )
76{
77 using namespace GEOM_TEST;
78
79 SHAPE_POLY_SET squarePolySet;
80
81 squarePolySet.AddOutline( KI_TEST::BuildSquareChain( aSquareSize, VECTOR2I( 0, 0 ) ) );
82
83 SHAPE_POLY_SET filleted = FilletPolySet( squarePolySet, aRadius, aError );
84
85 // expect a single filleted polygon
86 BOOST_CHECK_EQUAL( filleted.OutlineCount(), 1 );
87
88 auto segIter = filleted.IterateSegments();
89
90 const VECTOR2I radCentre { aSquareSize / 2 - aRadius,
91 aSquareSize / 2 - aRadius };
92
93 int checked = 0;
94
95 for( ; segIter; segIter++ )
96 {
97 // Only check the first Quadrant
98 if ( SegmentCompletelyInQuadrant( *segIter, QUADRANT::Q1 ) )
99 {
100 TestFilletSegmentConstraints( *segIter, radCentre, aRadius, aError );
101 checked++;
102 }
103 }
104
105 // we expect there to be at least one segment in the fillet
106 BOOST_CHECK( checked > 0 );
107}
108
109
113void TestConcaveSquareFillet( int aSquareSize, int aRadius, int aError )
114{
115 using namespace GEOM_TEST;
116
117 SHAPE_POLY_SET polySet;
118 SHAPE_LINE_CHAIN polyLine;
119
120 /*
121 * L-shape:
122 * ----
123 * | |
124 * ---- |
125 * | |
126 * --------
127 */
128
129 polyLine.Append( VECTOR2I{ 0, 0 } );
130 polyLine.Append( VECTOR2I{ 0, aSquareSize / 2 } );
131 polyLine.Append( VECTOR2I{ aSquareSize / 2 , aSquareSize / 2 } );
132 polyLine.Append( VECTOR2I{ aSquareSize / 2 , aSquareSize } );
133 polyLine.Append( VECTOR2I{ aSquareSize, aSquareSize } );
134 polyLine.Append( VECTOR2I{ aSquareSize, 0 } );
135
136 polyLine.SetClosed( true );
137
138 polySet.AddOutline( polyLine );
139
140 SHAPE_POLY_SET filleted = FilletPolySet(polySet, aRadius, aError);
141
142 // expect a single filleted polygon
143 BOOST_CHECK_EQUAL( filleted.OutlineCount(), 1 );
144
145 auto segIter = filleted.IterateSegments();
146
147 const VECTOR2I radCentre { aSquareSize / 2 - aRadius,
148 aSquareSize / 2 + aRadius };
149
150 int checked = 0;
151
152 for( ; segIter; segIter++ )
153 {
154 // Only check segments around the concave corner
155 if ( SegmentCompletelyWithinRadius( *segIter, radCentre, aRadius + 1) )
156 {
157 TestFilletSegmentConstraints( *segIter, radCentre, aRadius, aError );
158 checked++;
159 }
160 }
161
162 // we expect there to be at least one segment in the fillet
163 BOOST_CHECK( checked > 0 );
164}
165
166
168{
171 int error;
172
173 friend std::ostream& operator<<( std::ostream& os, const SquareFilletTestCase& testCase )
174 {
175 return os << "Square size: " << testCase.squareSize << ", Radius: " << testCase.radius
176 << ", Error: " << testCase.error;
177 }
178};
179
180const std::vector<SquareFilletTestCase> squareFilletCases{
181 { 1000, 120, 10 },
182 { 1000, 10, 1 },
183
184 /* Large error relative to fillet */
185 { 1000, 10, 5 },
186
187 /* Very small error relative to fillet(many segments in interpolation) */
188 { 70000, 1000, 1 },
189};
190
195BOOST_DATA_TEST_CASE( SquareFillet, boost::unit_test::data::make( squareFilletCases ), testCase )
196{
197 TestSquareFillet( testCase.squareSize, testCase.radius, testCase.error );
198}
199
200BOOST_DATA_TEST_CASE( SquareConcaveFillet, boost::unit_test::data::make( squareFilletCases ),
201 testCase )
202{
203 TestConcaveSquareFillet( testCase.squareSize, testCase.radius, testCase.error );
204}
205
206
Definition: seg.h:42
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
SEGMENT_ITERATOR IterateSegments(int aFirst, int aLast, bool aIterateHoles=false)
Return an iterator object, for iterating between aFirst and aLast outline, with or without holes (def...
int OutlineCount() const
Return the number of outlines in the set.
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
Utilities for creating useful line chain idioms commonly founds in QA utilities and tests.
Utility functions for testing geometry functions.
SHAPE_LINE_CHAIN BuildSquareChain(int aSize, const VECTOR2I &aCentre)
Builds a square SHAPE_LINE_CHAIN of a certain size at a certain centre.
friend std::ostream & operator<<(std::ostream &os, const SquareFilletTestCase &testCase)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_CHECK_EQUAL(ret, c.m_exp_result)
const std::vector< SquareFilletTestCase > squareFilletCases
void TestConcaveSquareFillet(int aSquareSize, int aRadius, int aError)
: Create a square concave corner, fillet and check correctness
void TestFilletSegmentConstraints(const SEG &aSeg, VECTOR2I aRadCentre, int aRadius, int aError)
Definition: test_fillet.cpp:48
BOOST_DATA_TEST_CASE(SquareConcaveFillet, boost::unit_test::data::make(squareFilletCases), testCase)
Tests the SHAPE_POLY_SET::FilletPolygon method against certain geometric constraints.
void TestSquareFillet(int aSquareSize, int aRadius, int aError)
: Create a square, fillet it, and check a corner for correctness
Definition: test_fillet.cpp:75
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_PREDICATE(ArePolylineEndPointsNearCircle,(chain)(c.m_geom.m_center_point)(radius)(accuracy+epsilon))
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695