KiCad PCB EDA Suite
direction_45.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 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.or/licenses/>.
18 */
19
21#include <trigo.h>
22
23
25 bool aStartDiagonal,
26 CORNER_MODE aMode ) const
27
28{
29 bool startDiagonal;
30
31 if( m_dir == UNDEFINED )
32 startDiagonal = aStartDiagonal;
33 else
34 startDiagonal = IsDiagonal();
35
36 int w = abs( aP1.x - aP0.x );
37 int h = abs( aP1.y - aP0.y );
38 int sw = sign( aP1.x - aP0.x );
39 int sh = sign( aP1.y - aP0.y );
40
41 bool is90mode = aMode == CORNER_MODE::ROUNDED_90 || aMode == CORNER_MODE::MITERED_90;
43
44 // Shortcut where we can generate just one segment and quit. Avoids more complicated handling
45 // of precision errors if filleting is enabled
46 if( w == 0 || h == 0 || ( !is90mode && h == w ) )
47 {
48 pl.Append( aP0 );
49 pl.Append( aP1 );
50 return pl;
51 }
52
53 VECTOR2I mp0, mp1;
54 int tangentLength;
55
56 if( is90mode )
57 {
58 if( startDiagonal == ( h >= w ) )
59 {
60 mp0 = VECTOR2I( w * sw, 0 ); // direction: E
61 }
62 else
63 {
64 mp0 = VECTOR2I( 0, sh * h ); // direction: N
65 }
66 }
67 else
68 {
69 if( w > h )
70 {
71 mp0 = VECTOR2I( ( w - h ) * sw, 0 ); // direction: E
72 mp1 = VECTOR2I( h * sw, h * sh ); // direction: NE
73 tangentLength = ( w - h ) - mp1.EuclideanNorm();
74 }
75 else
76 {
77 mp0 = VECTOR2I( 0, sh * ( h - w ) ); // direction: N
78 mp1 = VECTOR2I( sw * w, sh * w ); // direction: NE
79 tangentLength = ( h - w ) - mp1.EuclideanNorm();
80 }
81 }
82
83 SHAPE_ARC arc;
84 VECTOR2I arcEndpoint;
85
86 switch( aMode )
87 {
88 case CORNER_MODE::MITERED_45:
89 /*
90 * For width greater than height, we're calculating something like this.
91 * mp0 will be used if we start straight; mp1 if we start diagonal.
92 *
93 * aP0 ----------------- mp0
94 * . \
95 * . \
96 * . \
97 * mp1 . . . . . . . . aP1
98 *
99 */
100 pl.Append( aP0 );
101 pl.Append( startDiagonal ? ( aP0 + mp1 ) : ( aP0 + mp0 ) );
102 pl.Append( aP1 );
103 break;
104
105 case CORNER_MODE::ROUNDED_45:
106 {
107 /*
108 * For a fillet, we need to know the arc start point (A in the diagram below)
109 * A straight segment will be needed between aP0 and A if we are starting straight,
110 * or between the arc end and aP1 if we are starting diagonally.
111 *
112 * aP0 -- A --___ mp0
113 * . ---
114 * . --
115 * . --
116 * mp1 . . . . . . . . aP1
117 *
118 * For the length of this segment (tangentLength), we subtract the length of the "diagonal"
119 * line from the "straight" line (i.e. dist(aP0, mp0) - dist(mp0, aP1))
120 * In the example above, we will have a straight segment from aP0 to A, and then we can use
121 * the distance from A to aP1 (diagLength) to calculate the radius of the arc.
122 */
123 if( w == h )
124 {
125 pl.Append( aP0 );
126 pl.Append( aP1 );
127 break;
128 }
129
130 double diag2 = tangentLength >= 0 ? mp1.SquaredEuclideanNorm() : mp0.SquaredEuclideanNorm();
131 double diagLength = std::sqrt( ( 2 * diag2 ) - ( 2 * diag2 * std::cos( 3 * M_PI_4 ) ) );
132 int arcRadius = KiROUND( diagLength / ( 2.0 * std::cos( 67.5 * M_PI / 180.0 ) ) );
133
134 // There are four different ways to build an arc, depending on whether or not we are
135 // starting diagonally and whether or not we have a negative tangent length (meaning the
136 // arc has to be on the opposite end of the line from normal). This math could probably
137 // be condensed and optimized but I'm tired of staring at it for now (and it works!)
138
139 if( startDiagonal )
140 {
141 int rotationSign = ( w > h ) ? ( sw * sh * -1 ) : ( sw * sh );
142
143 if( tangentLength >= 0 )
144 {
145 // Positive tangentLength, diagonal start: arc goes at the start
146 arcEndpoint = aP1 - mp0.Resize( tangentLength );
147 arc.ConstructFromStartEndAngle( aP0, arcEndpoint, ANGLE_45 * rotationSign );
148
149 if( arc.GetP0() == arc.GetP1() )
150 pl.Append( aP0 );
151 else
152 pl.Append( arc );
153
154 pl.Append( aP1 );
155 }
156 else
157 {
158 // Negative tangentLength, diagonal start: arc goes at the end
159 arcEndpoint = aP0 + mp1.Resize( std::abs( tangentLength ) );
160 arc.ConstructFromStartEndAngle( arcEndpoint, aP1, ANGLE_45 * rotationSign );
161
162 pl.Append( aP0 );
163
164 if( arc.GetP0() == arc.GetP1() )
165 pl.Append( aP1 );
166 else
167 pl.Append( arc );
168 }
169 }
170 else
171 {
172 int rotationSign = ( w > h ) ? ( sw * sh * -1 ) : ( sw * sh );
173 VECTOR2D centerDir( mp0 );
174
175 RotatePoint( centerDir, ANGLE_90 * rotationSign );
176
177 if( tangentLength >= 0 )
178 {
179 // Positive tangentLength: arc goes at the end
180 arcEndpoint = aP0 + mp0.Resize( tangentLength );
181 arc.ConstructFromStartEndAngle( arcEndpoint, aP1, - ANGLE_45 * rotationSign );
182
183 pl.Append( aP0 );
184
185 if( arc.GetP0() == arc.GetP1() )
186 pl.Append( aP1 );
187 else
188 pl.Append( arc );
189 }
190 else
191 {
192 // Negative tangentLength: arc goes at the start
193 VECTOR2I arcCenter = aP0 + centerDir.Resize( arcRadius );
194 SHAPE_ARC ca( arcCenter, aP0, -ANGLE_45 * rotationSign );
195
196
197 // Constructing with a center can lead to imprecise endpoint. We need to guarantee
198 // tangency of the endpoint.
199 // TODO: update the math above to calculate the proper endpoint directly
200 VECTOR2I endpoint( ca.GetP1() );
201
202 if( std::abs( endpoint.y - aP1.y ) < SHAPE_ARC::MIN_PRECISION_IU )
203 {
204 VECTOR2I fixedEnd( endpoint.x, aP1.y );
205 ca.ConstructFromStartEndAngle( ca.GetP0(), fixedEnd, ANGLE_45 * rotationSign );
206 }
207 else if( std::abs( endpoint.x - aP1.x ) < SHAPE_ARC::MIN_PRECISION_IU )
208 {
209 VECTOR2I fixedEnd( aP1.x, endpoint.y );
210 ca.ConstructFromStartEndAngle( ca.GetP0(), fixedEnd, ANGLE_45 * rotationSign );
211 }
212
213 if( ca.GetP0() == ca.GetP1() )
214 pl.Append( aP0 );
215 else
216 pl.Append( ca );
217
218 pl.Append( aP1 );
219 }
220 }
221 break;
222 }
223 case CORNER_MODE::MITERED_90:
224 /*
225 * For width greater than height, we're calculating something like this.
226 *
227 * <-mp0->
228 * aP0 -------------------+
229 * |
230 * |
231 * |
232 * aP1
233 */
234 pl.Append( aP0 );
235 pl.Append( aP0 + mp0 );
236 pl.Append( aP1 );
237 break;
238
239 case CORNER_MODE::ROUNDED_90:
240 /*
241 * For a fillet, we need to know the arc end point
242 * A straight segment will be needed between aP0 and arcEnd in case distance aP0,mp0 is bigger
243 * than the distance mp0,aP1, if the distance is shorter the straigth segment is between
244 * arcEnd and aP1. If both distances are equal, we don't need a straight segment.
245 *
246 * aP0 ----- arcEnd ---__
247 * --
248 * \
249 * |
250 * arcCenter aP1
251 *
252 * For the length of the radius we use the shorter of the horizontal and vertical distance.
253 */
254 SHAPE_ARC arc;
255
256 if( w == h ) // we only need one arc without a straigth line.
257 {
258 arc.ConstructFromStartEndCenter( aP0, aP1, aP1 - mp0, sh == sw != startDiagonal );
259 pl.Append( arc );
260 return pl;
261 }
262
263 VECTOR2I arcEnd; // Arc position that is not at aP0 nor aP1
264 VECTOR2I arcCenter;
265
266 if( startDiagonal ) //Means start with the arc first
267 {
268 if( h > w ) // Arc followed by a vertical line
269 {
270 int y = aP0.y + ( w * sh );
271 arcEnd = VECTOR2I( aP1.x, y );
272 arcCenter = VECTOR2I( aP0.x, y );
273 arc.ConstructFromStartEndCenter( aP0, arcEnd, arcCenter, sh != sw );
274 pl.Append( arc );
275 pl.Append( aP1 );
276 }
277 else // Arc followed by a horizontal line
278 {
279 int x = aP0.x + ( h * sw );
280 arcEnd = VECTOR2I( x, aP1.y );
281 arcCenter = VECTOR2I( x, aP0.y );
282 arc.ConstructFromStartEndCenter( aP0, arcEnd, arcCenter, sh == sw );
283 pl.Append( arc );
284 pl.Append( aP1 );
285 }
286 }
287 else
288 {
289 if( w > h ) // Horizontal line followed by the arc
290 {
291 int x = aP1.x - ( h * sw );
292 arcEnd = VECTOR2I( x, aP0.y );
293 arcCenter = VECTOR2I( x, aP1.y );
294 pl.Append( aP0 );
295 arc.ConstructFromStartEndCenter( arcEnd, aP1, arcCenter, sh != sw );
296 pl.Append( arc );
297 }
298 else // Vertical line followed by the arc
299 {
300 int y = aP1.y - ( w * sh );
301 arcEnd = VECTOR2I( aP0.x, y );
302 arcCenter = VECTOR2I( aP1.x, y );
303 pl.Append( aP0 );
304 arc.ConstructFromStartEndCenter( arcEnd, aP1, arcCenter, sh == sw );
305 pl.Append( arc );
306 }
307 }
308 break;
309 }
310
311 pl.Simplify();
312 return pl;
313}
const SHAPE_LINE_CHAIN BuildInitialTrace(const VECTOR2I &aP0, const VECTOR2I &aP1, bool aStartDiagonal=false, CORNER_MODE aMode=CORNER_MODE::MITERED_45) const
Build a 2-segment line chain between points aP0 and aP1 and following 45-degree routing regime.
bool IsDiagonal() const
Returns true if the direction is diagonal (e.g.
Definition: direction45.h:213
CORNER_MODE
Corner modes.
Definition: direction45.h:67
Directions m_dir
Are we routing on 45 or 90 degree increments.
Definition: direction45.h:346
SHAPE_ARC & ConstructFromStartEndAngle(const VECTOR2I &aStart, const VECTOR2I &aEnd, const EDA_ANGLE &aAngle, double aWidth=0)
Construct this arc from the given start, end and angle.
Definition: shape_arc.cpp:191
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:209
const VECTOR2I & GetP1() const
Definition: shape_arc.h:113
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...
SHAPE_LINE_CHAIN & Simplify(bool aRemoveColinear=true)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
Definition: shape.h:128
extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition: vector2d.h:300
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:293
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:378
static constexpr EDA_ANGLE & ANGLE_45
Definition: eda_angle.h:413
static constexpr EDA_ANGLE & ANGLE_90
Definition: eda_angle.h:414
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:401
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
int sign(T val)
Definition: util.h:124
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618