KiCad PCB EDA Suite
Loading...
Searching...
No Matches
ruler_item.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
23#include <layer_ids.h>
24#include <gal/painter.h>
25#include <view/view.h>
26#include <trigo.h>
27
28using namespace KIGFX::PREVIEW;
29
30static const double maxTickDensity = 10.0; // min pixels between tick marks
31static const double midTickLengthFactor = 1.5;
32static const double majorTickLengthFactor = 2.5;
33
34
35/*
36 * It would be nice to know why Cairo seems to have an opposite layer order from GAL, but
37 * only when drawing RULER_ITEMs (the TWO_POINT_ASSISTANT and ARC_ASSISTANT are immune from
38 * this issue).
39 *
40 * Until then, this egregious hack...
41 */
42static int getShadowLayer( KIGFX::GAL* aGal )
43{
44 if( aGal->IsCairoEngine() )
46 else
47 return LAYER_GP_OVERLAY;
48}
49
50
51static double getTickLineWidth( const TEXT_DIMS& textDims, bool aDrawingDropShadows )
52{
53 double width = textDims.StrokeWidth * 0.8;
54
55 if( aDrawingDropShadows )
56 width += textDims.ShadowWidth;
57
58 return width;
59}
60
61
67{
68 double divisionBase;
70 int midStep;
71};
72
73
74static TICK_FORMAT getTickFormatForScale( double aScale, double& aTickSpace, EDA_UNITS aUnits )
75{
76 // simple 1/2/5 scales per decade
77 static std::vector<TICK_FORMAT> tickFormats =
78 {
79 { 2, 10, 5 }, // |....:....|
80 { 2, 5, 0 }, // |....|
81 { 2.5, 2, 0 }, // |.|.|
82 };
83
84 // could start at a set number of MM, but that's not available in common
85 aTickSpace = 1;
86
87 // Convert to a round (mod-10) number of mils for imperial units
88 if( EDA_UNIT_UTILS::IsImperialUnit( aUnits ) )
89 aTickSpace *= 2.54;
90
91 int tickFormat = 0;
92
93 while( true )
94 {
95 const auto pixelSpace = aTickSpace * aScale;
96
97 if( pixelSpace >= maxTickDensity )
98 break;
99
100 tickFormat = ( tickFormat + 1 ) % tickFormats.size();
101 aTickSpace *= tickFormats[tickFormat].divisionBase;
102 }
103
104 return tickFormats[tickFormat];
105}
106
107
117void drawTicksAlongLine( KIGFX::VIEW* aView, const VECTOR2D& aOrigin, const VECTOR2D& aLine,
118 double aMinorTickLen, const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits,
119 bool aDrawingDropShadows )
120{
121 KIGFX::GAL* gal = aView->GetGAL();
123 double tickSpace;
124 TICK_FORMAT tickFormat = getTickFormatForScale( gal->GetWorldScale(), tickSpace, aUnits );
125 double majorTickLen = aMinorTickLen * ( majorTickLengthFactor + 1 );
126 VECTOR2D tickLine = aLine;
127
128 RotatePoint( tickLine, ANGLE_90 );
129
130 // number of ticks in whole ruler
131 int numTicks = (int) std::ceil( aLine.EuclideanNorm() / tickSpace );
132
133 // work out which way up the tick labels go
134 TEXT_DIMS labelDims = GetConstantGlyphHeight( gal, -1 );
135 EDA_ANGLE labelAngle = - EDA_ANGLE( tickLine );
136 VECTOR2I labelOffset = tickLine.Resize( majorTickLen );
137
138 // text is left (or right) aligned, so shadow text need a small offset to be draw
139 // around the basic text
140 int shadowXoffset = 0;
141
142 if( aDrawingDropShadows )
143 {
144 labelDims.StrokeWidth += 2 * labelDims.ShadowWidth;
145 shadowXoffset = labelDims.ShadowWidth;
146
147 // Due to the fact a shadow text is drawn left or right aligned,
148 // it needs an offset = shadowXoffset to be drawn at the same place as normal text
149 // But for some reason we need to slightly modify this offset
150 // for a better look for KiCad font (better alignment of shadow shape)
151 const float adjust = 1.2f; // Value chosen after tests
152 shadowXoffset *= adjust;
153 }
154
155 if( aView->IsMirroredX() )
156 {
157 labelOffset = -labelOffset;
158 shadowXoffset = -shadowXoffset;
159 }
160
161 TEXT_ATTRIBUTES labelAttrs;
162 labelAttrs.m_Size = labelDims.GlyphSize;
163 labelAttrs.m_StrokeWidth = labelDims.StrokeWidth;
164 labelAttrs.m_Mirrored = aView->IsMirroredX(); // Prevent text mirrored when view is mirrored
165
166 if( EDA_ANGLE( aLine ) > ANGLE_0 )
167 {
168 labelAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
169 labelAttrs.m_Angle = labelAngle;
170
171 // Adjust the text position of the shadow shape:
172 labelOffset.x -= shadowXoffset * labelAttrs.m_Angle.Cos();;
173 labelOffset.y += shadowXoffset * labelAttrs.m_Angle.Sin();;
174 }
175 else
176 {
177 labelAttrs.m_Halign = GR_TEXT_H_ALIGN_RIGHT;
178 labelAttrs.m_Angle = labelAngle + ANGLE_180;
179
180 // Adjust the text position of the shadow shape:
181 labelOffset.x += shadowXoffset * labelAttrs.m_Angle.Cos();;
182 labelOffset.y -= shadowXoffset * labelAttrs.m_Angle.Sin();;
183 }
184
185 BOX2D viewportD = aView->GetViewport();
186 BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
187
188 viewport.Inflate( majorTickLen * 2 ); // Doesn't have to be accurate, just big enough not
189 // to exclude anything that should be partially drawn
190
191 int isign = aView->IsMirroredX() ? -1 : 1;
192
193 for( int i = 0; i < numTicks; ++i )
194 {
195 const VECTOR2D tickPos = aOrigin + aLine.Resize( tickSpace * i );
196
197 if( !viewport.Contains( tickPos ) )
198 continue;
199
200 double length = aMinorTickLen;
201 bool drawLabel = false;
202
203 if( i % tickFormat.majorStep == 0 )
204 {
205 drawLabel = true;
206 length *= majorTickLengthFactor;
207 }
208 else if( tickFormat.midStep && i % tickFormat.midStep == 0 )
209 {
210 drawLabel = true;
211 length *= midTickLengthFactor;
212 }
213
214 gal->SetLineWidth( labelAttrs.m_StrokeWidth / 2 );
215 gal->DrawLine( tickPos, tickPos + tickLine.Resize( length*isign ) );
216
217 if( drawLabel )
218 {
219 wxString label = DimensionLabel( "", tickSpace * i, aIuScale, aUnits, false );
220 font->Draw( gal, label, tickPos + labelOffset, labelAttrs, KIFONT::METRICS::Default() );
221 }
222 }
223}
224
225
236void drawBacksideTicks( KIGFX::VIEW* aView, const VECTOR2D& aOrigin, const VECTOR2D& aLine,
237 double aTickLen, int aNumDivisions, bool aDrawingDropShadows )
238{
239 KIGFX::GAL* gal = aView->GetGAL();
240 TEXT_DIMS textDims = GetConstantGlyphHeight( gal, -1 );
241 const double backTickSpace = aLine.EuclideanNorm() / aNumDivisions;
242 VECTOR2D backTickVec = aLine;
243 int isign = aView->IsMirroredX() ? -1 : 1;
244
245 RotatePoint( backTickVec, -ANGLE_90 );
246 backTickVec = backTickVec.Resize( aTickLen * isign );
247
248 BOX2D viewportD = aView->GetViewport();
249 BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
250
251 viewport.Inflate( aTickLen * 4 ); // Doesn't have to be accurate, just big enough not to
252 // exclude anything that should be partially drawn
253
254 for( int i = 0; i < aNumDivisions + 1; ++i )
255 {
256 const VECTOR2D backTickPos = aOrigin + aLine.Resize( backTickSpace * i );
257
258 if( !viewport.Contains( backTickPos ) )
259 continue;
260
261 gal->SetLineWidth( getTickLineWidth( textDims, aDrawingDropShadows ) );
262 gal->DrawLine( backTickPos, backTickPos + backTickVec );
263 }
264}
265
266
268 EDA_UNITS userUnits, bool aFlipX, bool aFlipY )
269 : EDA_ITEM( NOT_USED ), // Never added to anything - just a preview
270 m_geomMgr( aGeomMgr ),
271 m_userUnits( userUnits ),
272 m_iuScale( aIuScale ),
273 m_flipX( aFlipX ),
274 m_flipY( aFlipY )
275{
276}
277
278
280{
281 BOX2I tmp;
282
283 if( m_geomMgr.GetOrigin() == m_geomMgr.GetEnd() )
284 return tmp;
285
286 // this is an edit-time artefact; no reason to try and be smart with the bounding box
287 // (besides, we can't tell the text extents without a view to know what the scale is)
288 tmp.SetMaximum();
289 return tmp;
290}
291
292
293std::vector<int> RULER_ITEM::ViewGetLayers() const
294{
295 std::vector<int> layers{ LAYER_SELECT_OVERLAY, LAYER_GP_OVERLAY };
296 return layers;
297}
298
299
300void RULER_ITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
301{
302 KIGFX::GAL* gal = aView->GetGAL();
303 RENDER_SETTINGS* rs = aView->GetPainter()->GetSettings();
304 bool drawingDropShadows = ( aLayer == getShadowLayer( gal ) );
305
307 gal->SetLayerDepth( gal->GetMinDepth() );
308
309 VECTOR2D origin = m_geomMgr.GetOrigin();
310 VECTOR2D end = m_geomMgr.GetEnd();
311
312 gal->SetIsStroke( true );
313 gal->SetIsFill( false );
314 gal->SetTextMirrored( false );
315
316 if( m_color )
317 gal->SetStrokeColor( *m_color );
318 else
320
321 if( drawingDropShadows )
323
324 gal->ResetTextAttributes();
325 TEXT_DIMS textDims = GetConstantGlyphHeight( gal );
326
327 // draw the main line from the origin to cursor
328 gal->SetLineWidth( getTickLineWidth( textDims, drawingDropShadows ) );
329 gal->DrawLine( origin, end );
330
331 VECTOR2D rulerVec( end - origin );
332
333 wxArrayString cursorStrings = GetDimensionStrings();
334
335 // Choose a text quadrant that keeps the measurement text on-screen while avoiding
336 // overlapping the ruler geometry. Start with the preferred direction (away from the
337 // origin) and fall back to other quadrants as needed to keep the label visible.
338 int prefX = rulerVec.y < 0.0 ? -1 : 1;
339 int prefY = rulerVec.x < 0.0 ? 1 : -1;
340
341 double scale = gal->GetWorldScale();
342
343 TEXT_DIMS dims = GetConstantGlyphHeight( gal );
345 double width = 0.0;
346
347 for( const wxString& s : cursorStrings )
348 {
349 VECTOR2I extents = font->StringBoundaryLimits( s, dims.GlyphSize, dims.StrokeWidth, false, false,
351 width = std::max( width, (double) extents.x );
352 }
353
354 double height = dims.LinePitch * cursorStrings.size();
355
356 // Convert to screen coordinates for visibility checks
357 VECTOR2D cursorScreen = gal->ToScreen( end );
358 VECTOR2I screenSize = gal->GetScreenPixelSize();
359 double offsetX = 15.0; // same as DrawTextNextToCursor()
360 double offsetY = dims.LinePitch * scale; // vertical spacing from cursor
361
362 auto fits =
363 [&]( int sx, int sy )
364 {
365 double left, right, top, bottom;
366 double xStart = cursorScreen.x + ( sx < 0 ? offsetX : -offsetX );
367
368 if( sx < 0 )
369 {
370 left = xStart;
371 right = left + width * scale;
372 }
373 else
374 {
375 right = xStart;
376 left = right - width * scale;
377 }
378
379 if( sy > 0 ) // above cursor
380 {
381 bottom = cursorScreen.y - offsetY;
382 top = bottom - height * scale;
383 }
384 else // below cursor
385 {
386 top = cursorScreen.y + offsetY;
387 bottom = top + height * scale;
388 }
389
390 return left >= 0 && right <= screenSize.x && top >= 0 && bottom <= screenSize.y;
391 };
392
393 std::vector<VECTOR2I> candidates = { { prefX, prefY }, { -prefX, prefY },
394 { prefX, -prefY }, { -prefX, -prefY } };
395
396 VECTOR2I chosen = candidates[0];
397 double bestDot = -1.0;
398
399 for( const VECTOR2I& c : candidates )
400 {
401 double dot = c.x * prefX + c.y * prefY;
402
403 if( dot >= 0 && fits( c.x, c.y ) )
404 {
405 if( dot > bestDot )
406 {
407 bestDot = dot;
408 chosen = c;
409 }
410 }
411 }
412
413 VECTOR2D quadrant( chosen.x, chosen.y );
414 DrawTextNextToCursor( aView, end, quadrant, cursorStrings, drawingDropShadows );
415
416 // basic tick size
417 double minorTickLen = 5.0 / gal->GetWorldScale();
418 double majorTickLen = minorTickLen * majorTickLengthFactor;
419
420 minorTickLen = std::min( minorTickLen, (double) INT_MAX / 2.0 );
421 majorTickLen = std::min( majorTickLen, (double) INT_MAX / 2.0 );
422
423 if( m_showTicks )
424 {
425 drawTicksAlongLine( aView, origin, rulerVec, minorTickLen, m_iuScale, m_userUnits, drawingDropShadows );
426 drawBacksideTicks( aView, origin, rulerVec, majorTickLen, 2, drawingDropShadows );
427 }
428
430 {
431 const EDA_ANGLE arrowAngle{ 30.0 };
432 VECTOR2D arrowHead = rulerVec;
433 RotatePoint( arrowHead, arrowAngle );
434 arrowHead = arrowHead.Resize( majorTickLen );
435
436 gal->DrawLine( end, end - arrowHead );
437
438 arrowHead = rulerVec;
439 RotatePoint( arrowHead, -arrowAngle );
440 arrowHead = arrowHead.Resize( majorTickLen );
441
442 gal->DrawLine( end, end - arrowHead );
443 }
444 else
445 {
446 // draw the back of the origin "crosshair"
447 gal->DrawLine( origin, origin + rulerVec.Resize( -minorTickLen * midTickLengthFactor ) );
448 }
449}
450
451
453{
454 const VECTOR2D rulerVec = m_geomMgr.GetEnd() - m_geomMgr.GetOrigin();
455 VECTOR2D temp = rulerVec;
456
457 if( m_flipX )
458 temp.x = -temp.x;
459
460 if( m_flipY )
461 temp.y = -temp.y;
462
463 wxArrayString cursorStrings;
464
465 cursorStrings.push_back( DimensionLabel( "x", temp.x, m_iuScale, m_userUnits ) );
466 cursorStrings.push_back( DimensionLabel( "y", temp.y, m_iuScale, m_userUnits ) );
467
468 cursorStrings.push_back( DimensionLabel( "r", rulerVec.EuclideanNorm(), m_iuScale, m_userUnits ) );
469
470 EDA_ANGLE angle = -EDA_ANGLE( rulerVec );
471 cursorStrings.push_back( DimensionLabel( wxString::FromUTF8( "θ" ), angle.AsDegrees(), m_iuScale,
473 return cursorStrings;
474}
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
BOX2< VECTOR2D > BOX2D
Definition box2.h:919
constexpr const Vec & GetPosition() const
Definition box2.h:207
constexpr void SetMaximum()
Definition box2.h:76
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:554
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:164
constexpr const SizeVec & GetSize() const
Definition box2.h:202
double Sin() const
Definition eda_angle.h:178
double AsDegrees() const
Definition eda_angle.h:116
double Cos() const
Definition eda_angle.h:197
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:37
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:94
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:143
void Draw(KIGFX::GAL *aGal, const wxString &aText, const VECTOR2I &aPosition, const VECTOR2I &aCursor, const TEXT_ATTRIBUTES &aAttributes, const METRICS &aFontMetrics, std::optional< VECTOR2I > aMousePos=std::nullopt, wxString *aActiveUrl=nullptr) const
Draw a string.
Definition font.cpp:246
VECTOR2I StringBoundaryLimits(const wxString &aText, const VECTOR2I &aSize, int aThickness, bool aBold, bool aItalic, const METRICS &aFontMetrics) const
Compute the boundary limits of aText (the bounding box of all shapes).
Definition font.cpp:447
static const METRICS & Default()
Definition font.cpp:48
Attribute save/restore for GAL attributes.
Abstract interface for drawing on a 2D-surface.
virtual void SetLayerDepth(double aLayerDepth)
Set the depth of the layer (position on the z-axis)
virtual void SetIsFill(bool aIsFillEnabled)
Enable/disable fill.
VECTOR2D ToScreen(const VECTOR2D &aPoint) const
Compute the point position in screen coordinates from given world coordinates.
const COLOR4D & GetStrokeColor() const
Get the stroke color.
void ResetTextAttributes()
Reset text attributes to default styling.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
void SetTextMirrored(const bool aMirrored)
virtual void SetStrokeColor(const COLOR4D &aColor)
Set the stroke color.
virtual void SetIsStroke(bool aIsStrokeEnabled)
Enable/disable stroked outlines.
virtual bool IsCairoEngine()
Return true if the GAL engine is a Cairo based type.
virtual void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a line.
double GetMinDepth() const
const VECTOR2I & GetScreenPixelSize() const
Return GAL canvas size in pixels.
double GetWorldScale() const
Get the world scale.
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
std::vector< int > ViewGetLayers() const override
RULER_ITEM(const TWO_POINT_GEOMETRY_MANAGER &m_geomMgr, const EDA_IU_SCALE &aIuScale, EDA_UNITS userUnits, bool aFlipX, bool aFlipY)
Return the bounding box of the item covering all its layers.
const TWO_POINT_GEOMETRY_MANAGER & m_geomMgr
Definition ruler_item.h:97
wxArrayString GetDimensionStrings() const
Get the strings for the dimensions of the ruler.
std::optional< COLOR4D > m_color
Definition ruler_item.h:102
void ViewDraw(int aLayer, KIGFX::VIEW *aView) const override final
Draw the parts of the object belonging to layer aLayer.
const BOX2I ViewBBox() const override
Return the all the layers within the VIEW the object is painted on.
const EDA_IU_SCALE & m_iuScale
Definition ruler_item.h:99
Represent a very simple geometry manager for items that have a start and end point.
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const COLOR4D & GetLayerColor(int aLayer) const
Return the color used to draw a layer.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:63
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition view.cpp:597
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:207
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition view.h:255
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition view.h:225
GR_TEXT_H_ALIGN_T m_Halign
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:279
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:381
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
EDA_UNITS
Definition eda_units.h:44
@ LAYER_GP_OVERLAY
General purpose overlay.
Definition layer_ids.h:275
@ LAYER_AUX_ITEMS
Auxiliary items (guides, rule, etc).
Definition layer_ids.h:279
@ LAYER_SELECT_OVERLAY
Selected items overlay.
Definition layer_ids.h:276
KICOMMON_API bool IsImperialUnit(EDA_UNITS aUnit)
Definition eda_units.cpp:43
COLOR4D GetShadowColor(const COLOR4D &aColor)
void DrawTextNextToCursor(KIGFX::VIEW *aView, const VECTOR2D &aCursorPos, const VECTOR2D &aTextQuadrant, const wxArrayString &aStrings, bool aDrawingDropShadows)
Draw strings next to the cursor.
wxString DimensionLabel(const wxString &prefix, double aVal, const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, bool aIncludeUnits=true)
Get a formatted string showing a dimension to a sane precision with an optional prefix and unit suffi...
TEXT_DIMS GetConstantGlyphHeight(KIGFX::GAL *aGal, int aRelativeSize=0)
Set the GAL glyph height to a constant scaled value, so that it always looks the same on screen.
static double getTickLineWidth(const TEXT_DIMS &textDims, bool aDrawingDropShadows)
static const double maxTickDensity
static TICK_FORMAT getTickFormatForScale(double aScale, double &aTickSpace, EDA_UNITS aUnits)
void drawBacksideTicks(KIGFX::VIEW *aView, const VECTOR2D &aOrigin, const VECTOR2D &aLine, double aTickLen, int aNumDivisions, bool aDrawingDropShadows)
Draw simple ticks on the back of a line such that the line is divided into n parts.
void drawTicksAlongLine(KIGFX::VIEW *aView, const VECTOR2D &aOrigin, const VECTOR2D &aLine, double aMinorTickLen, const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, bool aDrawingDropShadows)
Draw labeled ticks on a line.
static const double majorTickLengthFactor
static int getShadowLayer(KIGFX::GAL *aGal)
static const double midTickLengthFactor
const int scale
Description of a "tick format" for a scale factor - how many ticks there are between medium/major tic...
int majorStep
ticks between major ticks
double divisionBase
multiple from the last scale
int midStep
ticks between medium ticks (0 if no medium ticks)
KIBIS top(path, &reporter)
VECTOR2I end
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:225
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:72
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682