KiCad PCB EDA Suite
Loading...
Searching...
No Matches
stroke_params.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 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.org/licenses/>.
18 */
19
20#include "stroke_params.h"
22
23#include <base_units.h>
24#include <charconv>
25#include <string_utils.h>
26#include <render_settings.h>
28#include <geometry/shape.h>
29#include <geometry/shape_rect.h>
33#include <macros.h>
34#include <trigo.h>
35#include <widgets/msgpanel.h>
36
37using namespace STROKEPARAMS_T;
38
39
40const std::map<LINE_STYLE, struct LINE_STYLE_DESC> lineTypeNames = {
41 { LINE_STYLE::SOLID, { _( "Solid" ), BITMAPS::stroke_solid } },
42 { LINE_STYLE::DASH, { _( "Dashed" ), BITMAPS::stroke_dash } },
43 { LINE_STYLE::DOT, { _( "Dotted" ), BITMAPS::stroke_dot } },
44 { LINE_STYLE::DASHDOT, { _( "Dash-Dot" ), BITMAPS::stroke_dashdot } },
45 { LINE_STYLE::DASHDOTDOT, { _( "Dash-Dot-Dot" ), BITMAPS::stroke_dashdotdot } }
46};
47
48
49void STROKE_PARAMS::Stroke( const SHAPE* aShape, LINE_STYLE aLineStyle, int aWidth,
50 const KIGFX::RENDER_SETTINGS* aRenderSettings,
51 const std::function<void( const VECTOR2I& a,
52 const VECTOR2I& b )>& aStroker )
53{
54 double strokes[6] = { aWidth * 1.0, aWidth * 1.0, aWidth * 1.0, aWidth * 1.0, aWidth * 1.0,
55 aWidth * 1.0 };
56 int wrapAround = 6;
57
58 switch( aLineStyle )
59 {
61 strokes[0] = aRenderSettings->GetDashLength( aWidth );
62 strokes[1] = aRenderSettings->GetGapLength( aWidth );
63 wrapAround = 2;
64 break;
65 case LINE_STYLE::DOT:
66 strokes[0] = aRenderSettings->GetDotLength( aWidth );
67 strokes[1] = aRenderSettings->GetGapLength( aWidth );
68 wrapAround = 2;
69 break;
71 strokes[0] = aRenderSettings->GetDashLength( aWidth );
72 strokes[1] = aRenderSettings->GetGapLength( aWidth );
73 strokes[2] = aRenderSettings->GetDotLength( aWidth );
74 strokes[3] = aRenderSettings->GetGapLength( aWidth );
75 wrapAround = 4;
76 break;
78 strokes[0] = aRenderSettings->GetDashLength( aWidth );
79 strokes[1] = aRenderSettings->GetGapLength( aWidth );
80 strokes[2] = aRenderSettings->GetDotLength( aWidth );
81 strokes[3] = aRenderSettings->GetGapLength( aWidth );
82 strokes[4] = aRenderSettings->GetDotLength( aWidth );
83 strokes[5] = aRenderSettings->GetGapLength( aWidth );
84 wrapAround = 6;
85 break;
86 default:
87 UNIMPLEMENTED_FOR( lineTypeNames.at( aLineStyle ).name );
88 }
89
90 switch( aShape->Type() )
91 {
92 case SH_RECT:
93 {
94 SHAPE_LINE_CHAIN outline = static_cast<const SHAPE_RECT*>( aShape )->Outline();
95 std::set<size_t> arcsHandled;
96
97 for( int ii = 0; ii < outline.SegmentCount(); ++ii )
98 {
99 if( outline.IsArcSegment( ii ) )
100 {
101 size_t arcIndex = outline.ArcIndex( ii );
102
103 if( !arcsHandled.contains( arcIndex ) )
104 {
105 arcsHandled.insert( arcIndex );
106 const SHAPE_ARC& arc( outline.Arc( arcIndex ) );
107 STROKE_PARAMS::Stroke( &arc, aLineStyle, aWidth, aRenderSettings, aStroker );
108 }
109 }
110 else
111 {
112 const SEG& seg = outline.GetSegment( ii );
113 SHAPE_SEGMENT line( seg.A, seg.B );
114 STROKE_PARAMS::Stroke( &line, aLineStyle, aWidth, aRenderSettings, aStroker );
115 }
116 }
117
118 for( int jj = 0; jj < (int) outline.ArcCount(); ++jj )
119 {
120 const SHAPE_ARC& arc( outline.Arc( jj ) );
121 STROKE_PARAMS::Stroke( &arc, aLineStyle, aWidth, aRenderSettings, aStroker );
122 }
123
124 break;
125 }
126
127 case SH_SIMPLE:
128 {
129 const SHAPE_SIMPLE* poly = static_cast<const SHAPE_SIMPLE*>( aShape );
130
131 for( size_t ii = 0; ii < poly->GetSegmentCount(); ++ii )
132 {
133 SEG seg = poly->GetSegment( (int) ii );
134 SHAPE_SEGMENT line( seg.A, seg.B );
135 STROKE_PARAMS::Stroke( &line, aLineStyle, aWidth, aRenderSettings, aStroker );
136 }
137
138 break;
139 }
140
141 case SH_SEGMENT:
142 {
143 const SHAPE_SEGMENT* line = static_cast<const SHAPE_SEGMENT*>( aShape );
144
145 VECTOR2D start = line->GetSeg().A;
146 VECTOR2D end = line->GetSeg().B;
147 BOX2I clip( start, KiROUND( end.x - start.x, end.y - start.y ) );
148 clip.Normalize();
149
150 double theta = atan2( end.y - start.y, end.x - start.x );
151
152 for( size_t i = 0; i < 10000; ++i )
153 {
154 // Calculations MUST be done in doubles to keep from accumulating rounding
155 // errors as we go.
156 VECTOR2D next( start.x + strokes[ i % wrapAround ] * cos( theta ),
157 start.y + strokes[ i % wrapAround ] * sin( theta ) );
158
159 // Drawing each segment can be done rounded to ints.
160 VECTOR2I a = KiROUND( start );
161 VECTOR2I b = KiROUND( next );
162
163 if( ClipLine( &clip, a.x, a.y, b.x, b.y ) )
164 break;
165 else if( i % 2 == 0 )
166 aStroker( a, b );
167
168 start = next;
169 }
170
171 break;
172 }
173
174 case SH_ARC:
175 {
176 const SHAPE_ARC* arc = static_cast<const SHAPE_ARC*>( aShape );
177
178 double r = arc->GetRadius();
179 double C = 2.0 * M_PI * r;
180 VECTOR2I center = arc->GetCenter();
181 VECTOR2D startRadial( arc->GetP0() - center );
182 EDA_ANGLE startAngle( startRadial );
183 VECTOR2D endRadial( arc->GetP1() - center );
184 EDA_ANGLE arcEndAngle( endRadial );
185
186 if( arcEndAngle == startAngle )
187 arcEndAngle = startAngle + ANGLE_360; // ring, not null
188
189 if( startAngle > arcEndAngle )
190 {
191 if( arcEndAngle < ANGLE_0 )
192 arcEndAngle = arcEndAngle.Normalize();
193 else
194 startAngle = startAngle.Normalize() - ANGLE_360;
195 }
196
197 wxASSERT( startAngle < arcEndAngle );
198
199 EDA_ANGLE angleIncrement = EDA_ANGLE( 0.5, DEGREES_T );
200
201 for( size_t i = 0; i < 10000 && startAngle < arcEndAngle; ++i )
202 {
203 EDA_ANGLE theta = ANGLE_360 * strokes[ i % wrapAround ] / C;
204 EDA_ANGLE endAngle = std::min( startAngle + theta, arcEndAngle );
205
206 if( i % 2 == 0 )
207 {
208 if( ( ( aLineStyle == LINE_STYLE::DASHDOT || aLineStyle == LINE_STYLE::DASHDOTDOT )
209 && i % wrapAround == 0 )
210 || aLineStyle == LINE_STYLE::DASH )
211 {
212 for( EDA_ANGLE currentAngle = startAngle; currentAngle < endAngle;
213 currentAngle += angleIncrement )
214 {
215 VECTOR2I a( center.x + KiROUND( r * currentAngle.Cos() ),
216 center.y + KiROUND( r * currentAngle.Sin() ) );
217
218 // Calculate the next angle step, ensuring it doesn't exceed the endAngle
219 EDA_ANGLE nextAngle = currentAngle + angleIncrement;
220
221 if( nextAngle > endAngle )
222 {
223 nextAngle = endAngle; // Set nextAngle to endAngle if it exceeds
224 }
225
226 VECTOR2I b( center.x + KiROUND( r * nextAngle.Cos() ),
227 center.y + KiROUND( r * nextAngle.Sin() ) );
228
229 aStroker( a, b ); // Draw the segment as an arc
230 }
231 }
232 else
233 {
234 VECTOR2I a( center.x + KiROUND( r * startAngle.Cos() ),
235 center.y + KiROUND( r * startAngle.Sin() ) );
236 VECTOR2I b( center.x + KiROUND( r * endAngle.Cos() ),
237 center.y + KiROUND( r * endAngle.Sin() ) );
238
239 aStroker( a, b );
240 }
241 }
242
243 startAngle = endAngle;
244 }
245
246 break;
247 }
248
249 case SH_ELLIPSE:
250 {
251 const SHAPE_ELLIPSE* ellipse = static_cast<const SHAPE_ELLIPSE*>( aShape );
252
253 VECTOR2I center = ellipse->GetCenter();
254 double a = ellipse->GetMajorRadius();
255 double b = ellipse->GetMinorRadius();
256 EDA_ANGLE rotation = ellipse->GetRotation();
257 double cosRot = rotation.Cos();
258 double sinRot = rotation.Sin();
259
260 EDA_ANGLE startAngle = ellipse->IsArc() ? ellipse->GetStartAngle() : ANGLE_0;
261 EDA_ANGLE endAngle = ellipse->IsArc() ? ellipse->GetEndAngle() : ANGLE_360;
262
263 if( endAngle == startAngle )
264 endAngle = startAngle + ANGLE_360;
265
266 if( startAngle > endAngle )
267 {
268 if( endAngle < ANGLE_0 )
269 endAngle = endAngle.Normalize();
270 else
271 startAngle = startAngle.Normalize() - ANGLE_360;
272 }
273
274 auto evalRad = [&]( double thetaRad ) -> VECTOR2D
275 {
276 double x = a * std::cos( thetaRad );
277 double y = b * std::sin( thetaRad );
278 return VECTOR2D( center.x + x * cosRot - y * sinRot, center.y + x * sinRot + y * cosRot );
279 };
280
281 auto evalAngle = [&]( const EDA_ANGLE& theta ) -> VECTOR2I
282 {
283 double x = a * theta.Cos();
284 double y = b * theta.Sin();
285 return VECTOR2I( center.x + KiROUND( x * cosRot - y * sinRot ),
286 center.y + KiROUND( x * sinRot + y * cosRot ) );
287 };
288
289 // Place strokes by arclength rather than angle so the pattern stays
290 // visually uniform on highly curved ellipses.
291 const double startRad = startAngle.AsRadians();
292 const double endRad = endAngle.AsRadians();
293 const int LUT_SIZE = 1024;
294 std::vector<double> arcLens( LUT_SIZE + 1 );
295
296 arcLens[0] = 0.0;
297 VECTOR2D prev = evalRad( startRad );
298
299 for( int j = 1; j <= LUT_SIZE; ++j )
300 {
301 double t = startRad + ( endRad - startRad ) * j / LUT_SIZE;
302 VECTOR2D curr = evalRad( t );
303 arcLens[j] = arcLens[j - 1] + ( curr - prev ).EuclideanNorm();
304 prev = curr;
305 }
306
307 const double totalLen = arcLens[LUT_SIZE];
308
309 auto angleAtLen = [&]( double s ) -> double
310 {
311 if( s >= totalLen )
312 return endRad;
313
314 if( s <= 0.0 )
315 return startRad;
316
317 auto it = std::upper_bound( arcLens.begin(), arcLens.end(), s );
318 int idx = std::distance( arcLens.begin(), it ) - 1;
319
320 double seg = arcLens[idx + 1] - arcLens[idx];
321 double frac = ( seg > 0 ) ? ( s - arcLens[idx] ) / seg : 0.0;
322
323 double t0 = startRad + ( endRad - startRad ) * idx / LUT_SIZE;
324 double t1 = startRad + ( endRad - startRad ) * ( idx + 1 ) / LUT_SIZE;
325
326 return t0 + frac * ( t1 - t0 );
327 };
328
329 EDA_ANGLE angleIncrement( 0.5, DEGREES_T );
330 double currentLen = 0.0;
331
332 for( size_t i = 0; i < 10000 && currentLen < totalLen; ++i )
333 {
334 double targetLen = std::min( currentLen + strokes[i % wrapAround], totalLen );
335
336 if( i % 2 == 0 )
337 {
338 EDA_ANGLE elemStart( angleAtLen( currentLen ), RADIANS_T );
339 EDA_ANGLE elemEnd( angleAtLen( targetLen ), RADIANS_T );
340
341 for( EDA_ANGLE c = elemStart; c < elemEnd; c += angleIncrement )
342 {
343 EDA_ANGLE n = std::min( c + angleIncrement, elemEnd );
344 aStroker( evalAngle( c ), evalAngle( n ) );
345 }
346 }
347
348 currentLen = targetLen;
349 }
350
351 break;
352 }
353
354 case SH_CIRCLE:
355 // A circle is always filled. A ring is represented by a 360° arc.
357
358 default:
360 }
361}
362
363
365{
366 wxString token;
367
368 switch( aStyle )
369 {
370 case LINE_STYLE::DASH: token = wxT( "dash" ); break;
371 case LINE_STYLE::DOT: token = wxT( "dot" ); break;
372 case LINE_STYLE::DASHDOT: token = wxT( "dash_dot" ); break;
373 case LINE_STYLE::DASHDOTDOT: token = wxT( "dash_dot_dot" ); break;
374 case LINE_STYLE::SOLID: token = wxT( "solid" ); break;
375 case LINE_STYLE::DEFAULT: token = wxT( "default" ); break;
376 }
377
378 return token;
379}
380
381
383 std::vector<MSG_PANEL_ITEM>& aList,
384 bool aIncludeStyle, bool aIncludeWidth )
385{
386 if( aIncludeStyle )
387 {
388 wxString msg = _( "Default" );
389
390 for( const auto& [ lineStyle, lineStyleDesc ] : lineTypeNames )
391 {
392 if( lineStyle == GetLineStyle() )
393 {
394 msg = lineStyleDesc.name;
395 break;
396 }
397 }
398
399 aList.emplace_back( _( "Line Style" ), msg );
400 }
401
402 if( aIncludeWidth )
403 aList.emplace_back( _( "Line Width" ), aUnitsProvider->MessageTextFromValue( GetWidth() ) );
404}
405
406
407void STROKE_PARAMS::Format( OUTPUTFORMATTER* aFormatter, const EDA_IU_SCALE& aIuScale ) const
408{
409 wxASSERT( aFormatter != nullptr );
410
412 {
413 aFormatter->Print( "(stroke (width %s) (type %s))",
414 EDA_UNIT_UTILS::FormatInternalUnits( aIuScale, GetWidth() ).c_str(),
416 }
417 else
418 {
419 aFormatter->Print( "(stroke (width %s) (type %s) (color %d %d %d %s))",
420 EDA_UNIT_UTILS::FormatInternalUnits( aIuScale, GetWidth() ).c_str(),
422 KiROUND( GetColor().r * 255.0 ),
423 KiROUND( GetColor().g * 255.0 ),
424 KiROUND( GetColor().b * 255.0 ),
425 FormatDouble2Str( GetColor().a ).c_str() );
426 }
427}
428
429
431{
432 for( T token = NextTok(); token != T_RIGHT; token = NextTok() )
433 {
434 if( token != T_LEFT )
435 Expecting( T_LEFT );
436
437 token = NextTok();
438
439 switch( token )
440 {
441 case T_width:
442 aStroke.SetWidth( KiROUND( parseDouble( "stroke width" ) * m_iuPerMM ) );
443 NeedRIGHT();
444 break;
445
446 case T_type:
447 {
448 token = NextTok();
449
450 switch( token )
451 {
452 case T_dash: aStroke.SetLineStyle( LINE_STYLE::DASH ); break;
453 case T_dot: aStroke.SetLineStyle( LINE_STYLE::DOT ); break;
454 case T_dash_dot: aStroke.SetLineStyle( LINE_STYLE::DASHDOT ); break;
455 case T_dash_dot_dot: aStroke.SetLineStyle( LINE_STYLE::DASHDOTDOT ); break;
456 case T_solid: aStroke.SetLineStyle( LINE_STYLE::SOLID ); break;
457 case T_default: aStroke.SetLineStyle( LINE_STYLE::DEFAULT ); break;
458 default:
459 Expecting( "solid, dash, dash_dot, dash_dot_dot, dot or default" );
460 }
461
462 NeedRIGHT();
463 break;
464 }
465
466 case T_color:
467 {
468 KIGFX::COLOR4D color;
469
470 color.r = parseInt( "red" ) / 255.0;
471 color.g = parseInt( "green" ) / 255.0;
472 color.b = parseInt( "blue" ) / 255.0;
473 color.a = std::clamp( parseDouble( "alpha" ), 0.0, 1.0 );
474
475 aStroke.SetColor( color );
476 NeedRIGHT();
477 break;
478 }
479
480 default:
481 Expecting( "width, type, or color" );
482 }
483 }
484}
485
486
487int STROKE_PARAMS_PARSER::parseInt( const char* aText )
488{
489 T token = NextTok();
490
491 if( token != T_NUMBER )
492 Expecting( aText );
493
494 return atoi( CurText() );
495}
496
497
498double STROKE_PARAMS_PARSER::parseDouble( const char* aText )
499{
500 T token = NextTok();
501
502 if( token != T_NUMBER )
503 Expecting( aText );
504
505 return DSNLEXER::parseDouble();
506}
507
508
@ stroke_dashdotdot
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:146
double parseDouble()
Parse the current token as an ASCII numeric string with possible leading whitespace into a double pre...
Definition dsnlexer.cpp:864
EDA_ANGLE Normalize()
Definition eda_angle.h:229
double Sin() const
Definition eda_angle.h:178
double AsRadians() const
Definition eda_angle.h:120
double Cos() const
Definition eda_angle.h:197
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
double r
Red component.
Definition color4d.h:393
double g
Green component.
Definition color4d.h:394
double a
Alpha component.
Definition color4d.h:396
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:402
double b
Blue component.
Definition color4d.h:395
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
double GetGapLength(int aLineWidth) const
double GetDotLength(int aLineWidth) const
double GetDashLength(int aLineWidth) const
An interface used to output 8 bit text in a convenient way.
Definition richio.h:295
int PRINTF_FUNC_N Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition richio.cpp:426
Definition seg.h:42
VECTOR2I A
Definition seg.h:49
VECTOR2I B
Definition seg.h:50
const VECTOR2I & GetP1() const
Definition shape_arc.h:119
double GetRadius() const
const VECTOR2I & GetP0() const
Definition shape_arc.h:118
const VECTOR2I & GetCenter() const
SHAPE_TYPE Type() const
Return the type of the shape.
Definition shape.h:100
int GetMajorRadius() const
const VECTOR2I & GetCenter() const
const EDA_ANGLE & GetStartAngle() const
const EDA_ANGLE & GetEndAngle() const
const EDA_ANGLE & GetRotation() const
bool IsArc() const
int GetMinorRadius() const
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
virtual const SEG GetSegment(int aIndex) const override
int SegmentCount() const
Return the number of segments in this line chain.
size_t ArcCount() const
bool IsArcSegment(size_t aSegment) const
const SEG & GetSeg() const
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
virtual const SEG GetSegment(int aIndex) const override
virtual size_t GetSegmentCount() const override
An abstract shape on 2D plane.
Definition shape.h:128
int parseInt(const char *aText)
void ParseStroke(STROKE_PARAMS &aStroke)
double parseDouble(const char *aText)
Simple container to manage line stroke parameters.
int GetWidth() const
void SetLineStyle(LINE_STYLE aLineStyle)
void SetWidth(int aWidth)
void SetColor(const KIGFX::COLOR4D &aColor)
void GetMsgPanelInfo(UNITS_PROVIDER *aUnitsProvider, std::vector< MSG_PANEL_ITEM > &aList, bool aIncludeStyle=true, bool aIncludeWidth=true)
LINE_STYLE GetLineStyle() const
void Format(OUTPUTFORMATTER *out, const EDA_IU_SCALE &aIuScale) const
KIGFX::COLOR4D GetColor() const
static void Stroke(const SHAPE *aShape, LINE_STYLE aLineStyle, int aWidth, const KIGFX::RENDER_SETTINGS *aRenderSettings, const std::function< void(const VECTOR2I &a, const VECTOR2I &b)> &aStroker)
static wxString GetLineStyleToken(LINE_STYLE aStyle)
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
@ RADIANS_T
Definition eda_angle.h:32
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
a few functions useful in geometry calculations.
bool ClipLine(const BOX2I *aClipBox, int &x1, int &y1, int &x2, int &y2)
Test if any part of a line falls within the bounds of a rectangle.
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition macros.h:83
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:96
Message panel definition file.
KICOMMON_API std::string FormatInternalUnits(const EDA_IU_SCALE &aIuScale, int aValue, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
Converts aValue from internal units to a string appropriate for writing to file.
CITER next(CITER it)
Definition ptree.cpp:124
@ SH_RECT
axis-aligned rectangle
Definition shape.h:47
@ SH_CIRCLE
circle
Definition shape.h:50
@ SH_SIMPLE
simple polygon
Definition shape.h:51
@ SH_ELLIPSE
ellipse or elliptical arc
Definition shape.h:57
@ SH_SEGMENT
line segment
Definition shape.h:48
@ SH_ARC
circular arc
Definition shape.h:54
static wxString SHAPE_TYPE_asString(SHAPE_TYPE a)
Definition shape.h:60
std::string FormatDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 This function is intended in...
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
const std::map< LINE_STYLE, struct LINE_STYLE_DESC > lineTypeNames
Conversion map between LINE_STYLE values and style names displayed.
const std::map< LINE_STYLE, struct LINE_STYLE_DESC > lineTypeNames
Conversion map between LINE_STYLE values and style names displayed.
LINE_STYLE
Dashed line types.
VECTOR2I center
VECTOR2I end
#define M_PI
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686