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 
27 #include <layer_ids.h>
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).
currently selected items overlay
Definition: layer_ids.h:214
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:513
Auxiliary items (guides, rule, etc)
Definition: layer_ids.h:217
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:40
const COLOR4D & GetLayerColor(int aLayer) const
Return the color used to draw a layer.
double RAD2DECIDEG(double rad)
Definition: trigo.h:234
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
static double getTickLineWidth(const TEXT_DIMS &textDims, bool aDrawingDropShadows)
Definition: ruler_item.cpp:76
const COLOR4D & GetStrokeColor() const
Get the stroke color.
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
Definition: box2.h:134
void SetMaximum()
Definition: box2.h:57
virtual bool IsCairoEngine()
Return true if the GAL engine is a Cairo based type.
static const double midTickLengthFactor
Definition: ruler_item.cpp:35
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:177
EDA_UNITS
Definition: eda_units.h:38
void SetTextMirrored(bool aMirrored)
Set a mirrored property of text.
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)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:281
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.
general purpose overlay
Definition: layer_ids.h:213
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:100
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:172
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.
COLOR4D GetShadowColor(const COLOR4D &aColor)