You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1309 lines
39 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
  5. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 2012 Wayne Stambaugh <stambaughw@gmail.com>
  7. * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <bitmaps.h>
  27. #include <pcb_edit_frame.h>
  28. #include <base_units.h>
  29. #include <convert_basic_shapes_to_polygon.h>
  30. #include <pcb_dimension.h>
  31. #include <pcb_text.h>
  32. #include <geometry/shape_compound.h>
  33. #include <geometry/shape_circle.h>
  34. #include <geometry/shape_segment.h>
  35. #include <settings/color_settings.h>
  36. #include <settings/settings_manager.h>
  37. #include <trigo.h>
  38. static const EDA_ANGLE s_arrowAngle( 27.5, DEGREES_T );
  39. PCB_DIMENSION_BASE::PCB_DIMENSION_BASE( BOARD_ITEM* aParent, KICAD_T aType ) :
  40. BOARD_ITEM( aParent, aType ),
  41. m_overrideTextEnabled( false ),
  42. m_units( EDA_UNITS::INCHES ),
  43. m_autoUnits( false ),
  44. m_unitsFormat( DIM_UNITS_FORMAT::BARE_SUFFIX ),
  45. m_precision( 4 ),
  46. m_suppressZeroes( false ),
  47. m_lineThickness( pcbIUScale.mmToIU( 0.2 ) ),
  48. m_arrowLength( pcbIUScale.MilsToIU( 50 ) ),
  49. m_extensionOffset( 0 ),
  50. m_textPosition( DIM_TEXT_POSITION::OUTSIDE ),
  51. m_keepTextAligned( true ),
  52. m_text( aParent ),
  53. m_measuredValue( 0 )
  54. {
  55. m_layer = Dwgs_User;
  56. }
  57. void PCB_DIMENSION_BASE::SetParent( EDA_ITEM* aParent )
  58. {
  59. BOARD_ITEM::SetParent( aParent );
  60. m_text.SetParent( aParent );
  61. }
  62. void PCB_DIMENSION_BASE::updateText()
  63. {
  64. wxString text = m_overrideTextEnabled ? m_valueString : GetValueText();
  65. switch( m_unitsFormat )
  66. {
  67. case DIM_UNITS_FORMAT::NO_SUFFIX: // no units
  68. break;
  69. case DIM_UNITS_FORMAT::BARE_SUFFIX: // normal
  70. text += EDA_UNIT_UTILS::GetText( m_units );
  71. break;
  72. case DIM_UNITS_FORMAT::PAREN_SUFFIX: // parenthetical
  73. text += wxT( " (" ) + EDA_UNIT_UTILS::GetText( m_units ).Trim( false ) + wxT( ")" );
  74. break;
  75. }
  76. text.Prepend( m_prefix );
  77. text.Append( m_suffix );
  78. m_text.SetText( text );
  79. }
  80. template<typename ShapeType>
  81. void PCB_DIMENSION_BASE::addShape( const ShapeType& aShape )
  82. {
  83. m_shapes.push_back( std::make_shared<ShapeType>( aShape ) );
  84. }
  85. wxString PCB_DIMENSION_BASE::GetValueText() const
  86. {
  87. struct lconv* lc = localeconv();
  88. wxChar sep = lc->decimal_point[0];
  89. int val = GetMeasuredValue();
  90. int precision = m_precision;
  91. wxString text;
  92. if( precision >= 6 )
  93. {
  94. switch( m_units )
  95. {
  96. case EDA_UNITS::INCHES: precision = precision - 4; break;
  97. case EDA_UNITS::MILS: precision = std::max( 0, precision - 7 ); break;
  98. case EDA_UNITS::MILLIMETRES: precision = precision - 5; break;
  99. default: precision = precision - 4; break;
  100. }
  101. }
  102. wxString format = wxT( "%." ) + wxString::Format( wxT( "%i" ), precision ) + wxT( "f" );
  103. text.Printf( format, EDA_UNIT_UTILS::UI::ToUserUnit( pcbIUScale, m_units, val ) );
  104. if( m_suppressZeroes )
  105. {
  106. while( text.Last() == '0' )
  107. {
  108. text.RemoveLast();
  109. if( text.Last() == '.' || text.Last() == sep )
  110. {
  111. text.RemoveLast();
  112. break;
  113. }
  114. }
  115. }
  116. return text;
  117. }
  118. void PCB_DIMENSION_BASE::SetPrefix( const wxString& aPrefix )
  119. {
  120. m_prefix = aPrefix;
  121. }
  122. void PCB_DIMENSION_BASE::SetSuffix( const wxString& aSuffix )
  123. {
  124. m_suffix = aSuffix;
  125. }
  126. void PCB_DIMENSION_BASE::SetUnits( EDA_UNITS aUnits )
  127. {
  128. m_units = aUnits;
  129. }
  130. DIM_UNITS_MODE PCB_DIMENSION_BASE::GetUnitsMode() const
  131. {
  132. if( m_autoUnits )
  133. {
  134. return DIM_UNITS_MODE::AUTOMATIC;
  135. }
  136. else
  137. {
  138. switch( m_units )
  139. {
  140. default:
  141. case EDA_UNITS::INCHES: return DIM_UNITS_MODE::INCHES;
  142. case EDA_UNITS::MILLIMETRES: return DIM_UNITS_MODE::MILLIMETRES;
  143. case EDA_UNITS::MILS: return DIM_UNITS_MODE::MILS;
  144. }
  145. }
  146. }
  147. void PCB_DIMENSION_BASE::SetUnitsMode( DIM_UNITS_MODE aMode )
  148. {
  149. m_autoUnits = false;
  150. switch( aMode )
  151. {
  152. case DIM_UNITS_MODE::INCHES: m_units = EDA_UNITS::INCHES; break;
  153. case DIM_UNITS_MODE::MILS: m_units = EDA_UNITS::MILS; break;
  154. case DIM_UNITS_MODE::MILLIMETRES: m_units = EDA_UNITS::MILLIMETRES; break;
  155. case DIM_UNITS_MODE::AUTOMATIC: m_autoUnits = true; break;
  156. }
  157. }
  158. void PCB_DIMENSION_BASE::SetText( const wxString& aNewText )
  159. {
  160. m_valueString = aNewText;
  161. updateText();
  162. }
  163. const wxString PCB_DIMENSION_BASE::GetText() const
  164. {
  165. return m_text.GetText();
  166. }
  167. void PCB_DIMENSION_BASE::SetLayer( PCB_LAYER_ID aLayer )
  168. {
  169. m_layer = aLayer;
  170. m_text.SetLayer( aLayer );
  171. }
  172. void PCB_DIMENSION_BASE::Move( const VECTOR2I& offset )
  173. {
  174. m_text.Offset( offset );
  175. m_start += offset;
  176. m_end += offset;
  177. Update();
  178. }
  179. void PCB_DIMENSION_BASE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  180. {
  181. EDA_ANGLE newAngle = m_text.GetTextAngle() + aAngle;
  182. newAngle.Normalize();
  183. m_text.SetTextAngle( newAngle );
  184. VECTOR2I pt = m_text.GetTextPos();
  185. RotatePoint( pt, aRotCentre, aAngle );
  186. m_text.SetTextPos( pt );
  187. RotatePoint( m_start, aRotCentre, aAngle );
  188. RotatePoint( m_end, aRotCentre, aAngle );
  189. Update();
  190. }
  191. void PCB_DIMENSION_BASE::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
  192. {
  193. Mirror( aCentre );
  194. SetLayer( FlipLayer( GetLayer(), GetBoard()->GetCopperLayerCount() ) );
  195. }
  196. void PCB_DIMENSION_BASE::Mirror( const VECTOR2I& axis_pos, bool aMirrorLeftRight )
  197. {
  198. int axis = aMirrorLeftRight ? axis_pos.x : axis_pos.y;
  199. VECTOR2I newPos = m_text.GetTextPos();
  200. #define INVERT( pos ) ( ( pos ) = axis - ( ( pos ) - axis ) )
  201. if( aMirrorLeftRight )
  202. INVERT( newPos.x );
  203. else
  204. INVERT( newPos.y );
  205. m_text.SetTextPos( newPos );
  206. // invert angle
  207. m_text.SetTextAngle( -m_text.GetTextAngle() );
  208. if( aMirrorLeftRight )
  209. {
  210. INVERT( m_start.x );
  211. INVERT( m_end.x );
  212. }
  213. else
  214. {
  215. INVERT( m_start.y );
  216. INVERT( m_end.y );
  217. }
  218. m_text.SetMirrored( !m_text.IsMirrored() );
  219. Update();
  220. }
  221. void PCB_DIMENSION_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame,
  222. std::vector<MSG_PANEL_ITEM>& aList )
  223. {
  224. // for now, display only the text within the DIMENSION using class PCB_TEXT.
  225. wxString msg;
  226. wxCHECK_RET( m_parent != nullptr, wxT( "PCB_TEXT::GetMsgPanelInfo() m_Parent is NULL." ) );
  227. aList.emplace_back( _( "Dimension" ), m_text.GetShownText() );
  228. aList.emplace_back( _( "Prefix" ), GetPrefix() );
  229. if( GetOverrideTextEnabled() )
  230. {
  231. aList.emplace_back( _( "Override Text" ), GetOverrideText() );
  232. }
  233. else
  234. {
  235. aList.emplace_back( _( "Value" ), GetValueText() );
  236. switch( GetPrecision() )
  237. {
  238. case 6: msg = wxT( "0.00 in / 0 mils / 0.0 mm" ); break;
  239. case 7: msg = wxT( "0.000 in / 0 mils / 0.00 mm" ); break;
  240. case 8: msg = wxT( "0.0000 in / 0.0 mils / 0.000 mm" ); break;
  241. case 9: msg = wxT( "0.00000 in / 0.00 mils / 0.0000 mm" ); break;
  242. default: msg = wxT( "%" ) + wxString::Format( wxT( "1.%df" ), GetPrecision() );
  243. }
  244. aList.emplace_back( _( "Precision" ), wxString::Format( msg, 0.0 ) );
  245. }
  246. aList.emplace_back( _( "Suffix" ), GetSuffix() );
  247. // Use our own UNITS_PROVIDER to report dimension info in dimension's units rather than
  248. // in frame's units.
  249. UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::MILLIMETRES );
  250. unitsProvider.SetUserUnits( GetUnits() );
  251. aList.emplace_back( _( "Units" ), EDA_UNIT_UTILS::GetLabel( GetUnits() ) );
  252. aList.emplace_back( _( "Font" ), m_text.GetDrawFont()->GetName() );
  253. aList.emplace_back( _( "Text Thickness" ),
  254. unitsProvider.MessageTextFromValue( m_text.GetTextThickness() ) );
  255. aList.emplace_back( _( "Text Width" ),
  256. unitsProvider.MessageTextFromValue( m_text.GetTextWidth() ) );
  257. aList.emplace_back( _( "Text Height" ),
  258. unitsProvider.MessageTextFromValue( m_text.GetTextHeight() ) );
  259. ORIGIN_TRANSFORMS originTransforms = aFrame->GetOriginTransforms();
  260. if( Type() == PCB_DIM_CENTER_T || Type() == PCB_FP_DIM_CENTER_T )
  261. {
  262. VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
  263. wxString start = wxString::Format( wxT( "@(%s, %s)" ),
  264. aFrame->MessageTextFromValue( startCoord.x ),
  265. aFrame->MessageTextFromValue( startCoord.y ) );
  266. aList.emplace_back( start, wxEmptyString );
  267. }
  268. else
  269. {
  270. VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
  271. wxString start = wxString::Format( wxT( "@(%s, %s)" ),
  272. aFrame->MessageTextFromValue( startCoord.x ),
  273. aFrame->MessageTextFromValue( startCoord.y ) );
  274. VECTOR2I endCoord = originTransforms.ToDisplayAbs( GetEnd() );
  275. wxString end = wxString::Format( wxT( "@(%s, %s)" ),
  276. aFrame->MessageTextFromValue( endCoord.x ),
  277. aFrame->MessageTextFromValue( endCoord.y ) );
  278. aList.emplace_back( start, end );
  279. }
  280. if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
  281. aList.emplace_back( _( "Status" ), _( "Locked" ) );
  282. aList.emplace_back( _( "Layer" ), GetLayerName() );
  283. }
  284. std::shared_ptr<SHAPE> PCB_DIMENSION_BASE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  285. {
  286. std::shared_ptr<SHAPE_COMPOUND> effectiveShape = std::make_shared<SHAPE_COMPOUND>();
  287. effectiveShape->AddShape( Text().GetEffectiveTextShape()->Clone() );
  288. for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
  289. effectiveShape->AddShape( shape->Clone() );
  290. return effectiveShape;
  291. }
  292. bool PCB_DIMENSION_BASE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  293. {
  294. if( m_text.TextHitTest( aPosition ) )
  295. return true;
  296. int dist_max = aAccuracy + ( m_lineThickness / 2 );
  297. // Locate SEGMENTS
  298. for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
  299. {
  300. if( shape->Collide( aPosition, dist_max ) )
  301. return true;
  302. }
  303. return false;
  304. }
  305. bool PCB_DIMENSION_BASE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  306. {
  307. BOX2I arect = aRect;
  308. arect.Inflate( aAccuracy );
  309. BOX2I rect = GetBoundingBox();
  310. if( aAccuracy )
  311. rect.Inflate( aAccuracy );
  312. if( aContained )
  313. return arect.Contains( rect );
  314. return arect.Intersects( rect );
  315. }
  316. const BOX2I PCB_DIMENSION_BASE::GetBoundingBox() const
  317. {
  318. BOX2I bBox;
  319. int xmin, xmax, ymin, ymax;
  320. bBox = m_text.GetTextBox();
  321. xmin = bBox.GetX();
  322. xmax = bBox.GetRight();
  323. ymin = bBox.GetY();
  324. ymax = bBox.GetBottom();
  325. for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
  326. {
  327. BOX2I shapeBox = shape->BBox();
  328. shapeBox.Inflate( m_lineThickness / 2 );
  329. xmin = std::min( xmin, shapeBox.GetOrigin().x );
  330. xmax = std::max( xmax, shapeBox.GetEnd().x );
  331. ymin = std::min( ymin, shapeBox.GetOrigin().y );
  332. ymax = std::max( ymax, shapeBox.GetEnd().y );
  333. }
  334. bBox.SetX( xmin );
  335. bBox.SetY( ymin );
  336. bBox.SetWidth( xmax - xmin + 1 );
  337. bBox.SetHeight( ymax - ymin + 1 );
  338. bBox.Normalize();
  339. return bBox;
  340. }
  341. wxString PCB_DIMENSION_BASE::GetSelectMenuText( UNITS_PROVIDER* aUnitsProvider ) const
  342. {
  343. return wxString::Format( _( "Dimension '%s' on %s" ), GetText(), GetLayerName() );
  344. }
  345. const BOX2I PCB_DIMENSION_BASE::ViewBBox() const
  346. {
  347. BOX2I dimBBox = BOX2I( VECTOR2I( GetBoundingBox().GetPosition() ),
  348. VECTOR2I( GetBoundingBox().GetSize() ) );
  349. dimBBox.Merge( m_text.ViewBBox() );
  350. return dimBBox;
  351. }
  352. OPT_VECTOR2I PCB_DIMENSION_BASE::segPolyIntersection( const SHAPE_POLY_SET& aPoly, const SEG& aSeg,
  353. bool aStart )
  354. {
  355. VECTOR2I start( aStart ? aSeg.A : aSeg.B );
  356. VECTOR2I endpoint( aStart ? aSeg.B : aSeg.A );
  357. if( aPoly.Contains( start ) )
  358. return std::nullopt;
  359. for( SHAPE_POLY_SET::CONST_SEGMENT_ITERATOR seg = aPoly.CIterateSegments(); seg; ++seg )
  360. {
  361. if( OPT_VECTOR2I intersection = ( *seg ).Intersect( aSeg ) )
  362. {
  363. if( ( *intersection - start ).SquaredEuclideanNorm() <
  364. ( endpoint - start ).SquaredEuclideanNorm() )
  365. endpoint = *intersection;
  366. }
  367. }
  368. if( start == endpoint )
  369. return std::nullopt;
  370. return OPT_VECTOR2I( endpoint );
  371. }
  372. OPT_VECTOR2I PCB_DIMENSION_BASE::segCircleIntersection( CIRCLE& aCircle, SEG& aSeg, bool aStart )
  373. {
  374. VECTOR2I start( aStart ? aSeg.A : aSeg.B );
  375. VECTOR2I endpoint( aStart ? aSeg.B : aSeg.A );
  376. if( aCircle.Contains( start ) )
  377. return std::nullopt;
  378. std::vector<VECTOR2I> intersections = aCircle.Intersect( aSeg );
  379. for( VECTOR2I& intersection : aCircle.Intersect( aSeg ) )
  380. {
  381. if( ( intersection - start ).SquaredEuclideanNorm() <
  382. ( endpoint - start ).SquaredEuclideanNorm() )
  383. endpoint = intersection;
  384. }
  385. if( start == endpoint )
  386. return std::nullopt;
  387. return OPT_VECTOR2I( endpoint );
  388. }
  389. void PCB_DIMENSION_BASE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
  390. PCB_LAYER_ID aLayer, int aClearance,
  391. int aError, ERROR_LOC aErrorLoc,
  392. bool aIgnoreLineWidth ) const
  393. {
  394. wxASSERT_MSG( !aIgnoreLineWidth, wxT( "IgnoreLineWidth has no meaning for dimensions." ) );
  395. for( const std::shared_ptr<SHAPE>& shape : m_shapes )
  396. {
  397. const SHAPE_CIRCLE* circle = dynamic_cast<const SHAPE_CIRCLE*>( shape.get() );
  398. const SHAPE_SEGMENT* seg = dynamic_cast<const SHAPE_SEGMENT*>( shape.get() );
  399. if( circle )
  400. {
  401. TransformCircleToPolygon( aCornerBuffer, circle->GetCenter(),
  402. circle->GetRadius() + m_lineThickness / 2 + aClearance,
  403. aError, aErrorLoc );
  404. }
  405. else if( seg )
  406. {
  407. TransformOvalToPolygon( aCornerBuffer, seg->GetSeg().A,
  408. seg->GetSeg().B, m_lineThickness + 2 * aClearance,
  409. aError, aErrorLoc );
  410. }
  411. else
  412. {
  413. wxFAIL_MSG( wxT( "PCB_DIMENSION_BASE::TransformShapeWithClearanceToPolygon unexpected "
  414. "shape type." ) );
  415. }
  416. }
  417. }
  418. PCB_DIM_ALIGNED::PCB_DIM_ALIGNED( BOARD_ITEM* aParent, KICAD_T aType ) :
  419. PCB_DIMENSION_BASE( aParent, aType ),
  420. m_height( 0 )
  421. {
  422. // To preserve look of old dimensions, initialize extension height based on default arrow length
  423. m_extensionHeight = static_cast<int>( m_arrowLength * s_arrowAngle.Sin() );
  424. }
  425. EDA_ITEM* PCB_DIM_ALIGNED::Clone() const
  426. {
  427. return new PCB_DIM_ALIGNED( *this );
  428. }
  429. void PCB_DIM_ALIGNED::SwapData( BOARD_ITEM* aImage )
  430. {
  431. wxASSERT( aImage->Type() == Type() );
  432. m_shapes.clear();
  433. static_cast<PCB_DIM_ALIGNED*>( aImage )->m_shapes.clear();
  434. std::swap( *static_cast<PCB_DIM_ALIGNED*>( this ), *static_cast<PCB_DIM_ALIGNED*>( aImage ) );
  435. Update();
  436. }
  437. void PCB_DIM_ALIGNED::Mirror( const VECTOR2I& axis_pos, bool aMirrorLeftRight )
  438. {
  439. PCB_DIMENSION_BASE::Mirror( axis_pos, aMirrorLeftRight );
  440. m_height = -m_height;
  441. }
  442. BITMAPS PCB_DIM_ALIGNED::GetMenuImage() const
  443. {
  444. return BITMAPS::add_aligned_dimension;
  445. }
  446. void PCB_DIM_ALIGNED::UpdateHeight( const VECTOR2I& aCrossbarStart, const VECTOR2I& aCrossbarEnd )
  447. {
  448. VECTOR2D height( aCrossbarStart - GetStart() );
  449. VECTOR2D crossBar( aCrossbarEnd - aCrossbarStart );
  450. if( height.Cross( crossBar ) > 0 )
  451. m_height = -height.EuclideanNorm();
  452. else
  453. m_height = height.EuclideanNorm();
  454. Update();
  455. }
  456. void PCB_DIM_ALIGNED::updateGeometry()
  457. {
  458. m_shapes.clear();
  459. VECTOR2I dimension( m_end - m_start );
  460. m_measuredValue = KiROUND( dimension.EuclideanNorm() );
  461. VECTOR2I extension;
  462. if( m_height > 0 )
  463. extension = VECTOR2I( -dimension.y, dimension.x );
  464. else
  465. extension = VECTOR2I( dimension.y, -dimension.x );
  466. // Add extension lines
  467. int extensionHeight = std::abs( m_height ) - m_extensionOffset + m_extensionHeight;
  468. VECTOR2I extStart( m_start );
  469. extStart += extension.Resize( m_extensionOffset );
  470. addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
  471. extStart = VECTOR2I( m_end );
  472. extStart += extension.Resize( m_extensionOffset );
  473. addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
  474. // Add crossbar
  475. VECTOR2I crossBarDistance = sign( m_height ) * extension.Resize( m_height );
  476. m_crossBarStart = m_start + crossBarDistance;
  477. m_crossBarEnd = m_end + crossBarDistance;
  478. // Update text after calculating crossbar position but before adding crossbar lines
  479. updateText();
  480. // Now that we have the text updated, we can determine how to draw the crossbar.
  481. // First we need to create an appropriate bounding polygon to collide with
  482. BOX2I textBox = m_text.GetTextBox().Inflate( m_text.GetTextWidth() / 2,
  483. - m_text.GetEffectiveTextPenWidth() );
  484. SHAPE_POLY_SET polyBox;
  485. polyBox.NewOutline();
  486. polyBox.Append( textBox.GetOrigin() );
  487. polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
  488. polyBox.Append( textBox.GetEnd() );
  489. polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
  490. polyBox.Rotate( m_text.GetTextAngle(), textBox.GetCenter() );
  491. // The ideal crossbar, if the text doesn't collide
  492. SEG crossbar( m_crossBarStart, m_crossBarEnd );
  493. // Now we can draw 0, 1, or 2 crossbar lines depending on how the polygon collides
  494. bool containsA = polyBox.Contains( crossbar.A );
  495. bool containsB = polyBox.Contains( crossbar.B );
  496. OPT_VECTOR2I endpointA = segPolyIntersection( polyBox, crossbar );
  497. OPT_VECTOR2I endpointB = segPolyIntersection( polyBox, crossbar, false );
  498. if( endpointA )
  499. m_shapes.emplace_back( new SHAPE_SEGMENT( crossbar.A, *endpointA ) );
  500. if( endpointB )
  501. m_shapes.emplace_back( new SHAPE_SEGMENT( *endpointB, crossbar.B ) );
  502. if( !containsA && !containsB && !endpointA && !endpointB )
  503. m_shapes.emplace_back( new SHAPE_SEGMENT( crossbar ) );
  504. // Add arrows
  505. VECTOR2I arrowEndPos( m_arrowLength, 0 );
  506. VECTOR2I arrowEndNeg( m_arrowLength, 0 );
  507. RotatePoint( arrowEndPos, -EDA_ANGLE( dimension ) + s_arrowAngle );
  508. RotatePoint( arrowEndNeg, -EDA_ANGLE( dimension ) - s_arrowAngle );
  509. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarStart, m_crossBarStart + arrowEndPos ) );
  510. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarStart, m_crossBarStart + arrowEndNeg ) );
  511. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarEnd, m_crossBarEnd - arrowEndPos ) );
  512. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarEnd, m_crossBarEnd - arrowEndNeg ) );
  513. }
  514. void PCB_DIM_ALIGNED::updateText()
  515. {
  516. VECTOR2I crossbarCenter( ( m_crossBarEnd - m_crossBarStart ) / 2 );
  517. if( m_textPosition == DIM_TEXT_POSITION::OUTSIDE )
  518. {
  519. int textOffsetDistance = m_text.GetEffectiveTextPenWidth() + m_text.GetTextHeight();
  520. EDA_ANGLE rotation;
  521. if( crossbarCenter.x == 0 )
  522. rotation = ANGLE_90 * sign( -crossbarCenter.y );
  523. else if( crossbarCenter.x < 0 )
  524. rotation = -ANGLE_90;
  525. else
  526. rotation = ANGLE_90;
  527. VECTOR2I textOffset = crossbarCenter;
  528. RotatePoint( textOffset, rotation );
  529. textOffset = crossbarCenter + textOffset.Resize( textOffsetDistance );
  530. m_text.SetTextPos( m_crossBarStart + textOffset );
  531. }
  532. else if( m_textPosition == DIM_TEXT_POSITION::INLINE )
  533. {
  534. m_text.SetTextPos( m_crossBarStart + crossbarCenter );
  535. }
  536. if( m_keepTextAligned )
  537. {
  538. EDA_ANGLE textAngle = FULL_CIRCLE - EDA_ANGLE( crossbarCenter );
  539. textAngle.Normalize();
  540. if( textAngle > ANGLE_90 && textAngle <= ANGLE_270 )
  541. textAngle -= ANGLE_180;
  542. m_text.SetTextAngle( textAngle );
  543. }
  544. PCB_DIMENSION_BASE::updateText();
  545. }
  546. void PCB_DIM_ALIGNED::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  547. {
  548. PCB_DIMENSION_BASE::GetMsgPanelInfo( aFrame, aList );
  549. // Use our own UNITS_PROVIDER to report dimension info in dimension's units rather than
  550. // in frame's units.
  551. UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::MILLIMETRES );
  552. unitsProvider.SetUserUnits( GetUnits() );
  553. aList.emplace_back( _( "Height" ), unitsProvider.MessageTextFromValue( m_height ) );
  554. }
  555. PCB_DIM_ORTHOGONAL::PCB_DIM_ORTHOGONAL( BOARD_ITEM* aParent, bool aInFP ) :
  556. PCB_DIM_ALIGNED( aParent, aInFP ? PCB_FP_DIM_ORTHOGONAL_T : PCB_DIM_ORTHOGONAL_T )
  557. {
  558. // To preserve look of old dimensions, initialize extension height based on default arrow length
  559. m_extensionHeight = static_cast<int>( m_arrowLength * s_arrowAngle.Sin() );
  560. m_orientation = DIR::HORIZONTAL;
  561. }
  562. EDA_ITEM* PCB_DIM_ORTHOGONAL::Clone() const
  563. {
  564. return new PCB_DIM_ORTHOGONAL( *this );
  565. }
  566. void PCB_DIM_ORTHOGONAL::SwapData( BOARD_ITEM* aImage )
  567. {
  568. wxASSERT( aImage->Type() == Type() );
  569. m_shapes.clear();
  570. static_cast<PCB_DIM_ORTHOGONAL*>( aImage )->m_shapes.clear();
  571. std::swap( *static_cast<PCB_DIM_ORTHOGONAL*>( this ),
  572. *static_cast<PCB_DIM_ORTHOGONAL*>( aImage ) );
  573. Update();
  574. }
  575. BITMAPS PCB_DIM_ORTHOGONAL::GetMenuImage() const
  576. {
  577. return BITMAPS::add_orthogonal_dimension;
  578. }
  579. void PCB_DIM_ORTHOGONAL::updateGeometry()
  580. {
  581. m_shapes.clear();
  582. int measurement = ( m_orientation == DIR::HORIZONTAL ? m_end.x - m_start.x :
  583. m_end.y - m_start.y );
  584. m_measuredValue = KiROUND( std::abs( measurement ) );
  585. VECTOR2I extension;
  586. if( m_orientation == DIR::HORIZONTAL )
  587. extension = VECTOR2I( 0, m_height );
  588. else
  589. extension = VECTOR2I( m_height, 0 );
  590. // Add first extension line
  591. int extensionHeight = std::abs( m_height ) - m_extensionOffset + m_extensionHeight;
  592. VECTOR2I extStart( m_start );
  593. extStart += extension.Resize( m_extensionOffset );
  594. addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
  595. // Add crossbar
  596. VECTOR2I crossBarDistance = sign( m_height ) * extension.Resize( m_height );
  597. m_crossBarStart = m_start + crossBarDistance;
  598. if( m_orientation == DIR::HORIZONTAL )
  599. m_crossBarEnd = VECTOR2I( m_end.x, m_crossBarStart.y );
  600. else
  601. m_crossBarEnd = VECTOR2I( m_crossBarStart.x, m_end.y );
  602. // Add second extension line (m_end to crossbar end)
  603. if( m_orientation == DIR::HORIZONTAL )
  604. extension = VECTOR2I( 0, m_end.y - m_crossBarEnd.y );
  605. else
  606. extension = VECTOR2I( m_end.x - m_crossBarEnd.x, 0 );
  607. extensionHeight = extension.EuclideanNorm() - m_extensionOffset + m_extensionHeight;
  608. extStart = VECTOR2I( m_crossBarEnd );
  609. extStart -= extension.Resize( m_extensionHeight );
  610. addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
  611. // Update text after calculating crossbar position but before adding crossbar lines
  612. updateText();
  613. // Now that we have the text updated, we can determine how to draw the crossbar.
  614. // First we need to create an appropriate bounding polygon to collide with
  615. BOX2I textBox = m_text.GetTextBox().Inflate( m_text.GetTextWidth() / 2,
  616. m_text.GetEffectiveTextPenWidth() );
  617. SHAPE_POLY_SET polyBox;
  618. polyBox.NewOutline();
  619. polyBox.Append( textBox.GetOrigin() );
  620. polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
  621. polyBox.Append( textBox.GetEnd() );
  622. polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
  623. polyBox.Rotate( m_text.GetTextAngle(), textBox.GetCenter() );
  624. // The ideal crossbar, if the text doesn't collide
  625. SEG crossbar( m_crossBarStart, m_crossBarEnd );
  626. // Now we can draw 0, 1, or 2 crossbar lines depending on how the polygon collides
  627. bool containsA = polyBox.Contains( crossbar.A );
  628. bool containsB = polyBox.Contains( crossbar.B );
  629. OPT_VECTOR2I endpointA = segPolyIntersection( polyBox, crossbar );
  630. OPT_VECTOR2I endpointB = segPolyIntersection( polyBox, crossbar, false );
  631. if( endpointA )
  632. m_shapes.emplace_back( new SHAPE_SEGMENT( crossbar.A, *endpointA ) );
  633. if( endpointB )
  634. m_shapes.emplace_back( new SHAPE_SEGMENT( *endpointB, crossbar.B ) );
  635. if( !containsA && !containsB && !endpointA && !endpointB )
  636. m_shapes.emplace_back( new SHAPE_SEGMENT( crossbar ) );
  637. // Add arrows
  638. EDA_ANGLE crossBarAngle( m_crossBarEnd - m_crossBarStart );
  639. VECTOR2I arrowEndPos( m_arrowLength, 0 );
  640. VECTOR2I arrowEndNeg( m_arrowLength, 0 );
  641. RotatePoint( arrowEndPos, -crossBarAngle + s_arrowAngle );
  642. RotatePoint( arrowEndNeg, -crossBarAngle - s_arrowAngle );
  643. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarStart, m_crossBarStart + arrowEndPos ) );
  644. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarStart, m_crossBarStart + arrowEndNeg ) );
  645. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarEnd, m_crossBarEnd - arrowEndPos ) );
  646. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarEnd, m_crossBarEnd - arrowEndNeg ) );
  647. }
  648. void PCB_DIM_ORTHOGONAL::updateText()
  649. {
  650. VECTOR2I crossbarCenter( ( m_crossBarEnd - m_crossBarStart ) / 2 );
  651. if( m_textPosition == DIM_TEXT_POSITION::OUTSIDE )
  652. {
  653. int textOffsetDistance = m_text.GetEffectiveTextPenWidth() + m_text.GetTextHeight();
  654. VECTOR2I textOffset;
  655. if( m_orientation == DIR::HORIZONTAL )
  656. textOffset.y = -textOffsetDistance;
  657. else
  658. textOffset.x = -textOffsetDistance;
  659. textOffset += crossbarCenter;
  660. m_text.SetTextPos( m_crossBarStart + textOffset );
  661. }
  662. else if( m_textPosition == DIM_TEXT_POSITION::INLINE )
  663. {
  664. m_text.SetTextPos( m_crossBarStart + crossbarCenter );
  665. }
  666. if( m_keepTextAligned )
  667. {
  668. if( abs( crossbarCenter.x ) > abs( crossbarCenter.y ) )
  669. m_text.SetTextAngle( ANGLE_HORIZONTAL );
  670. else
  671. m_text.SetTextAngle( ANGLE_VERTICAL );
  672. }
  673. PCB_DIMENSION_BASE::updateText();
  674. }
  675. void PCB_DIM_ORTHOGONAL::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  676. {
  677. EDA_ANGLE angle( aAngle );
  678. // restrict angle to -179.9 to 180.0 degrees
  679. angle.Normalize180();
  680. // adjust orientation and height to new angle
  681. // we can only handle the cases of -90, 0, 90, 180 degrees exactly;
  682. // in the other cases we will use the nearest 90 degree angle to
  683. // choose at least an approximate axis for the target orientation
  684. // In case of exactly 45 or 135 degrees, we will round towards zero for consistency
  685. if( angle > ANGLE_45 && angle <= ANGLE_135 )
  686. {
  687. // about 90 degree
  688. if( m_orientation == DIR::HORIZONTAL )
  689. {
  690. m_orientation = DIR::VERTICAL;
  691. }
  692. else
  693. {
  694. m_orientation = DIR::HORIZONTAL;
  695. m_height = -m_height;
  696. }
  697. }
  698. else if( angle < -ANGLE_45 && angle >= -ANGLE_135 )
  699. {
  700. // about -90 degree
  701. if( m_orientation == DIR::HORIZONTAL )
  702. {
  703. m_orientation = DIR::VERTICAL;
  704. m_height = -m_height;
  705. }
  706. else
  707. {
  708. m_orientation = DIR::HORIZONTAL;
  709. }
  710. }
  711. else if( angle > ANGLE_135 || angle < -ANGLE_135 )
  712. {
  713. // about 180 degree
  714. m_height = -m_height;
  715. }
  716. // this will update m_crossBarStart and m_crossbarEnd
  717. PCB_DIMENSION_BASE::Rotate( aRotCentre, angle );
  718. }
  719. PCB_DIM_LEADER::PCB_DIM_LEADER( BOARD_ITEM* aParent, bool aInFP ) :
  720. PCB_DIMENSION_BASE( aParent, aInFP ? PCB_FP_DIM_LEADER_T : PCB_DIM_LEADER_T ),
  721. m_textBorder( DIM_TEXT_BORDER::NONE )
  722. {
  723. m_unitsFormat = DIM_UNITS_FORMAT::NO_SUFFIX;
  724. m_overrideTextEnabled = true;
  725. m_keepTextAligned = false;
  726. SetText( _( "Leader" ) );
  727. }
  728. EDA_ITEM* PCB_DIM_LEADER::Clone() const
  729. {
  730. return new PCB_DIM_LEADER( *this );
  731. }
  732. void PCB_DIM_LEADER::SwapData( BOARD_ITEM* aImage )
  733. {
  734. wxASSERT( aImage->Type() == Type() );
  735. m_shapes.clear();
  736. static_cast<PCB_DIM_LEADER*>( aImage )->m_shapes.clear();
  737. std::swap( *static_cast<PCB_DIM_LEADER*>( this ), *static_cast<PCB_DIM_LEADER*>( aImage ) );
  738. Update();
  739. }
  740. BITMAPS PCB_DIM_LEADER::GetMenuImage() const
  741. {
  742. return BITMAPS::add_leader;
  743. }
  744. void PCB_DIM_LEADER::updateGeometry()
  745. {
  746. m_shapes.clear();
  747. updateText();
  748. // Now that we have the text updated, we can determine how to draw the second line
  749. // First we need to create an appropriate bounding polygon to collide with
  750. BOX2I textBox = m_text.GetTextBox().Inflate( m_text.GetTextWidth() / 2,
  751. m_text.GetEffectiveTextPenWidth() );
  752. SHAPE_POLY_SET polyBox;
  753. polyBox.NewOutline();
  754. polyBox.Append( textBox.GetOrigin() );
  755. polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
  756. polyBox.Append( textBox.GetEnd() );
  757. polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
  758. polyBox.Rotate( m_text.GetTextAngle(), textBox.GetCenter() );
  759. VECTOR2I firstLine( m_end - m_start );
  760. VECTOR2I start( m_start );
  761. start += firstLine.Resize( m_extensionOffset );
  762. SEG arrowSeg( m_start, m_end );
  763. SEG textSeg( m_end, m_text.GetPosition() );
  764. OPT_VECTOR2I arrowSegEnd;
  765. OPT_VECTOR2I textSegEnd;
  766. if( m_textBorder == DIM_TEXT_BORDER::CIRCLE )
  767. {
  768. double penWidth = m_text.GetEffectiveTextPenWidth() / 2.0;
  769. double radius = ( textBox.GetWidth() / 2.0 ) - penWidth;
  770. CIRCLE circle( textBox.GetCenter(), radius );
  771. arrowSegEnd = segCircleIntersection( circle, arrowSeg );
  772. textSegEnd = segCircleIntersection( circle, textSeg );
  773. }
  774. else
  775. {
  776. arrowSegEnd = segPolyIntersection( polyBox, arrowSeg );
  777. textSegEnd = segPolyIntersection( polyBox, textSeg );
  778. }
  779. if( !arrowSegEnd )
  780. arrowSegEnd = m_end;
  781. m_shapes.emplace_back( new SHAPE_SEGMENT( start, *arrowSegEnd ) );
  782. // Add arrows
  783. VECTOR2I arrowEndPos( m_arrowLength, 0 );
  784. VECTOR2I arrowEndNeg( m_arrowLength, 0 );
  785. RotatePoint( arrowEndPos, -EDA_ANGLE( firstLine ) + s_arrowAngle );
  786. RotatePoint( arrowEndNeg, -EDA_ANGLE( firstLine ) - s_arrowAngle );
  787. m_shapes.emplace_back( new SHAPE_SEGMENT( start, start + arrowEndPos ) );
  788. m_shapes.emplace_back( new SHAPE_SEGMENT( start, start + arrowEndNeg ) );
  789. if( !GetText().IsEmpty() )
  790. {
  791. switch( m_textBorder )
  792. {
  793. case DIM_TEXT_BORDER::RECTANGLE:
  794. {
  795. for( SHAPE_POLY_SET::SEGMENT_ITERATOR seg = polyBox.IterateSegments(); seg; seg++ )
  796. m_shapes.emplace_back( new SHAPE_SEGMENT( *seg ) );
  797. break;
  798. }
  799. case DIM_TEXT_BORDER::CIRCLE:
  800. {
  801. double penWidth = m_text.GetEffectiveTextPenWidth() / 2.0;
  802. double radius = ( textBox.GetWidth() / 2.0 ) - penWidth;
  803. m_shapes.emplace_back( new SHAPE_CIRCLE( textBox.GetCenter(), radius ) );
  804. break;
  805. }
  806. default:
  807. break;
  808. }
  809. }
  810. if( textSegEnd && *arrowSegEnd == m_end )
  811. m_shapes.emplace_back( new SHAPE_SEGMENT( m_end, *textSegEnd ) );
  812. }
  813. void PCB_DIM_LEADER::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  814. {
  815. aList.emplace_back( _( "Leader" ), m_text.GetShownText() );
  816. ORIGIN_TRANSFORMS originTransforms = aFrame->GetOriginTransforms();
  817. VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
  818. wxString start = wxString::Format( wxT( "@(%s, %s)" ),
  819. aFrame->MessageTextFromValue( startCoord.x ),
  820. aFrame->MessageTextFromValue( startCoord.y ) );
  821. aList.emplace_back( start, wxEmptyString );
  822. aList.emplace_back( _( "Layer" ), GetLayerName() );
  823. }
  824. PCB_DIM_RADIAL::PCB_DIM_RADIAL( BOARD_ITEM* aParent, bool aInFP ) :
  825. PCB_DIMENSION_BASE( aParent, aInFP ? PCB_FP_DIM_RADIAL_T : PCB_DIM_RADIAL_T )
  826. {
  827. m_unitsFormat = DIM_UNITS_FORMAT::NO_SUFFIX;
  828. m_overrideTextEnabled = false;
  829. m_keepTextAligned = true;
  830. m_isDiameter = false;
  831. m_prefix = "R ";
  832. m_leaderLength = m_arrowLength * 3;
  833. }
  834. EDA_ITEM* PCB_DIM_RADIAL::Clone() const
  835. {
  836. return new PCB_DIM_RADIAL( *this );
  837. }
  838. void PCB_DIM_RADIAL::SwapData( BOARD_ITEM* aImage )
  839. {
  840. wxASSERT( aImage->Type() == Type() );
  841. m_shapes.clear();
  842. static_cast<PCB_DIM_RADIAL*>( aImage )->m_shapes.clear();
  843. std::swap( *static_cast<PCB_DIM_RADIAL*>( this ), *static_cast<PCB_DIM_RADIAL*>( aImage ) );
  844. Update();
  845. }
  846. BITMAPS PCB_DIM_RADIAL::GetMenuImage() const
  847. {
  848. return BITMAPS::add_radial_dimension;
  849. }
  850. VECTOR2I PCB_DIM_RADIAL::GetKnee() const
  851. {
  852. VECTOR2I radial( m_end - m_start );
  853. return m_end + radial.Resize( m_leaderLength );
  854. }
  855. void PCB_DIM_RADIAL::updateText()
  856. {
  857. if( m_keepTextAligned )
  858. {
  859. VECTOR2I textLine( Text().GetPosition() - GetKnee() );
  860. EDA_ANGLE textAngle = FULL_CIRCLE - EDA_ANGLE( textLine );
  861. textAngle.Normalize();
  862. if( textAngle > ANGLE_90 && textAngle <= ANGLE_270 )
  863. textAngle -= ANGLE_180;
  864. // Round to nearest degree
  865. textAngle = EDA_ANGLE( KiROUND( textAngle.AsDegrees() ), DEGREES_T );
  866. m_text.SetTextAngle( textAngle );
  867. }
  868. PCB_DIMENSION_BASE::updateText();
  869. }
  870. void PCB_DIM_RADIAL::updateGeometry()
  871. {
  872. m_shapes.clear();
  873. VECTOR2I center( m_start );
  874. VECTOR2I centerArm( 0, m_arrowLength );
  875. m_shapes.emplace_back( new SHAPE_SEGMENT( center - centerArm, center + centerArm ) );
  876. RotatePoint( centerArm, -ANGLE_90 );
  877. m_shapes.emplace_back( new SHAPE_SEGMENT( center - centerArm, center + centerArm ) );
  878. VECTOR2I radius( m_end - m_start );
  879. if( m_isDiameter )
  880. m_measuredValue = KiROUND( radius.EuclideanNorm() * 2 );
  881. else
  882. m_measuredValue = KiROUND( radius.EuclideanNorm() );
  883. updateText();
  884. // Now that we have the text updated, we can determine how to draw the second line
  885. // First we need to create an appropriate bounding polygon to collide with
  886. BOX2I textBox = m_text.GetTextBox().Inflate( m_text.GetTextWidth() / 2,
  887. m_text.GetEffectiveTextPenWidth() );
  888. SHAPE_POLY_SET polyBox;
  889. polyBox.NewOutline();
  890. polyBox.Append( textBox.GetOrigin() );
  891. polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
  892. polyBox.Append( textBox.GetEnd() );
  893. polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
  894. polyBox.Rotate( m_text.GetTextAngle(), textBox.GetCenter() );
  895. VECTOR2I radial( m_end - m_start );
  896. radial = radial.Resize( m_leaderLength );
  897. SEG arrowSeg( m_end, m_end + radial );
  898. SEG textSeg( arrowSeg.B, m_text.GetPosition() );
  899. OPT_VECTOR2I arrowSegEnd = segPolyIntersection( polyBox, arrowSeg );
  900. OPT_VECTOR2I textSegEnd = segPolyIntersection( polyBox, textSeg );
  901. if( arrowSegEnd )
  902. arrowSeg.B = *arrowSegEnd;
  903. if( textSegEnd )
  904. textSeg.B = *textSegEnd;
  905. m_shapes.emplace_back( new SHAPE_SEGMENT( arrowSeg ) );
  906. // Add arrows
  907. VECTOR2I arrowEndPos( m_arrowLength, 0 );
  908. VECTOR2I arrowEndNeg( m_arrowLength, 0 );
  909. RotatePoint( arrowEndPos, -EDA_ANGLE( radial ) + s_arrowAngle );
  910. RotatePoint( arrowEndNeg, -EDA_ANGLE( radial ) - s_arrowAngle );
  911. m_shapes.emplace_back( new SHAPE_SEGMENT( m_end, m_end + arrowEndPos ) );
  912. m_shapes.emplace_back( new SHAPE_SEGMENT( m_end, m_end + arrowEndNeg ) );
  913. m_shapes.emplace_back( new SHAPE_SEGMENT( textSeg ) );
  914. }
  915. PCB_DIM_CENTER::PCB_DIM_CENTER( BOARD_ITEM* aParent, bool aInFP ) :
  916. PCB_DIMENSION_BASE( aParent, aInFP ? PCB_FP_DIM_CENTER_T : PCB_DIM_CENTER_T )
  917. {
  918. m_unitsFormat = DIM_UNITS_FORMAT::NO_SUFFIX;
  919. m_overrideTextEnabled = true;
  920. }
  921. EDA_ITEM* PCB_DIM_CENTER::Clone() const
  922. {
  923. return new PCB_DIM_CENTER( *this );
  924. }
  925. void PCB_DIM_CENTER::SwapData( BOARD_ITEM* aImage )
  926. {
  927. wxASSERT( aImage->Type() == Type() );
  928. std::swap( *static_cast<PCB_DIM_CENTER*>( this ), *static_cast<PCB_DIM_CENTER*>( aImage ) );
  929. }
  930. BITMAPS PCB_DIM_CENTER::GetMenuImage() const
  931. {
  932. return BITMAPS::add_center_dimension;
  933. }
  934. const BOX2I PCB_DIM_CENTER::GetBoundingBox() const
  935. {
  936. int halfWidth = VECTOR2I( m_end - m_start ).x + ( m_lineThickness / 2.0 );
  937. BOX2I bBox;
  938. bBox.SetX( m_start.x - halfWidth );
  939. bBox.SetY( m_start.y - halfWidth );
  940. bBox.SetWidth( halfWidth * 2 );
  941. bBox.SetHeight( halfWidth * 2 );
  942. bBox.Normalize();
  943. return bBox;
  944. }
  945. const BOX2I PCB_DIM_CENTER::ViewBBox() const
  946. {
  947. return BOX2I( VECTOR2I( GetBoundingBox().GetPosition() ),
  948. VECTOR2I( GetBoundingBox().GetSize() ) );
  949. }
  950. void PCB_DIM_CENTER::updateGeometry()
  951. {
  952. m_shapes.clear();
  953. VECTOR2I center( m_start );
  954. VECTOR2I arm( m_end - m_start );
  955. m_shapes.emplace_back( new SHAPE_SEGMENT( center - arm, center + arm ) );
  956. RotatePoint( arm, -ANGLE_90 );
  957. m_shapes.emplace_back( new SHAPE_SEGMENT( center - arm, center + arm ) );
  958. }
  959. static struct DIMENSION_DESC
  960. {
  961. DIMENSION_DESC()
  962. {
  963. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  964. REGISTER_TYPE( PCB_DIMENSION_BASE );
  965. propMgr.InheritsAfter( TYPE_HASH( PCB_DIMENSION_BASE ), TYPE_HASH( BOARD_ITEM ) );
  966. // TODO: add dimension properties:
  967. //propMgr.AddProperty( new PROPERTY<DIMENSION, int>( _HKI( "Height" ),
  968. //&DIMENSION::SetHeight, &DIMENSION::GetHeight, PROPERTY_DISPLAY::SIZE ) );
  969. }
  970. } _DIMENSION_DESC;