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 (C) 2017-2023 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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
27#include <layer_ids.h>
28#include <gal/painter.h>
29#include <view/view.h>
30#include <trigo.h>
31
32using namespace KIGFX::PREVIEW;
33
34static const double maxTickDensity = 10.0; // min pixels between tick marks
35static const double midTickLengthFactor = 1.5;
36static const double majorTickLengthFactor = 2.5;
37
38
39/*
40 * It would be nice to know why Cairo seems to have an opposite layer order from GAL, but
41 * only when drawing RULER_ITEMs (the TWO_POINT_ASSISTANT and ARC_ASSISTANT are immune from
42 * this issue).
43 *
44 * Until then, this egregious hack...
45 */
46static int getShadowLayer( KIGFX::GAL* aGal )
47{
48 if( aGal->IsCairoEngine() )
50 else
51 return LAYER_GP_OVERLAY;
52}
53
54
55static double getTickLineWidth( const TEXT_DIMS& textDims, bool aDrawingDropShadows )
56{
57 double width = textDims.StrokeWidth * 0.8;
58
59 if( aDrawingDropShadows )
60 width += textDims.ShadowWidth;
61
62 return width;
63}
64
65
71{
72 double divisionBase;
74 int midStep;
75};
76
77
78static TICK_FORMAT getTickFormatForScale( double aScale, double& aTickSpace, EDA_UNITS aUnits )
79{
80 // simple 1/2/5 scales per decade
81 static std::vector<TICK_FORMAT> tickFormats =
82 {
83 { 2, 10, 5 }, // |....:....|
84 { 2, 5, 0 }, // |....|
85 { 2.5, 2, 0 }, // |.|.|
86 };
87
88 // could start at a set number of MM, but that's not available in common
89 aTickSpace = 1;
90
91 // Convert to a round (mod-10) number of mils for imperial units
92 if( EDA_UNIT_UTILS::IsImperialUnit( aUnits ) )
93 aTickSpace *= 2.54;
94
95 int tickFormat = 0;
96
97 while( true )
98 {
99 const auto pixelSpace = aTickSpace * aScale;
100
101 if( pixelSpace >= maxTickDensity )
102 break;
103
104 tickFormat = ( tickFormat + 1 ) % tickFormats.size();
105 aTickSpace *= tickFormats[tickFormat].divisionBase;
106 }
107
108 return tickFormats[tickFormat];
109}
110
111
121void drawTicksAlongLine( KIGFX::VIEW* aView, const VECTOR2D& aOrigin, const VECTOR2D& aLine,
122 double aMinorTickLen, const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits, bool aDrawingDropShadows )
123{
124 KIGFX::GAL* gal = aView->GetGAL();
126 double tickSpace;
127 TICK_FORMAT tickFormat = getTickFormatForScale( gal->GetWorldScale(), tickSpace, aUnits );
128 double majorTickLen = aMinorTickLen * ( majorTickLengthFactor + 1 );
129 VECTOR2D tickLine = aLine;
130
131 RotatePoint( tickLine, ANGLE_90 );
132
133 // number of ticks in whole ruler
134 int numTicks = (int) std::ceil( aLine.EuclideanNorm() / tickSpace );
135
136 // work out which way up the tick labels go
137 TEXT_DIMS labelDims = GetConstantGlyphHeight( gal, -1 );
138 EDA_ANGLE labelAngle = - EDA_ANGLE( tickLine );
139 VECTOR2I labelOffset = tickLine.Resize( majorTickLen );
140
141 // text is left (or right) aligned, so shadow text need a small offset to be draw
142 // around the basic text
143 int shadowXoffset = 0;
144
145 if( aDrawingDropShadows )
146 {
147 labelDims.StrokeWidth += 2 * labelDims.ShadowWidth;
148 shadowXoffset = labelDims.ShadowWidth;
149 // Due to the fact a shadow text is drawn left or right aligned,
150 // it needs an offset = shadowXoffset to be drawn at the same place as normal text
151 // But for some reason we need to slightly modify this offset
152 // for a better look for KiCad font (better alignment of shadow shape)
153 const float adjust = 1.2f; // Value chosen after tests
154 shadowXoffset *= adjust;
155 }
156
157 if( aView->IsMirroredX() )
158 {
159 labelOffset = -labelOffset;
160 shadowXoffset = -shadowXoffset;
161 }
162
163 TEXT_ATTRIBUTES labelAttrs;
164 labelAttrs.m_Size = labelDims.GlyphSize;
165 labelAttrs.m_StrokeWidth = labelDims.StrokeWidth;
166 labelAttrs.m_Mirrored = aView->IsMirroredX(); // Prevent text mirrored when view is mirrored
167
168 if( EDA_ANGLE( aLine ) > ANGLE_0 )
169 {
170 labelAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
171 labelAttrs.m_Angle = labelAngle;
172
173 // Adjust the text position of the shadow shape:
174 labelOffset.x -= shadowXoffset * labelAttrs.m_Angle.Cos();;
175 labelOffset.y += shadowXoffset * labelAttrs.m_Angle.Sin();;
176 }
177 else
178 {
179 labelAttrs.m_Halign = GR_TEXT_H_ALIGN_RIGHT;
180 labelAttrs.m_Angle = labelAngle + ANGLE_180;
181
182 // Adjust the text position of the shadow shape:
183 labelOffset.x += shadowXoffset * labelAttrs.m_Angle.Cos();;
184 labelOffset.y -= shadowXoffset * labelAttrs.m_Angle.Sin();;
185 }
186
187 BOX2D viewportD = aView->GetViewport();
188 BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
189
190 viewport.Inflate( majorTickLen * 2 ); // Doesn't have to be accurate, just big enough not
191 // to exclude anything that should be partially drawn
192
193 int isign = aView->IsMirroredX() ? -1 : 1;
194
195 for( int i = 0; i < numTicks; ++i )
196 {
197 const VECTOR2D tickPos = aOrigin + aLine.Resize( tickSpace * i );
198
199 if( !viewport.Contains( tickPos ) )
200 continue;
201
202 double length = aMinorTickLen;
203 bool drawLabel = false;
204
205 if( i % tickFormat.majorStep == 0 )
206 {
207 drawLabel = true;
208 length *= majorTickLengthFactor;
209 }
210 else if( tickFormat.midStep && i % tickFormat.midStep == 0 )
211 {
212 drawLabel = true;
213 length *= midTickLengthFactor;
214 }
215
216 gal->SetLineWidth( labelAttrs.m_StrokeWidth / 2 );
217 gal->DrawLine( tickPos, tickPos + tickLine.Resize( length*isign ) );
218
219 if( drawLabel )
220 {
221 wxString label = DimensionLabel( "", tickSpace * i, aIuScale, aUnits, false );
222 font->Draw( gal, label, tickPos + labelOffset, labelAttrs, KIFONT::METRICS::Default() );
223 }
224 }
225}
226
227
238void drawBacksideTicks( KIGFX::VIEW* aView, const VECTOR2D& aOrigin, const VECTOR2D& aLine,
239 double aTickLen, int aNumDivisions, bool aDrawingDropShadows )
240{
241 KIGFX::GAL* gal = aView->GetGAL();
242 TEXT_DIMS textDims = GetConstantGlyphHeight( gal, -1 );
243 const double backTickSpace = aLine.EuclideanNorm() / aNumDivisions;
244 VECTOR2D backTickVec = aLine;
245 int isign = aView->IsMirroredX() ? -1 : 1;
246
247 RotatePoint( backTickVec, -ANGLE_90 );
248 backTickVec = backTickVec.Resize( aTickLen * isign );
249
250 BOX2D viewportD = aView->GetViewport();
251 BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
252
253 viewport.Inflate( aTickLen * 4 ); // Doesn't have to be accurate, just big enough not to
254 // exclude anything that should be partially drawn
255
256 for( int i = 0; i < aNumDivisions + 1; ++i )
257 {
258 const VECTOR2D backTickPos = aOrigin + aLine.Resize( backTickSpace * i );
259
260 if( !viewport.Contains( backTickPos ) )
261 continue;
262
263 gal->SetLineWidth( getTickLineWidth( textDims, aDrawingDropShadows ) );
264 gal->DrawLine( backTickPos, backTickPos + backTickVec );
265 }
266}
267
268
269RULER_ITEM::RULER_ITEM( const TWO_POINT_GEOMETRY_MANAGER& aGeomMgr, const EDA_IU_SCALE& aIuScale, EDA_UNITS userUnits,
270 bool aFlipX, bool aFlipY )
271 : EDA_ITEM( NOT_USED ), // Never added to anything - just a preview
272 m_geomMgr( aGeomMgr ),
273 m_userUnits( userUnits ),
274 m_iuScale( aIuScale ),
275 m_flipX( aFlipX ),
276 m_flipY( aFlipY )
277{
278}
279
280
282{
283 BOX2I tmp;
284
286 return tmp;
287
288 // this is an edit-time artefact; no reason to try and be smart with the bounding box
289 // (besides, we can't tell the text extents without a view to know what the scale is)
290 tmp.SetMaximum();
291 return tmp;
292}
293
294
295void RULER_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const
296{
297 aLayers[0] = LAYER_SELECT_OVERLAY;
298 aLayers[1] = LAYER_GP_OVERLAY;
299 aCount = 2;
300}
301
302
303void RULER_ITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
304{
305 KIGFX::GAL* gal = aView->GetGAL();
306 RENDER_SETTINGS* rs = aView->GetPainter()->GetSettings();
307 bool drawingDropShadows = ( aLayer == getShadowLayer( gal ) );
308
310 gal->SetLayerDepth( gal->GetMinDepth() );
311
312 VECTOR2D origin = m_geomMgr.GetOrigin();
313 VECTOR2D end = m_geomMgr.GetEnd();
314
315 gal->SetIsStroke( true );
316 gal->SetIsFill( false );
317 gal->SetTextMirrored( false );
318
319 if( m_color )
320 gal->SetStrokeColor( *m_color );
321 else
323
324 if( drawingDropShadows )
326
327 gal->ResetTextAttributes();
328 TEXT_DIMS textDims = GetConstantGlyphHeight( gal );
329
330 // draw the main line from the origin to cursor
331 gal->SetLineWidth( getTickLineWidth( textDims, drawingDropShadows ) );
332 gal->DrawLine( origin, end );
333
334 VECTOR2D rulerVec( end - origin );
335
336 wxArrayString cursorStrings = GetDimensionStrings();
337 DrawTextNextToCursor( aView, end, -rulerVec, cursorStrings, drawingDropShadows );
338
339 // basic tick size
340 const double minorTickLen = 5.0 / gal->GetWorldScale();
341 const double majorTickLen = minorTickLen * majorTickLengthFactor;
342
343 if( m_showTicks )
344 {
345 drawTicksAlongLine( aView, origin, rulerVec, minorTickLen, m_iuScale, m_userUnits,
346 drawingDropShadows );
347
348 drawBacksideTicks( aView, origin, rulerVec, majorTickLen, 2, drawingDropShadows );
349 }
350
352 {
353 const EDA_ANGLE arrowAngle{ 30.0 };
354 VECTOR2D arrowHead = rulerVec;
355 RotatePoint( arrowHead, arrowAngle );
356 arrowHead = arrowHead.Resize( majorTickLen );
357
358 gal->DrawLine( end, end - arrowHead );
359
360 arrowHead = rulerVec;
361 RotatePoint( arrowHead, -arrowAngle );
362 arrowHead = arrowHead.Resize( majorTickLen );
363
364 gal->DrawLine( end, end - arrowHead );
365 }
366 else
367 {
368 // draw the back of the origin "crosshair"
369 gal->DrawLine( origin, origin + rulerVec.Resize( -minorTickLen * midTickLengthFactor ) );
370 }
371}
372
373
375{
376 const VECTOR2D rulerVec = m_geomMgr.GetEnd() - m_geomMgr.GetOrigin();
377 VECTOR2D temp = rulerVec;
378
379 if( m_flipX )
380 temp.x = -temp.x;
381
382 if( m_flipY )
383 temp.y = -temp.y;
384
385 wxArrayString cursorStrings;
386
387 cursorStrings.push_back( DimensionLabel( "x", temp.x, m_iuScale, m_userUnits ) );
388 cursorStrings.push_back( DimensionLabel( "y", temp.y, m_iuScale, m_userUnits ) );
389
390 cursorStrings.push_back(
391 DimensionLabel( "r", rulerVec.EuclideanNorm(), m_iuScale, m_userUnits ) );
392
393 EDA_ANGLE angle = -EDA_ANGLE( rulerVec );
394 cursorStrings.push_back( DimensionLabel( wxString::FromUTF8( "θ" ), angle.AsDegrees(),
395 m_iuScale, EDA_UNITS::DEGREES ) );
396 return cursorStrings;
397}
constexpr const Vec & GetPosition() const
Definition: box2.h:211
constexpr void SetMaximum()
Definition: box2.h:80
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr bool Contains(const Vec &aPoint) const
Definition: box2.h:168
constexpr const SizeVec & GetSize() const
Definition: box2.h:206
double Sin() const
Definition: eda_angle.h:170
double AsDegrees() const
Definition: eda_angle.h:113
double Cos() const
Definition: eda_angle.h:189
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
FONT is an abstract base class for both outline and stroke fonts.
Definition: font.h:131
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:146
void Draw(KIGFX::GAL *aGal, const wxString &aText, const VECTOR2I &aPosition, const VECTOR2I &aCursor, const TEXT_ATTRIBUTES &aAttributes, const METRICS &aFontMetrics) const
Draw a string.
Definition: font.cpp:258
static const METRICS & Default()
Definition: font.cpp:52
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.
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
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.
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.
Definition: ruler_item.cpp:269
const TWO_POINT_GEOMETRY_MANAGER & m_geomMgr
Definition: ruler_item.h:99
void ViewGetLayers(int aLayers[], int &aCount) const override
Definition: ruler_item.cpp:295
wxArrayString GetDimensionStrings() const
Get the strings for the dimensions of the ruler.
Definition: ruler_item.cpp:374
std::optional< COLOR4D > m_color
Definition: ruler_item.h:104
void ViewDraw(int aLayer, KIGFX::VIEW *aView) const override final
Draw the parts of the object belonging to layer aLayer.
Definition: ruler_item.cpp:303
const BOX2I ViewBBox() const override
Return the all the layers within the VIEW the object is painted on.
Definition: ruler_item.cpp:281
const EDA_IU_SCALE & m_iuScale
Definition: ruler_item.h:101
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:68
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition: view.cpp:547
GAL * GetGAL() const
Return the #GAL this view is using to draw graphical primitives.
Definition: view.h:203
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition: view.h:251
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:221
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:283
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:385
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
EDA_UNITS
Definition: eda_units.h:46
@ LAYER_GP_OVERLAY
general purpose overlay
Definition: layer_ids.h:219
@ LAYER_AUX_ITEMS
Auxiliary items (guides, rule, etc)
Definition: layer_ids.h:223
@ LAYER_SELECT_OVERLAY
currently selected items overlay
Definition: layer_ids.h:220
KICOMMON_API bool IsImperialUnit(EDA_UNITS aUnit)
Definition: eda_units.cpp:47
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)
Definition: ruler_item.cpp:55
static const double maxTickDensity
Definition: ruler_item.cpp:34
static TICK_FORMAT getTickFormatForScale(double aScale, double &aTickSpace, EDA_UNITS aUnits)
Definition: ruler_item.cpp:78
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.
Definition: ruler_item.cpp:238
void drawTicksAlongLine(KIGFX::VIEW *aView, const VECTOR2D &aOrigin, const VECTOR2D &aLine, double aMinorTickLen, const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, bool aDrawingDropShadows)
Draw labelled ticks on a line.
Definition: ruler_item.cpp:121
static const double majorTickLengthFactor
Definition: ruler_item.cpp:36
static int getShadowLayer(KIGFX::GAL *aGal)
Definition: ruler_item.cpp:46
static const double midTickLengthFactor
Definition: ruler_item.cpp:35
Description of a "tick format" for a scale factor - how many ticks there are between medium/major tic...
Definition: ruler_item.cpp:71
int majorStep
ticks between major ticks
Definition: ruler_item.cpp:73
double divisionBase
multiple from the last scale
Definition: ruler_item.cpp:72
int midStep
ticks between medium ticks (0 if no medium ticks)
Definition: ruler_item.cpp:74
@ 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:229
@ NOT_USED
the 3d code uses this value
Definition: typeinfo.h:79
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691