KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eda_angle.h
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 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
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <cassert>
23#include <cmath>
24#include <math/vector2d.h> // for VECTOR2I
25
26
33
34
36{
37public:
45 EDA_ANGLE( double aValue, EDA_ANGLE_T aAngleType )
46 {
47 switch( aAngleType )
48 {
49 case RADIANS_T:
50 m_value = aValue / DEGREES_TO_RADIANS;
51 break;
52
54 m_value = aValue / 10.0;
55 break;
56
57 default:
58 m_value = aValue;
59 }
60 }
61
67 constexpr explicit EDA_ANGLE( double aAngleInDegrees ) :
68 m_value( aAngleInDegrees )
69 {}
70
71 explicit EDA_ANGLE( const VECTOR2D& aVector )
72 {
73 if( aVector.x == 0.0 && aVector.y == 0.0 )
74 {
75 m_value = 0.0;
76 }
77 else if( aVector.y == 0.0 )
78 {
79 if( aVector.x >= 0 )
80 m_value = 0.0;
81 else
82 m_value = -180.0;
83 }
84 else if( aVector.x == 0.0 )
85 {
86 if( aVector.y >= 0.0 )
87 m_value = 90.0;
88 else
89 m_value = -90.0;
90 }
91 else if( aVector.x == aVector.y )
92 {
93 if( aVector.x >= 0.0 )
94 m_value = 45.0;
95 else
96 m_value = -180.0 + 45.0;
97 }
98 else if( aVector.x == -aVector.y )
99 {
100 if( aVector.x >= 0.0 )
101 m_value = -45.0;
102 else
103 m_value = 180.0 - 45.0;
104 }
105 else
106 {
107 *this = EDA_ANGLE( atan2( aVector.y, aVector.x ), RADIANS_T );
108 }
109 }
110
112 m_value( 0.0 )
113 {}
114
115 inline double AsDegrees() const { return m_value; }
116
117 inline int AsTenthsOfADegree() const { return KiROUND( m_value * 10.0 ); }
118
119 inline double AsRadians() const { return m_value * DEGREES_TO_RADIANS; }
120
121 static constexpr double DEGREES_TO_RADIANS = M_PI / 180.0;
122
127 bool IsCardinal() const;
128
133 bool IsCardinal90() const;
134
135 bool IsZero() const
136 {
137 return m_value == 0.0;
138 // return equals( m_value, 0.0 );
139 }
140
141 bool IsHorizontal() const
142 {
143 return m_value == 0.0 || m_value == 180.0;
144 // return equals( m_value, 0.0 ) || equals( m_value, 180.0 );
145 }
146
147 bool IsVertical() const
148 {
149 return m_value == 90.0 || m_value == 270.0;
150 //return equals( m_value, 90.0 ) || equals( m_value, 270.0 );
151 }
152
153 bool IsParallelTo( EDA_ANGLE aAngle ) const
154 {
155 EDA_ANGLE thisNormalized = *this;
156
157 // Normalize90 is inclusive on both ends [-90, +90]
158 // but we need it to be (-90, +90] for this test to work
159 thisNormalized.Normalize90();
160
161 if( equals( thisNormalized.AsDegrees(), -90.0 ) )
162 thisNormalized = EDA_ANGLE( 90.0, DEGREES_T );
163
164 aAngle.Normalize90();
165
166 if( equals( aAngle.AsDegrees(), -90.0 ) )
167 aAngle = EDA_ANGLE( 90.0, DEGREES_T );
168
169 return ( equals( thisNormalized.AsDegrees(), aAngle.AsDegrees() ) );
170 }
171
173 {
174 return EDA_ANGLE( -AsDegrees(), DEGREES_T );
175 }
176
177 double Sin() const
178 {
179 EDA_ANGLE test = *this;
180 test.Normalize();
181
182 if( test.m_value == 0.0 || test.m_value == 180.0 )
183 return 0.0;
184 else if( test.m_value == 45.0 || test.m_value == 135.0 )
185 return M_SQRT1_2; // sqrt(2)/2
186 else if( test.m_value == 225.0 || test.m_value == 315.0 )
187 return -M_SQRT1_2;
188 else if( test.m_value == 90.0 )
189 return 1.0;
190 else if( test.m_value == 270.0 )
191 return -1.0;
192 else
193 return sin( AsRadians() );
194 }
195
196 double Cos() const
197 {
198 EDA_ANGLE test = *this;
199 test.Normalize();
200
201 if( test.m_value == 0.0 )
202 return 1.0;
203 else if( test.m_value == 180.0 )
204 return -1.0;
205 else if( test.m_value == 90.0 || test.m_value == 270.0 )
206 return 0.0;
207 else if( test.m_value == 45.0 || test.m_value == 315.0 )
208 return M_SQRT1_2; // sqrt(2)/2
209 else if( test.m_value == 135.0 || test.m_value == 225.0 )
210 return -M_SQRT1_2;
211 else
212 return cos( AsRadians() );
213 }
214
215 double Tan() const { return tan( AsRadians() ); }
216
217 static EDA_ANGLE Arccos( double x ) { return EDA_ANGLE( acos( x ), RADIANS_T ); }
218
219 static EDA_ANGLE Arcsin( double x ) { return EDA_ANGLE( asin( x ), RADIANS_T ); }
220
221 static EDA_ANGLE Arctan( double x ) { return EDA_ANGLE( atan( x ), RADIANS_T ); }
222
223 static EDA_ANGLE Arctan2( double y, double x )
224 {
225 return EDA_ANGLE( atan2( y, x ), RADIANS_T );
226 }
227
229 {
230 m_value = foldToTwoPeriods( m_value, 360.0 );
231
232 while( m_value < -0.0 )
233 m_value += 360.0;
234
235 while( m_value >= 360.0 )
236 m_value -= 360.0;
237
238 return *this;
239 }
240
242 {
243 EDA_ANGLE ret( *this );
244 return ret.Normalize();
245 }
246
248 {
249 m_value = foldToTwoPeriods( m_value, 360.0 );
250
251 while( m_value <= -360.0 )
252 m_value += 360.0;
253
254 while( m_value > 0.0 )
255 m_value -= 360.0;
256
257 return *this;
258 }
259
261 {
262 m_value = foldToTwoPeriods( m_value, 180.0 );
263
264 while( m_value < -90.0 )
265 m_value += 180.0;
266
267 while( m_value > 90.0 )
268 m_value -= 180.0;
269
270 return *this;
271 }
272
274 {
275 m_value = foldToTwoPeriods( m_value, 360.0 );
276
277 while( m_value <= -180.0 )
278 m_value += 360.0;
279
280 while( m_value > 180.0 )
281 m_value -= 360.0;
282
283 return *this;
284 }
285
287 {
288 m_value = foldToTwoPeriods( m_value, 360.0 );
289
290 while( m_value < -360.0 )
291 m_value += 360.0;
292
293 while( m_value >= 360.0 )
294 m_value -= 360.0;
295
296 return *this;
297 }
298
300 EDA_ANGLE Snapped( const EDA_ANGLE& aStep ) const
301 {
302 double step = aStep.AsDegrees();
303
304 if( step <= 0.0 )
305 return *this;
306
307 return EDA_ANGLE( step * KiROUND( AsDegrees() / step ), DEGREES_T );
308 }
309
310 EDA_ANGLE KeepUpright() const;
311
312 [[nodiscard]] EDA_ANGLE Round( int digits ) const
313 {
314 EDA_ANGLE angle( *this );
315 double rounded = KiROUND( angle.AsDegrees() * pow( 10.0, digits ) ) / pow( 10.0, digits );
316 angle = EDA_ANGLE( rounded, DEGREES_T );
317 return angle;
318 }
319
321 {
322 *this = EDA_ANGLE( AsDegrees() + aAngle.AsDegrees(), DEGREES_T );
323 return *this;
324 }
325
327 {
328 *this = EDA_ANGLE( AsDegrees() - aAngle.AsDegrees(), DEGREES_T );
329 return *this;
330 }
331
332private:
338 static double foldToTwoPeriods( double aValue, double aPeriod )
339 {
340 if( !std::isfinite( aValue ) )
341 return 0.0;
342
343 if( std::abs( aValue ) < 2.0 * aPeriod )
344 return aValue;
345
346 // Exact because |aValue| >= 2 * aPeriod keeps the remainder on a grid no finer than the sum
347 return std::fmod( aValue, aPeriod ) + std::copysign( aPeriod, aValue );
348 }
349
350 double m_value;
351
352};
353
354
355inline EDA_ANGLE operator-( const EDA_ANGLE& aAngle )
356{
357 return aAngle.Invert();
358}
359
360
361inline EDA_ANGLE operator-( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
362{
363 return EDA_ANGLE( aAngleA.AsDegrees() - aAngleB.AsDegrees(), DEGREES_T );
364}
365
366
367inline EDA_ANGLE operator+( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
368{
369 return EDA_ANGLE( aAngleA.AsDegrees() + aAngleB.AsDegrees(), DEGREES_T );
370}
371
372
373inline EDA_ANGLE operator*( const EDA_ANGLE& aAngleA, double aOperator )
374{
375 return EDA_ANGLE( aAngleA.AsDegrees() * aOperator, DEGREES_T );
376}
377
378
379inline EDA_ANGLE operator/( const EDA_ANGLE& aAngleA, double aOperator )
380{
381 return EDA_ANGLE( aAngleA.AsDegrees() / aOperator, DEGREES_T );
382}
383
384
385inline double operator/( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aOperator )
386{
387 return aAngleA.AsDegrees() / aOperator.AsDegrees();
388}
389
390
391inline bool operator==( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
392{
393 return aAngleA.AsDegrees() == aAngleB.AsDegrees();
394 //return equals( aAngleA.AsDegrees(), aAngleB.AsDegrees() );
395}
396
397
398inline bool operator!=( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
399{
400 return aAngleA.AsDegrees() != aAngleB.AsDegrees();
401 // return !equals( aAngleA.AsDegrees(), aAngleB.AsDegrees() );
402}
403
404
405inline bool operator>( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
406{
407 return aAngleA.AsDegrees() > aAngleB.AsDegrees();
408}
409
410
411inline bool operator<( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
412{
413 return aAngleA.AsDegrees() < aAngleB.AsDegrees();
414}
415
416
417inline bool operator<=( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
418{
419 return aAngleA.AsDegrees() <= aAngleB.AsDegrees();
420}
421
422
423inline bool operator>=( const EDA_ANGLE& aAngleA, const EDA_ANGLE& aAngleB )
424{
425 return aAngleA.AsDegrees() >= aAngleB.AsDegrees();
426}
427
428
429inline std::ostream& operator<<( std::ostream& aStream, const EDA_ANGLE& aAngle )
430{
431 return aStream << aAngle.AsDegrees();
432}
433
434
435namespace std
436{
437inline EDA_ANGLE abs( const EDA_ANGLE& aAngle )
438{
439 return EDA_ANGLE( std::abs( aAngle.AsDegrees() ), DEGREES_T );
440}
441}
442
443
444static constexpr EDA_ANGLE ANGLE_HORIZONTAL{ 0 };
445static constexpr EDA_ANGLE ANGLE_VERTICAL{ 90 };
446static constexpr EDA_ANGLE FULL_CIRCLE{ 360 };
447
448static constexpr EDA_ANGLE ANGLE_0{ 0 };
449static constexpr EDA_ANGLE ANGLE_45{ 45 };
450static constexpr EDA_ANGLE ANGLE_90{ 90 };
451static constexpr EDA_ANGLE ANGLE_135{ 135 };
452static constexpr EDA_ANGLE ANGLE_180{ 180 };
453static constexpr EDA_ANGLE ANGLE_270{ 270 };
454static constexpr EDA_ANGLE ANGLE_360{ 360 };
455
456
473{
474public:
475 EDA_ORIENTATION() = default;
476
477 explicit EDA_ORIENTATION( const EDA_ANGLE& aAngle ) { SetAngle( aAngle ); }
478
483 {
484 SetAngle( aAngle );
485 return *this;
486 }
487
488 bool operator==( const EDA_ORIENTATION& aOther ) const = default;
489
493 EDA_ORIENTATION operator+( const EDA_ANGLE& aAngle ) const
494 {
495 return EDA_ORIENTATION( m_angle + aAngle );
496 }
497
501 EDA_ORIENTATION operator-( const EDA_ANGLE& aAngle ) const
502 {
503 return EDA_ORIENTATION( m_angle - aAngle );
504 }
505
507 {
508 return EDA_ORIENTATION( -m_angle );
509 }
510
516 EDA_ANGLE operator-( const EDA_ORIENTATION& aOther ) const
517 {
518 return m_angle - aOther.m_angle;
519 }
520
522 {
523 SetAngle( m_angle + aAngle );
524 return *this;
525 }
526
528 {
529 SetAngle( m_angle - aAngle );
530 return *this;
531 }
532
533 EDA_ANGLE GetAngle() const { return m_angle; }
534
535 void SetAngle( const EDA_ANGLE& aAngle )
536 {
537 m_angle = aAngle;
538 m_angle.Normalize();
539
540 // Normalize -0 to 0 to avoid -0 in display and serialization.
541 if( m_angle == ANGLE_0 )
543 }
544
545private:
547};
548
549
550// Free forms with the angle on the left, so the mirror-style formulas read as-written
551// (180 - orientation, -orientation).
552inline EDA_ORIENTATION operator+( const EDA_ANGLE& aAngle, const EDA_ORIENTATION& aOrientation )
553{
554 return aOrientation + aAngle;
555}
556
557
558inline EDA_ORIENTATION operator-( const EDA_ANGLE& aAngle, const EDA_ORIENTATION& aOrientation )
559{
560 return EDA_ORIENTATION( aAngle - aOrientation.GetAngle() );
561}
562
563
564inline std::ostream& operator<<( std::ostream& aStream, const EDA_ORIENTATION& aOrientation )
565{
566 // Follow EDA_ANGLE's convention of outputting angles in degrees.
567 return aStream << aOrientation.GetAngle().AsDegrees();
568}
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
EDA_ANGLE Normalize()
Definition eda_angle.h:228
EDA_ANGLE NormalizeNegative()
Definition eda_angle.h:247
double Sin() const
Definition eda_angle.h:177
EDA_ANGLE Normalize90()
Definition eda_angle.h:260
double Tan() const
Definition eda_angle.h:215
constexpr EDA_ANGLE(double aAngleInDegrees)
Construct an EDA_ANGLE in degrees.
Definition eda_angle.h:67
int AsTenthsOfADegree() const
Definition eda_angle.h:117
bool IsParallelTo(EDA_ANGLE aAngle) const
Definition eda_angle.h:153
double AsDegrees() const
Definition eda_angle.h:115
EDA_ANGLE Snapped(const EDA_ANGLE &aStep) const
Snap to the nearest integer multiple of aStep (returns *this if aStep <= 0).
Definition eda_angle.h:300
EDA_ANGLE Invert() const
Definition eda_angle.h:172
bool IsZero() const
Definition eda_angle.h:135
bool IsHorizontal() const
Definition eda_angle.h:141
bool IsCardinal() const
Definition eda_angle.cpp:40
EDA_ANGLE Normalize180()
Definition eda_angle.h:273
static EDA_ANGLE Arctan2(double y, double x)
Definition eda_angle.h:223
EDA_ANGLE KeepUpright() const
Definition eda_angle.cpp:23
EDA_ANGLE Normalize720()
Definition eda_angle.h:286
double AsRadians() const
Definition eda_angle.h:119
bool IsVertical() const
Definition eda_angle.h:147
EDA_ANGLE & operator-=(const EDA_ANGLE &aAngle)
Definition eda_angle.h:326
EDA_ANGLE Round(int digits) const
Definition eda_angle.h:312
EDA_ANGLE & operator+=(const EDA_ANGLE &aAngle)
Definition eda_angle.h:320
EDA_ANGLE(const VECTOR2D &aVector)
Definition eda_angle.h:71
EDA_ANGLE(double aValue, EDA_ANGLE_T aAngleType)
Angles can be created in degrees, 1/10ths of a degree, or radians, and read as any of the angle types...
Definition eda_angle.h:45
static double foldToTwoPeriods(double aValue, double aPeriod)
Exactly remove whole periods from values beyond two periods, keeping the sign, so the normalization l...
Definition eda_angle.h:338
static constexpr double DEGREES_TO_RADIANS
Definition eda_angle.h:121
bool IsCardinal90() const
Definition eda_angle.cpp:57
double Cos() const
Definition eda_angle.h:196
double m_value
value in degrees
Definition eda_angle.h:350
static EDA_ANGLE Arctan(double x)
Definition eda_angle.h:221
static EDA_ANGLE Arcsin(double x)
Definition eda_angle.h:219
EDA_ANGLE Normalized() const
Definition eda_angle.h:241
static EDA_ANGLE Arccos(double x)
Definition eda_angle.h:217
An "orientation", held as an angle constrained to the range [0, 360) degrees.
Definition eda_angle.h:473
EDA_ANGLE GetAngle() const
Definition eda_angle.h:533
EDA_ORIENTATION operator-() const
Definition eda_angle.h:506
EDA_ORIENTATION & operator+=(const EDA_ANGLE &aAngle)
Definition eda_angle.h:521
EDA_ORIENTATION(const EDA_ANGLE &aAngle)
Definition eda_angle.h:477
EDA_ORIENTATION operator-(const EDA_ANGLE &aAngle) const
Subtract an EDA_ANGLE from this orientation, wrapping around the [0, 360) range.
Definition eda_angle.h:501
void SetAngle(const EDA_ANGLE &aAngle)
Definition eda_angle.h:535
EDA_ANGLE operator-(const EDA_ORIENTATION &aOther) const
The difference of two orientations is a signed rotation amount, not an orientation.
Definition eda_angle.h:516
EDA_ANGLE m_angle
Definition eda_angle.h:546
bool operator==(const EDA_ORIENTATION &aOther) const =default
EDA_ORIENTATION()=default
EDA_ORIENTATION & operator=(const EDA_ANGLE &aAngle)
Assign a plain angle, normalising it.
Definition eda_angle.h:482
EDA_ORIENTATION operator+(const EDA_ANGLE &aAngle) const
Add an EDA_ANGLE to this orientation, wrapping around the [0, 360) range.
Definition eda_angle.h:493
EDA_ORIENTATION & operator-=(const EDA_ANGLE &aAngle)
Definition eda_angle.h:527
EDA_ANGLE operator/(const EDA_ANGLE &aAngleA, double aOperator)
Definition eda_angle.h:379
bool operator>(const EDA_ANGLE &aAngleA, const EDA_ANGLE &aAngleB)
Definition eda_angle.h:405
bool operator>=(const EDA_ANGLE &aAngleA, const EDA_ANGLE &aAngleB)
Definition eda_angle.h:423
EDA_ANGLE operator*(const EDA_ANGLE &aAngleA, double aOperator)
Definition eda_angle.h:373
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
bool operator<=(const EDA_ANGLE &aAngleA, const EDA_ANGLE &aAngleB)
Definition eda_angle.h:417
std::ostream & operator<<(std::ostream &aStream, const EDA_ANGLE &aAngle)
Definition eda_angle.h:429
EDA_ANGLE_T
Definition eda_angle.h:28
@ TENTHS_OF_A_DEGREE_T
Definition eda_angle.h:29
@ RADIANS_T
Definition eda_angle.h:31
@ DEGREES_T
Definition eda_angle.h:30
EDA_ANGLE operator-(const EDA_ANGLE &aAngle)
Definition eda_angle.h:355
bool operator<(const EDA_ANGLE &aAngleA, const EDA_ANGLE &aAngleB)
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:445
bool operator==(const EDA_ANGLE &aAngleA, const EDA_ANGLE &aAngleB)
Definition eda_angle.h:391
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:449
static constexpr EDA_ANGLE ANGLE_270
Definition eda_angle.h:453
static constexpr EDA_ANGLE FULL_CIRCLE
Definition eda_angle.h:446
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:454
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:452
EDA_ANGLE operator+(const EDA_ANGLE &aAngleA, const EDA_ANGLE &aAngleB)
Definition eda_angle.h:367
static constexpr EDA_ANGLE ANGLE_135
Definition eda_angle.h:451
bool operator!=(const EDA_ANGLE &aAngleA, const EDA_ANGLE &aAngleB)
Definition eda_angle.h:398
STL namespace.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
bool equals(glm::mat< L, C, T, Q > const &aFirst, glm::mat< L, C, T, Q > const &aSecond, T aEpsilon=static_cast< T >(FLT_EPSILON *10))
Template to compare two glm::mat<T> values for equality within a required epsilon.
#define M_PI
VECTOR2< double > VECTOR2D
Definition vector2d.h:707