1 /* 2 * This program source code file is part of KiCad, a free EDA CAD application. 3 * 4 * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com 5 * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors. 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License 9 * as published by the Free Software Foundation; either version 2 10 * of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, you may find one here: 19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 20 * or you may search the http://www.gnu.org website for the version 2 license, 21 * or you may write to the Free Software Foundation, Inc., 22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA 23 */ 24 25 #ifndef PCB_DIMENSION_H 26 #define PCB_DIMENSION_H 27 28 29 #include <board_item.h> 30 #include <pcb_text.h> 31 #include <geometry/shape.h> 32 #include <geometry/circle.h> 33 34 class LINE_READER; 35 class MSG_PANEL_ITEM; 36 37 38 /// How to display the units in a dimension's text 39 enum class DIM_UNITS_FORMAT 40 { 41 NO_SUFFIX, // 1234.0 42 BARE_SUFFIX, // 1234.0 mm 43 PAREN_SUFFIX // 1234.0 (mm) 44 }; 45 46 /// Where to place the text on a dimension 47 enum class DIM_TEXT_POSITION 48 { 49 OUTSIDE, ///< Text appears outside the dimension line (default) 50 INLINE, ///< Text appears in line with the dimension line 51 MANUAL ///< Text placement is manually set by the user 52 }; 53 54 /** 55 * Used for storing the units selection in the file because EDA_UNITS alone doesn't cut it 56 */ 57 enum class DIM_UNITS_MODE 58 { 59 INCHES, 60 MILS, 61 MILLIMETRES, 62 AUTOMATIC 63 }; 64 65 /** 66 * Frame to show around dimension text 67 */ 68 enum class DIM_TEXT_FRAME 69 { 70 NONE, 71 RECTANGLE, 72 CIRCLE, 73 ROUNDRECT 74 }; 75 76 /** 77 * Abstract dimension API 78 * 79 * Some notes about dimension nomenclature: 80 * 81 * - "feature points" are the points being measured by the dimension. For an example, the start 82 * and end points of a line to be measured. These are the first points picked when drawing a 83 * new dimension. Dimensions can have one or more feature points: linear dimensions (the only 84 * type supported in KiCad 5 and earlier) have two feature points; leader dimensions have one; 85 * and ordinate dimensions can have in theory an unlimited number of feature points. 86 * 87 * - "feature lines" are lines that coincide with feature points. Not all dimension types have 88 * feature lines. The actual start and end of feature lines is calculated from dimension style 89 * properties (offset from feature point to start of feature line, height of crossbar, and height 90 * of feature line past crossbar, for example in linear dimensions) 91 * 92 * - "crossbar" refers to the perpendicular line (usually with arrows at each end) between feature 93 * lines on linear dimensions 94 */ 95 class PCB_DIMENSION_BASE : public BOARD_ITEM 96 { 97 public: 98 PCB_DIMENSION_BASE( BOARD_ITEM* aParent, KICAD_T aType = PCB_DIMENSION_T ); 99 IsType(const KICAD_T aScanTypes[])100 bool IsType( const KICAD_T aScanTypes[] ) const override 101 { 102 if( BOARD_ITEM::IsType( aScanTypes ) ) 103 return true; 104 105 for( const KICAD_T* p = aScanTypes; *p != EOT; ++p ) 106 { 107 if( *p == PCB_LOCATE_GRAPHIC_T ) 108 return true; 109 } 110 111 return false; 112 } 113 114 void SetParent( EDA_ITEM* aParent ) override; 115 116 /** 117 * The dimension's origin is the first feature point for the dimension. Every dimension has 118 * one or more feature points, so every dimension has at least an origin. 119 * @return the origin point of this dimension 120 */ GetStart()121 virtual const wxPoint& GetStart() const { return m_start; } SetStart(const wxPoint & aPoint)122 virtual void SetStart( const wxPoint& aPoint ) { m_start = aPoint; } 123 GetEnd()124 virtual const wxPoint& GetEnd() const { return m_end; } SetEnd(const wxPoint & aPoint)125 virtual void SetEnd( const wxPoint& aPoint ) { m_end = aPoint; } 126 GetPosition()127 wxPoint GetPosition() const override { return m_start; } SetPosition(const wxPoint & aPos)128 void SetPosition( const wxPoint& aPos ) override { m_start = aPos; } 129 GetOverrideTextEnabled()130 bool GetOverrideTextEnabled() const { return m_overrideTextEnabled; } SetOverrideTextEnabled(bool aOverride)131 void SetOverrideTextEnabled( bool aOverride ) { m_overrideTextEnabled = aOverride; } 132 GetOverrideText()133 wxString GetOverrideText() const { return m_valueString; } SetOverrideText(const wxString & aValue)134 void SetOverrideText( const wxString& aValue ) { m_valueString = aValue; } 135 GetMeasuredValue()136 int GetMeasuredValue() const { return m_measuredValue; } 137 138 // KiCad normally calculates the measured value but some importers need to set it. SetMeasuredValue(int aValue)139 void SetMeasuredValue( int aValue ) { m_measuredValue = aValue; } 140 141 /** 142 * @return the dimension value, rendered with precision / zero suppression but no units, etc 143 */ 144 wxString GetValueText() const; 145 146 /** 147 * Update the dimension's cached text and geometry. 148 */ Update()149 void Update() 150 { 151 updateGeometry(); 152 updateText(); 153 } 154 GetPrefix()155 wxString GetPrefix() const { return m_prefix; } 156 void SetPrefix( const wxString& aPrefix ); 157 GetSuffix()158 wxString GetSuffix() const { return m_suffix; } 159 void SetSuffix( const wxString& aSuffix ); 160 GetUnits(EDA_UNITS & aUnits)161 void GetUnits( EDA_UNITS& aUnits ) const { aUnits = m_units; } 162 void SetUnits( EDA_UNITS aUnits ); 163 164 DIM_UNITS_MODE GetUnitsMode() const; 165 void SetUnitsMode( DIM_UNITS_MODE aMode ); 166 167 void SetAutoUnits( bool aAuto = true ) { m_autoUnits = aAuto; } 168 GetUnitsFormat()169 DIM_UNITS_FORMAT GetUnitsFormat() const { return m_unitsFormat; } SetUnitsFormat(const DIM_UNITS_FORMAT aFormat)170 void SetUnitsFormat( const DIM_UNITS_FORMAT aFormat ) { m_unitsFormat = aFormat; } 171 GetPrecision()172 int GetPrecision() const { return m_precision; } SetPrecision(int aPrecision)173 void SetPrecision( int aPrecision ) { m_precision = aPrecision; } 174 GetSuppressZeroes()175 bool GetSuppressZeroes() const { return m_suppressZeroes; } SetSuppressZeroes(bool aSuppress)176 void SetSuppressZeroes( bool aSuppress ) { m_suppressZeroes = aSuppress; } 177 GetKeepTextAligned()178 bool GetKeepTextAligned() const { return m_keepTextAligned; } SetKeepTextAligned(bool aKeepAligned)179 void SetKeepTextAligned( bool aKeepAligned ) { m_keepTextAligned = aKeepAligned; } 180 SetTextPositionMode(DIM_TEXT_POSITION aMode)181 void SetTextPositionMode( DIM_TEXT_POSITION aMode ) { m_textPosition = aMode; } GetTextPositionMode()182 DIM_TEXT_POSITION GetTextPositionMode() const { return m_textPosition; } 183 GetArrowLength()184 int GetArrowLength() const { return m_arrowLength; } SetArrowLength(int aLength)185 void SetArrowLength( int aLength ) { m_arrowLength = aLength; } 186 SetExtensionOffset(int aOffset)187 void SetExtensionOffset( int aOffset ) { m_extensionOffset = aOffset; } GetExtensionOffset()188 int GetExtensionOffset() const { return m_extensionOffset; } 189 GetLineThickness()190 int GetLineThickness() const { return m_lineThickness; } SetLineThickness(int aWidth)191 void SetLineThickness( int aWidth ) { m_lineThickness = aWidth; } 192 193 void SetLayer( PCB_LAYER_ID aLayer ) override; 194 SetTextSize(const wxSize & aTextSize)195 void SetTextSize( const wxSize& aTextSize ) 196 { 197 m_text.SetTextSize( aTextSize ); 198 } 199 200 /** 201 * Set the override text - has no effect if m_overrideValue == false. 202 * 203 * @param aNewText is the text to use as the value. 204 */ 205 void SetText( const wxString& aNewText ); 206 207 /** 208 * Retrieve the value text or override text, not including prefix or suffix. 209 * 210 * @return the value portion of the dimension text (either overridden or not). 211 */ 212 const wxString GetText() const; 213 Text()214 PCB_TEXT& Text() { return m_text; } Text()215 const PCB_TEXT& Text() const { return m_text; } 216 217 /** 218 * @return a list of line segments that make up this dimension (for drawing, plotting, etc). 219 */ GetShapes()220 const std::vector<std::shared_ptr<SHAPE>>& GetShapes() const { return m_shapes; } 221 222 // BOARD_ITEM overrides 223 224 void Move( const wxPoint& offset ) override; 225 void Rotate( const wxPoint& aRotCentre, double aAngle ) override; 226 void Flip( const wxPoint& aCentre, bool aFlipLeftRight ) override; 227 228 /** 229 * Mirror the dimension relative to a given horizontal axis. 230 * 231 * The text is not mirrored. Only its position (and angle) is mirrored. The layer is not 232 * changed. 233 * 234 * @param axis_pos is the vertical axis position to mirror around. 235 */ 236 void Mirror( const wxPoint& axis_pos, bool aMirrorLeftRight = false ); 237 238 void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override; 239 240 bool HitTest( const wxPoint& aPosition, int aAccuracy ) const override; 241 bool HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const override; 242 243 const EDA_RECT GetBoundingBox() const override; 244 245 std::shared_ptr<SHAPE> GetEffectiveShape( PCB_LAYER_ID aLayer ) const override; 246 247 wxString GetSelectMenuText( EDA_UNITS aUnits ) const override; 248 249 const BOX2I ViewBBox() const override; 250 251 void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, PCB_LAYER_ID aLayer, 252 int aClearance, int aError, ERROR_LOC aErrorLoc, 253 bool aIgnoreLineWidth ) const override; 254 255 #if defined(DEBUG) Show(int nestLevel,std::ostream & os)256 virtual void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); } 257 #endif 258 259 protected: 260 261 /** 262 * Update the cached geometry of the dimension after changing any of its properties. 263 */ 264 virtual void updateGeometry() = 0; 265 266 /** 267 * Update the text field value from the current geometry (called by updateGeometry normally). 268 */ 269 virtual void updateText(); 270 271 template<typename ShapeType> 272 void addShape( const ShapeType& aShape ); 273 274 /** 275 * Find the intersection between a given segment and polygon outline. 276 * 277 * @param aPoly is the polygon to collide. 278 * @param aSeg is the segment to collide. 279 * @param aStart if true will start from aSeg.A, otherwise aSeg.B. 280 * @return a point on aSeg that collides with aPoly closest to the start, if one exists. 281 */ 282 static OPT_VECTOR2I segPolyIntersection( const SHAPE_POLY_SET& aPoly, const SEG& aSeg, 283 bool aStart = true ); 284 static OPT_VECTOR2I segCircleIntersection( CIRCLE& aCircle, SEG& aSeg, bool aStart = true ); 285 286 // Value format 287 bool m_overrideTextEnabled; ///< Manually specify the displayed measurement value 288 wxString m_valueString; ///< Displayed value when m_overrideValue = true 289 wxString m_prefix; ///< String prepended to the value 290 wxString m_suffix; ///< String appended to the value 291 EDA_UNITS m_units; ///< 0 = inches, 1 = mm 292 bool m_autoUnits; ///< If true, follow the currently selected UI units 293 DIM_UNITS_FORMAT m_unitsFormat; ///< How to render the units suffix 294 int m_precision; ///< Number of digits to display after decimal 295 bool m_suppressZeroes; ///< Suppress trailing zeroes 296 297 // Geometry 298 int m_lineThickness; ///< Thickness used for all graphics in the dimension 299 int m_arrowLength; ///< Length of arrow shapes 300 int m_extensionOffset; ///< Distance from feature points to extension line start 301 DIM_TEXT_POSITION m_textPosition; ///< How to position the text 302 bool m_keepTextAligned; ///< Calculate text orientation to match dimension 303 304 // Internal 305 PCB_TEXT m_text; ///< The actual text object 306 int m_measuredValue; ///< value of PCB dimensions 307 wxPoint m_start; 308 wxPoint m_end; 309 310 ///< Internal cache of drawn shapes 311 std::vector<std::shared_ptr<SHAPE>> m_shapes; 312 313 static constexpr float s_arrowAngle = 27.5; 314 }; 315 316 317 /** 318 * For better understanding of the points that make a dimension: 319 * 320 * Note: historically KiCad called extension lines "feature lines", and also note that what we 321 * call the "crossbar line" here is more commonly called the "dimension line" 322 * 323 * Start (feature point 1) End (feature point 2) 324 * | | 325 * | <-- extension lines --> | 326 * | | 327 * | m_arrowG2F m_arrowD2F | 328 * | / \ | 329 * Crossbar start |/_______crossbar line________\| Crossbar end 330 * |\ m_text /| 331 * | \ / | 332 * | m_arrowG1F m_arrowD1F | 333 * | | 334 * m_featureLineGF m_featureLineDF 335 */ 336 337 /** 338 * An aligned dimension measures the distance between two feature points. It has a crossbar 339 * (dimension line) that stays parallel with the vector between the feature points. 340 * 341 * The height (distance from features to crossbar) can be set directly, or set by manipulating the 342 * crossbar start or end point (with the point editor). 343 */ 344 class PCB_DIM_ALIGNED : public PCB_DIMENSION_BASE 345 { 346 public: 347 PCB_DIM_ALIGNED( BOARD_ITEM* aParent, KICAD_T aType = PCB_DIM_ALIGNED_T ); 348 349 // Do not create a copy constructor & operator=. 350 // The ones generated by the compiler are adequate. 351 352 ~PCB_DIM_ALIGNED() = default; 353 ClassOf(const EDA_ITEM * aItem)354 static inline bool ClassOf( const EDA_ITEM* aItem ) 355 { 356 return aItem && PCB_DIM_ALIGNED_T == aItem->Type(); 357 } 358 359 EDA_ITEM* Clone() const override; 360 361 virtual void SwapData( BOARD_ITEM* aImage ) override; 362 363 BITMAPS GetMenuImage() const override; 364 GetCrossbarStart()365 const wxPoint& GetCrossbarStart() const { return m_crossBarStart; } 366 GetCrossbarEnd()367 const wxPoint& GetCrossbarEnd() const { return m_crossBarEnd; } 368 369 /** 370 * Set the distance from the feature points to the crossbar line. 371 * 372 * @param aHeight is the new height. 373 */ SetHeight(int aHeight)374 void SetHeight( int aHeight ) { m_height = aHeight; } GetHeight()375 int GetHeight() const { return m_height; } 376 377 /** 378 * Update the stored height basing on points coordinates. 379 * 380 * @param aCrossbarStart is the start point of the crossbar. 381 */ 382 void UpdateHeight( const wxPoint& aCrossbarStart, const wxPoint& aCrossbarEnd ); 383 SetExtensionHeight(int aHeight)384 void SetExtensionHeight( int aHeight ) { m_extensionHeight = aHeight; } GetExtensionHeight()385 int GetExtensionHeight() const { return m_extensionHeight; } 386 387 /** 388 * Return the angle of the crossbar. 389 * 390 * @return Angle of the crossbar line expressed in radians. 391 */ GetAngle()392 double GetAngle() const 393 { 394 wxPoint delta( m_end - m_start ); 395 396 return atan2( (double)delta.y, (double)delta.x ); 397 } 398 GetClass()399 wxString GetClass() const override 400 { 401 return wxT( "PCB_DIM_ALIGNED" ); 402 } 403 404 void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override; 405 406 protected: 407 void updateGeometry() override; 408 409 void updateText() override; 410 411 // Geometry 412 int m_height; ///< Perpendicular distance from features to crossbar 413 int m_extensionHeight; ///< Length of extension lines past the crossbar 414 415 wxPoint m_crossBarStart; ///< Crossbar start control point 416 wxPoint m_crossBarEnd; ///< Crossbar end control point 417 418 }; 419 420 421 /** 422 * An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the 423 * X or Y axes, and the measurement is only taken in the X or Y direction. 424 */ 425 class PCB_DIM_ORTHOGONAL : public PCB_DIM_ALIGNED 426 { 427 public: 428 enum class DIR 429 { 430 HORIZONTAL, // Aligned with x-axis 431 VERTICAL // Aligned with y-axis 432 }; 433 434 PCB_DIM_ORTHOGONAL( BOARD_ITEM* aParent ); 435 436 ~PCB_DIM_ORTHOGONAL() = default; 437 ClassOf(const EDA_ITEM * aItem)438 static inline bool ClassOf( const EDA_ITEM* aItem ) 439 { 440 return aItem && PCB_DIM_ORTHOGONAL_T == aItem->Type(); 441 } 442 443 EDA_ITEM* Clone() const override; 444 445 void SwapData( BOARD_ITEM* aImage ) override; 446 447 BITMAPS GetMenuImage() const override; 448 449 /** 450 * Set the orientation of the dimension line (so, perpendicular to the feature lines). 451 * 452 * @param aOrientation is the orientation the dimension should take. 453 */ SetOrientation(DIR aOrientation)454 void SetOrientation( DIR aOrientation ) { m_orientation = aOrientation; } GetOrientation()455 DIR GetOrientation() const { return m_orientation; } 456 GetClass()457 wxString GetClass() const override 458 { 459 return wxT( "PCB_DIM_ORTHOGONAL" ); 460 } 461 void Rotate( const wxPoint& aRotCentre, double aAngle ) override; 462 463 protected: 464 void updateGeometry() override; 465 466 void updateText() override; 467 468 private: 469 // Geometry 470 DIR m_orientation; ///< What axis to lock the dimension line to. 471 472 }; 473 474 475 /** 476 * A leader is a dimension-like object pointing to a specific point. 477 * 478 * A guide to the geometry of a leader: 479 * 480 * a 481 * _ 482 * |\ 483 * \ 484 * b---c TEXT 485 * 486 * Point (a) is m_start, point (b) is m_end, point (c) is the end of the "text line" 487 * The b-c line is drawn from b to the text center, and clipped on the text bounding box. 488 */ 489 class PCB_DIM_LEADER : public PCB_DIMENSION_BASE 490 { 491 public: 492 PCB_DIM_LEADER( BOARD_ITEM* aParent ); 493 ClassOf(const EDA_ITEM * aItem)494 static inline bool ClassOf( const EDA_ITEM* aItem ) 495 { 496 return aItem && PCB_DIM_LEADER_T == aItem->Type(); 497 } 498 499 EDA_ITEM* Clone() const override; 500 501 virtual void SwapData( BOARD_ITEM* aImage ) override; 502 503 BITMAPS GetMenuImage() const override; 504 GetClass()505 wxString GetClass() const override 506 { 507 return wxT( "PCB_DIM_LEADER" ); 508 } 509 SetTextFrame(DIM_TEXT_FRAME aFrame)510 void SetTextFrame( DIM_TEXT_FRAME aFrame ) { m_textFrame = aFrame; } GetTextFrame()511 DIM_TEXT_FRAME GetTextFrame() const { return m_textFrame; } 512 513 void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override; 514 515 protected: 516 void updateGeometry() override; 517 518 private: 519 DIM_TEXT_FRAME m_textFrame; 520 }; 521 522 523 /** 524 * Mark the center of a circle or arc with a cross shape. 525 * 526 * The size and orientation of the cross is adjustable. 527 * m_start always marks the center being measured; m_end marks the end of one leg of the cross. 528 */ 529 class PCB_DIM_CENTER : public PCB_DIMENSION_BASE 530 { 531 public: 532 PCB_DIM_CENTER( BOARD_ITEM* aParent ); 533 ClassOf(const EDA_ITEM * aItem)534 static inline bool ClassOf( const EDA_ITEM* aItem ) 535 { 536 return aItem && PCB_DIM_CENTER_T == aItem->Type(); 537 } 538 539 EDA_ITEM* Clone() const override; 540 541 virtual void SwapData( BOARD_ITEM* aImage ) override; 542 543 BITMAPS GetMenuImage() const override; 544 GetClass()545 wxString GetClass() const override 546 { 547 return wxT( "PCB_DIM_CENTER" ); 548 } 549 550 const EDA_RECT GetBoundingBox() const override; 551 552 const BOX2I ViewBBox() const override; 553 554 protected: 555 void updateGeometry() override; 556 }; 557 558 #endif // DIMENSION_H 559