KiCad PCB EDA Suite
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-2021 Kicad Developers, see change_log.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 
28 #include <painter.h>
29 #include <view/view.h>
30 #include <trigo.h>
31 
32 using namespace KIGFX::PREVIEW;
33 
34 static const double maxTickDensity = 10.0; // min pixels between tick marks
35 static const double midTickLengthFactor = 1.5;
36 static 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  */
46 static int getShadowLayer( KIGFX::GAL* aGal )
47 {
48  if( aGal->IsCairoEngine() )
49  return LAYER_SELECT_OVERLAY;
50  else
51  return LAYER_GP_OVERLAY;
52 }
53 
54 
55 static void drawCursorStrings( KIGFX::VIEW* aView, const VECTOR2D& aCursor,
56  const VECTOR2D& aRulerVec, EDA_UNITS aUnits,
57  bool aDrawingDropShadows )
58 {
59  // draw the cursor labels
60  std::vector<wxString> cursorStrings;
61 
62  cursorStrings.push_back( DimensionLabel( "x", aRulerVec.x, aUnits ) );
63  cursorStrings.push_back( DimensionLabel( "y", aRulerVec.y, aUnits ) );
64 
65  cursorStrings.push_back( DimensionLabel( "r", aRulerVec.EuclideanNorm(), aUnits ) );
66 
67  double degs = RAD2DECIDEG( -aRulerVec.Angle() );
68  cursorStrings.push_back( DimensionLabel( wxString::FromUTF8( "θ" ), degs,
70 
71  auto temp = aRulerVec;
72  DrawTextNextToCursor( aView, aCursor, -temp, cursorStrings, aDrawingDropShadows );
73 }
74 
75 
76 static double getTickLineWidth( const TEXT_DIMS& textDims, bool aDrawingDropShadows )
77 {
78  double width = textDims.StrokeWidth * 0.8;
79 
80  if( aDrawingDropShadows )
81  width += textDims.ShadowWidth;
82 
83  return width;
84 }
85 
86 
92 {
93  double divisionBase;
94  int majorStep;
95  int midStep;
96 };
97 
98 
99 static TICK_FORMAT getTickFormatForScale( double aScale, double& aTickSpace, EDA_UNITS aUnits )
100 {
101  // simple 1/2/5 scales per decade
102  static std::vector<TICK_FORMAT> tickFormats =
103  {
104  { 2, 10, 5 }, // |....:....|
105  { 2, 5, 0 }, // |....|
106  { 2.5, 2, 0 }, // |.|.|
107  };
108 
109  // could start at a set number of MM, but that's not available in common
110  aTickSpace = 1;
111 
112  // Convert to a round (mod-10) number of mils for imperial units
113  if( EDA_UNIT_UTILS::IsImperialUnit( aUnits ) )
114  aTickSpace *= 2.54;
115 
116  int tickFormat = 0;
117 
118  while( true )
119  {
120  const auto pixelSpace = aTickSpace * aScale;
121 
122  if( pixelSpace >= maxTickDensity )
123  break;
124 
125  tickFormat = ( tickFormat + 1 ) % tickFormats.size();
126  aTickSpace *= tickFormats[tickFormat].divisionBase;
127  }
128 
129  return tickFormats[tickFormat];
130 }
131 
132 
142 void drawTicksAlongLine( KIGFX::VIEW* aView, const VECTOR2D& aOrigin, const VECTOR2D& aLine,
143  double aMinorTickLen, EDA_UNITS aUnits, bool aDrawingDropShadows )
144 {
145  KIGFX::GAL* gal = aView->GetGAL();
146  VECTOR2D tickLine = aLine.Rotate( -M_PI_2 );
147  double tickSpace;
148  TICK_FORMAT tickF = getTickFormatForScale( gal->GetWorldScale(), tickSpace, aUnits );
149 
150  // number of ticks in whole ruler
151  int numTicks = (int) std::ceil( aLine.EuclideanNorm() / tickSpace );
152 
153  // work out which way up the tick labels go
154  TEXT_DIMS textDims = SetConstantGlyphHeight( gal, -1 );
155  double textThickness = textDims.StrokeWidth;
156  double labelAngle = -tickLine.Angle();
157  double textOffset = 0;
158 
159  if( aDrawingDropShadows )
160  {
161  textOffset = textDims.ShadowWidth;
162  textThickness += 2 * textDims.ShadowWidth;
163  }
164 
165  double majorTickLen = aMinorTickLen * ( majorTickLengthFactor + 1 );
166  VECTOR2D labelOffset = tickLine.Resize( majorTickLen - textOffset );
167 
168  if( aView->IsMirroredX() )
169  {
170  textOffset = -textOffset;
171  labelOffset = -labelOffset;
172  }
173 
174  if( aLine.Angle() > 0 )
175  {
177  }
178  else
179  {
181  labelAngle += M_PI;
182  }
183 
184  BOX2D viewportD = aView->GetViewport();
185  BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
186 
187  viewport.Inflate( majorTickLen * 2 ); // Doesn't have to be accurate, just big enough not
188  // to exclude anything that should be partially drawn
189 
190  for( int i = 0; i < numTicks; ++i )
191  {
192  const VECTOR2D tickPos = aOrigin + aLine.Resize( tickSpace * i );
193 
194  if( !viewport.Contains( tickPos ) )
195  continue;
196 
197  double length = aMinorTickLen;
198  bool drawLabel = false;
199 
200  if( i % tickF.majorStep == 0 )
201  {
202  drawLabel = true;
203  length *= majorTickLengthFactor;
204  }
205  else if( tickF.midStep && i % tickF.midStep == 0 )
206  {
207  drawLabel = true;
208  length *= midTickLengthFactor;
209  }
210 
211  gal->SetLineWidth( textThickness / 2 );
212  gal->DrawLine( tickPos, tickPos + tickLine.Resize( length ) );
213 
214  if( drawLabel )
215  {
216  wxString label = DimensionLabel( "", tickSpace * i, aUnits, false );
217  gal->SetLineWidth( textThickness );
218  gal->StrokeText( label, tickPos + labelOffset, labelAngle );
219  }
220  }
221 }
222 
223 
234 void drawBacksideTicks( KIGFX::VIEW* aView, const VECTOR2D& aOrigin, const VECTOR2D& aLine,
235  double aTickLen, int aNumDivisions, bool aDrawingDropShadows )
236 {
237  KIGFX::GAL* gal = aView->GetGAL();
238  const double backTickSpace = aLine.EuclideanNorm() / aNumDivisions;
239  const VECTOR2D backTickVec = aLine.Rotate( M_PI_2 ).Resize( aTickLen );
240  TEXT_DIMS textDims = SetConstantGlyphHeight( gal, -1 );
241 
242  BOX2D viewportD = aView->GetViewport();
243  BOX2I viewport( VECTOR2I( viewportD.GetPosition() ), VECTOR2I( viewportD.GetSize() ) );
244 
245  viewport.Inflate( aTickLen * 4 ); // Doesn't have to be accurate, just big enough not to
246  // exclude anything that should be partially drawn
247 
248  for( int i = 0; i < aNumDivisions + 1; ++i )
249  {
250  const VECTOR2D backTickPos = aOrigin + aLine.Resize( backTickSpace * i );
251 
252  if( !viewport.Contains( backTickPos ) )
253  continue;
254 
255  gal->SetLineWidth( getTickLineWidth( textDims, aDrawingDropShadows ) );
256  gal->DrawLine( backTickPos, backTickPos + backTickVec );
257  }
258 }
259 
260 
262  : EDA_ITEM( NOT_USED ), // Never added to anything - just a preview
263  m_geomMgr( aGeomMgr ),
264  m_userUnits( userUnits )
265 {
266 }
267 
268 
270 {
271  BOX2I tmp;
272 
273  if( m_geomMgr.GetOrigin() == m_geomMgr.GetEnd() )
274  return tmp;
275 
276  // this is an edit-time artefact; no reason to try and be smart with the bounding box
277  // (besides, we can't tell the text extents without a view to know what the scale is)
278  tmp.SetMaximum();
279  return tmp;
280 }
281 
282 
283 void RULER_ITEM::ViewGetLayers( int aLayers[], int& aCount ) const
284 {
285  aLayers[0] = LAYER_SELECT_OVERLAY;
286  aLayers[1] = LAYER_GP_OVERLAY;
287  aCount = 2;
288 }
289 
290 
291 void RULER_ITEM::ViewDraw( int aLayer, KIGFX::VIEW* aView ) const
292 {
293  KIGFX::GAL* gal = aView->GetGAL();
294  RENDER_SETTINGS* rs = aView->GetPainter()->GetSettings();
295  bool drawingDropShadows = ( aLayer == getShadowLayer( gal ) );
296 
297  gal->PushDepth();
298  gal->SetLayerDepth( gal->GetMinDepth() );
299 
300  VECTOR2D origin = m_geomMgr.GetOrigin();
301  VECTOR2D end = m_geomMgr.GetEnd();
302 
303  gal->SetIsStroke( true );
304  gal->SetIsFill( false );
305 
306  gal->SetTextMirrored( false );
308 
309  if( drawingDropShadows )
310  gal->SetStrokeColor( GetShadowColor( gal->GetStrokeColor() ) );
311 
312  gal->ResetTextAttributes();
313  TEXT_DIMS textDims = SetConstantGlyphHeight( gal );
314 
315  // draw the main line from the origin to cursor
316  gal->SetLineWidth( getTickLineWidth( textDims, drawingDropShadows ) );
317  gal->DrawLine( origin, end );
318 
319  VECTOR2D rulerVec( end - origin );
320 
321  drawCursorStrings( aView, end, rulerVec, m_userUnits, drawingDropShadows );
322 
323  // basic tick size
324  const double minorTickLen = 5.0 / gal->GetWorldScale();
325  const double majorTickLen = minorTickLen * majorTickLengthFactor;
326 
327  drawTicksAlongLine( aView, origin, rulerVec, minorTickLen, m_userUnits, drawingDropShadows );
328 
329  drawBacksideTicks( aView, origin, rulerVec, majorTickLen, 2, drawingDropShadows );
330 
331  // draw the back of the origin "crosshair"
332  gal->DrawLine( origin, origin + rulerVec.Resize( -minorTickLen * midTickLengthFactor ) );
333  gal->PopDepth();
334 }
double GetMinDepth() const
Return the minimum depth in the currently used range (the top).
const BOX2I ViewBBox() const override
Return the all the layers within the VIEW the object is painted on.
Definition: ruler_item.cpp:269
BOX2D GetViewport() const
Return the current viewport visible area rectangle.
Definition: view.cpp:514
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
TEXT_DIMS SetConstantGlyphHeight(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.
Represent a very simple geometry manager for items that have a start and end point.
the 3d code uses this value
Definition: typeinfo.h:79
#define M_PI_2
Definition: transline.cpp:37
const COLOR4D & GetLayerColor(int aLayer) const
Return the color used to draw a layer.
double RAD2DECIDEG(double rad)
Definition: trigo.h:236
GAL * GetGAL() const
Return the #GAL this view is using to draw graphical primitives.
Definition: view.h:189
int midStep
ticks between medium ticks (0 if no medium ticks)
Definition: ruler_item.cpp:95
double divisionBase
multiple from the last scale
Definition: ruler_item.cpp:93
virtual void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a line.
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
RULER_ITEM(const TWO_POINT_GEOMETRY_MANAGER &m_geomMgr, EDA_UNITS userUnits)
Return the bounding box of the item covering all its layers.
Definition: ruler_item.cpp:261
wxString DimensionLabel(const wxString &prefix, double aVal, EDA_UNITS aUnits, bool aIncludeUnits=true)
Get a formatted string showing a dimension to a sane precision with an optional prefix and unit suffi...
virtual void SetLayerDepth(double aLayerDepth)
Set the depth of the layer (position on the z-axis)
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:207
const TWO_POINT_GEOMETRY_MANAGER & m_geomMgr
Definition: ruler_item.h:79
bool IsImperialUnit(EDA_UNITS aUnit)
Definition: eda_units.cpp:26
virtual void SetLineWidth(float aLineWidth)
Set the line width.
static const double majorTickLengthFactor
Definition: ruler_item.cpp:36
COLOR4D GetShadowColor(COLOR4D aColor)
static double getTickLineWidth(const TEXT_DIMS &textDims, bool aDrawingDropShadows)
Definition: ruler_item.cpp:76
const COLOR4D & GetStrokeColor() const
Get the stroke color.
Auxiliary items (guides, rule, etc)
void ResetTextAttributes()
Reset text attributes to default styling.
virtual void SetIsFill(bool aIsFillEnabled)
Enable/disable fill.
void PopDepth()
Restore previously stored drawing depth for the depth stack.
virtual void StrokeText(const wxString &aText, const VECTOR2D &aPosition, double aRotationAngle)
Draw a vector type text using preloaded Newstroke font.
bool Contains(const Vec &aPoint) const
Function Contains.
Definition: box2.h:151
void SetMaximum()
Definition: box2.h:73
virtual bool IsCairoEngine()
Return true if the GAL engine is a Cairo based type.
static const double midTickLengthFactor
Definition: ruler_item.cpp:35
void SetTextMirrored(const bool aMirrored)
Set a mirrored property of text.
double Angle() const
Compute the angle of the vector.
Definition: vector2d.h:307
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:234
bool IsMirroredX() const
Return true if view is flipped across the X axis.
Definition: view.h:237
const Vec & GetPosition() const
Definition: box2.h:194
EDA_UNITS
Definition: eda_units.h:38
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition: vector2d.h:404
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
virtual void SetStrokeColor(const COLOR4D &aColor)
Set the stroke color.
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:302
VECTOR2< T > Rotate(double aAngle) const
Rotate the vector by a given angle.
Definition: vector2d.h:371
void ViewGetLayers(int aLayers[], int &aCount) const override
Definition: ruler_item.cpp:283
void PushDepth()
Store current drawing depth on the depth stack.
Board layer functions and definitions.
currently selected items overlay
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:150
Description of a "tick format" for a scale factor - how many ticks there are between medium/major tic...
Definition: ruler_item.cpp:91
static TICK_FORMAT getTickFormatForScale(double aScale, double &aTickSpace, EDA_UNITS aUnits)
Definition: ruler_item.cpp:99
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:293
const Vec & GetSize() const
Definition: box2.h:189
void SetHorizontalJustify(const EDA_TEXT_HJUSTIFY_T aHorizontalJustify)
Set the horizontal justify for text drawing.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition: view.h:67
static const double maxTickDensity
Definition: ruler_item.cpp:34
static int getShadowLayer(KIGFX::GAL *aGal)
Definition: ruler_item.cpp:46
int majorStep
ticks between major ticks
Definition: ruler_item.cpp:94
double GetWorldScale() const
Get the world scale.
void ViewDraw(int aLayer, KIGFX::VIEW *aView) const override final
Draw the parts of the object belonging to layer aLayer.
Definition: ruler_item.cpp:291
virtual void SetIsStroke(bool aIsStrokeEnabled)
Enable/disable stroked outlines.
void DrawTextNextToCursor(KIGFX::VIEW *aView, const VECTOR2D &aCursorPos, const VECTOR2D &aTextQuadrant, const std::vector< wxString > &aStrings, bool aDrawingDropShadows)
Draw strings next to the cursor.
void drawTicksAlongLine(KIGFX::VIEW *aView, const VECTOR2D &aOrigin, const VECTOR2D &aLine, double aMinorTickLen, EDA_UNITS aUnits, bool aDrawingDropShadows)
Draw labelled ticks on a line.
Definition: ruler_item.cpp:142
static void drawCursorStrings(KIGFX::VIEW *aView, const VECTOR2D &aCursor, const VECTOR2D &aRulerVec, EDA_UNITS aUnits, bool aDrawingDropShadows)
Definition: ruler_item.cpp:55
Abstract interface for drawing on a 2D-surface.