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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <boost/test/unit_test.hpp>
21#include <boost/test/data/test_case.hpp>
22
25
28
29#include <algorithm>
30
31#include "geom_test_utils.h"
32
33
35
36/*
37 * @brief check that a single segment of a fillet complies with the geometric
38 * constraint:
39 *
40 * 1: The end points are radius from the centre point
41 * 2: The mid point error is acceptable
42 * 3: The segment midpoints are perpendicular to the radius
43 */
44void TestFilletSegmentConstraints( const SEG& aSeg, VECTOR2I aRadCentre,
45 int aRadius, int aError )
46{
47 const auto diffA = aRadCentre - aSeg.A;
48 const auto diffB = aRadCentre - aSeg.B;
49 const auto diffC = aRadCentre - aSeg.Center();
50
51 // Check 1: radii (error of 1 for rounding)
53 KI_TEST::IsWithinAndBelow<int>, ( diffA.EuclideanNorm() )( aRadius )( 1 ) );
55 KI_TEST::IsWithinAndBelow<int>, ( diffB.EuclideanNorm() )( aRadius )( 1 ) );
56
57 // Check 2: Mid-point error
59 KI_TEST::IsWithinAndBelow<int>, ( diffC.EuclideanNorm() )( aRadius )( aError + 1 ) );
60
61 // Check 3: Mid-point -> radius centre perpendicular
62 const EDA_ANGLE perpendularityMaxError = ANGLE_90 / 10;
64 ( diffC )( aSeg.A - aSeg.B )( perpendularityMaxError ) );
65}
66
67
71void TestSquareFillet( int aSquareSize, int aRadius, int aError )
72{
73 using namespace GEOM_TEST;
74
75 SHAPE_POLY_SET squarePolySet;
76
77 squarePolySet.AddOutline( KI_TEST::BuildSquareChain( aSquareSize, VECTOR2I( 0, 0 ) ) );
78
79 SHAPE_POLY_SET filleted = FilletPolySet( squarePolySet, aRadius, aError );
80
81 // expect a single filleted polygon
82 BOOST_CHECK_EQUAL( filleted.OutlineCount(), 1 );
83
84 auto segIter = filleted.IterateSegments();
85
86 const VECTOR2I radCentre { aSquareSize / 2 - aRadius,
87 aSquareSize / 2 - aRadius };
88
89 int checked = 0;
90
91 for( ; segIter; segIter++ )
92 {
93 // Only check the first Quadrant
95 {
96 TestFilletSegmentConstraints( *segIter, radCentre, aRadius, aError );
97 checked++;
98 }
99 }
100
101 // we expect there to be at least one segment in the fillet
102 BOOST_CHECK( checked > 0 );
103}
104
105
109void TestConcaveSquareFillet( int aSquareSize, int aRadius, int aError )
110{
111 using namespace GEOM_TEST;
112
113 SHAPE_POLY_SET polySet;
114 SHAPE_LINE_CHAIN polyLine;
115
116 /*
117 * L-shape:
118 * ----
119 * | |
120 * ---- |
121 * | |
122 * --------
123 */
124
125 polyLine.Append( VECTOR2I{ 0, 0 } );
126 polyLine.Append( VECTOR2I{ 0, aSquareSize / 2 } );
127 polyLine.Append( VECTOR2I{ aSquareSize / 2 , aSquareSize / 2 } );
128 polyLine.Append( VECTOR2I{ aSquareSize / 2 , aSquareSize } );
129 polyLine.Append( VECTOR2I{ aSquareSize, aSquareSize } );
130 polyLine.Append( VECTOR2I{ aSquareSize, 0 } );
131
132 polyLine.SetClosed( true );
133
134 polySet.AddOutline( polyLine );
135
136 SHAPE_POLY_SET filleted = FilletPolySet(polySet, aRadius, aError);
137
138 // expect a single filleted polygon
139 BOOST_CHECK_EQUAL( filleted.OutlineCount(), 1 );
140
141 auto segIter = filleted.IterateSegments();
142
143 const VECTOR2I radCentre { aSquareSize / 2 - aRadius,
144 aSquareSize / 2 + aRadius };
145
146 int checked = 0;
147
148 for( ; segIter; segIter++ )
149 {
150 // Only check segments around the concave corner
151 if ( SegmentCompletelyWithinRadius( *segIter, radCentre, aRadius + 1) )
152 {
153 TestFilletSegmentConstraints( *segIter, radCentre, aRadius, aError );
154 checked++;
155 }
156 }
157
158 // we expect there to be at least one segment in the fillet
159 BOOST_CHECK( checked > 0 );
160}
161
162
164{
167 int error;
168
169 friend std::ostream& operator<<( std::ostream& os, const SquareFilletTestCase& testCase )
170 {
171 return os << "Square size: " << testCase.squareSize << ", Radius: " << testCase.radius
172 << ", Error: " << testCase.error;
173 }
174};
175
176const std::vector<SquareFilletTestCase> squareFilletCases{
177 { 1000, 120, 10 },
178 { 1000, 10, 1 },
179
180 /* Large error relative to fillet */
181 { 1000, 10, 5 },
182
183 /* Very small error relative to fillet(many segments in interpolation) */
184 { 70000, 1000, 1 },
185};
186
191BOOST_DATA_TEST_CASE( SquareFillet, boost::unit_test::data::make( squareFilletCases ), testCase )
192{
193 TestSquareFillet( testCase.squareSize, testCase.radius, testCase.error );
194}
195
196BOOST_DATA_TEST_CASE( SquareConcaveFillet, boost::unit_test::data::make( squareFilletCases ),
197 testCase )
198{
199 TestConcaveSquareFillet( testCase.squareSize, testCase.radius, testCase.error );
200}
201
202
Definition seg.h:38
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:413
Utilities for creating useful line chain idioms commonly founds in QA utilities and tests.
Utility functions for testing geometry functions.
bool SegmentCompletelyWithinRadius(const SEG &aSeg, const VECTOR2I &aPt, const int aRadius)
SHAPE_POLY_SET FilletPolySet(SHAPE_POLY_SET &aPolySet, int aRadius, int aError)
bool SegmentCompletelyInQuadrant(const SEG &aSeg, QUADRANT aQuadrant)
bool ArePerpendicular(const VECTOR2< T > &a, const VECTOR2< T > &b, const EDA_ANGLE &aTolerance)
SHAPE_LINE_CHAIN BuildSquareChain(int aSize, const VECTOR2I &aCentre)
Builds a square SHAPE_LINE_CHAIN of a certain size at a certain centre.
bool IsWithinAndBelow(T aValue, T aNominal, T aErrorBelow)
value is in range [aNominal - aErrorBelow, aNominal]
Definition numeric.h:76
friend std::ostream & operator<<(std::ostream &os, const SquareFilletTestCase &testCase)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
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)
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
BOOST_AUTO_TEST_SUITE_END()
BOOST_CHECK_PREDICATE(ArePolylineEndPointsNearCircle,(chain)(c.m_geom.m_center_point)(radius)(accuracy+epsilon))
BOOST_CHECK_EQUAL(result, "25.4")
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683