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.

1310 lines
38 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( Millimeter2iu( 0.2 ) ),
  48. m_arrowLength( Mils2iu( 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 += wxS( " " );
  71. text += GetAbbreviatedUnitsLabel( m_units );
  72. break;
  73. case DIM_UNITS_FORMAT::PAREN_SUFFIX: // parenthetical
  74. text += wxT( " (" );
  75. text += GetAbbreviatedUnitsLabel( m_units );
  76. text += wxT( ")" );
  77. break;
  78. }
  79. text.Prepend( m_prefix );
  80. text.Append( m_suffix );
  81. m_text.SetText( text );
  82. }
  83. template<typename ShapeType>
  84. void PCB_DIMENSION_BASE::addShape( const ShapeType& aShape )
  85. {
  86. m_shapes.push_back( std::make_shared<ShapeType>( aShape ) );
  87. }
  88. wxString PCB_DIMENSION_BASE::GetValueText() const
  89. {
  90. struct lconv* lc = localeconv();
  91. wxChar sep = lc->decimal_point[0];
  92. int val = GetMeasuredValue();
  93. int precision = m_precision;
  94. wxString text;
  95. if( precision >= 6 )
  96. {
  97. switch( m_units )
  98. {
  99. case EDA_UNITS::INCHES: precision = precision - 4; break;
  100. case EDA_UNITS::MILS: precision = std::max( 0, precision - 7 ); break;
  101. case EDA_UNITS::MILLIMETRES: precision = precision - 5; break;
  102. default: precision = precision - 4; break;
  103. }
  104. }
  105. wxString format = wxT( "%." ) + wxString::Format( wxT( "%i" ), precision ) + wxT( "f" );
  106. text.Printf( format, To_User_Unit( m_units, val ) );
  107. if( m_suppressZeroes )
  108. {
  109. while( text.Last() == '0' )
  110. {
  111. text.RemoveLast();
  112. if( text.Last() == '.' || text.Last() == sep )
  113. {
  114. text.RemoveLast();
  115. break;
  116. }
  117. }
  118. }
  119. return text;
  120. }
  121. void PCB_DIMENSION_BASE::SetPrefix( const wxString& aPrefix )
  122. {
  123. m_prefix = aPrefix;
  124. }
  125. void PCB_DIMENSION_BASE::SetSuffix( const wxString& aSuffix )
  126. {
  127. m_suffix = aSuffix;
  128. }
  129. void PCB_DIMENSION_BASE::SetUnits( EDA_UNITS aUnits )
  130. {
  131. m_units = aUnits;
  132. }
  133. DIM_UNITS_MODE PCB_DIMENSION_BASE::GetUnitsMode() const
  134. {
  135. if( m_autoUnits )
  136. {
  137. return DIM_UNITS_MODE::AUTOMATIC;
  138. }
  139. else
  140. {
  141. switch( m_units )
  142. {
  143. default:
  144. case EDA_UNITS::INCHES: return DIM_UNITS_MODE::INCHES;
  145. case EDA_UNITS::MILLIMETRES: return DIM_UNITS_MODE::MILLIMETRES;
  146. case EDA_UNITS::MILS: return DIM_UNITS_MODE::MILS;
  147. }
  148. }
  149. }
  150. void PCB_DIMENSION_BASE::SetUnitsMode( DIM_UNITS_MODE aMode )
  151. {
  152. m_autoUnits = false;
  153. switch( aMode )
  154. {
  155. case DIM_UNITS_MODE::INCHES: m_units = EDA_UNITS::INCHES; break;
  156. case DIM_UNITS_MODE::MILS: m_units = EDA_UNITS::MILS; break;
  157. case DIM_UNITS_MODE::MILLIMETRES: m_units = EDA_UNITS::MILLIMETRES; break;
  158. case DIM_UNITS_MODE::AUTOMATIC: m_autoUnits = true; break;
  159. }
  160. }
  161. void PCB_DIMENSION_BASE::SetText( const wxString& aNewText )
  162. {
  163. m_valueString = aNewText;
  164. updateText();
  165. }
  166. const wxString PCB_DIMENSION_BASE::GetText() const
  167. {
  168. return m_text.GetText();
  169. }
  170. void PCB_DIMENSION_BASE::SetLayer( PCB_LAYER_ID aLayer )
  171. {
  172. m_layer = aLayer;
  173. m_text.SetLayer( aLayer );
  174. }
  175. void PCB_DIMENSION_BASE::Move( const VECTOR2I& offset )
  176. {
  177. m_text.Offset( offset );
  178. m_start += offset;
  179. m_end += offset;
  180. Update();
  181. }
  182. void PCB_DIMENSION_BASE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  183. {
  184. EDA_ANGLE newAngle = m_text.GetTextAngle() + aAngle;
  185. newAngle.Normalize();
  186. m_text.SetTextAngle( newAngle );
  187. VECTOR2I pt = m_text.GetTextPos();
  188. RotatePoint( pt, aRotCentre, aAngle );
  189. m_text.SetTextPos( pt );
  190. RotatePoint( m_start, aRotCentre, aAngle );
  191. RotatePoint( m_end, aRotCentre, aAngle );
  192. Update();
  193. }
  194. void PCB_DIMENSION_BASE::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
  195. {
  196. Mirror( aCentre );
  197. SetLayer( FlipLayer( GetLayer(), GetBoard()->GetCopperLayerCount() ) );
  198. }
  199. void PCB_DIMENSION_BASE::Mirror( const VECTOR2I& axis_pos, bool aMirrorLeftRight )
  200. {
  201. int axis = aMirrorLeftRight ? axis_pos.x : axis_pos.y;
  202. VECTOR2I newPos = m_text.GetTextPos();
  203. #define INVERT( pos ) ( ( pos ) = axis - ( ( pos ) - axis ) )
  204. if( aMirrorLeftRight )
  205. INVERT( newPos.x );
  206. else
  207. INVERT( newPos.y );
  208. m_text.SetTextPos( newPos );
  209. // invert angle
  210. m_text.SetTextAngle( -m_text.GetTextAngle() );
  211. if( aMirrorLeftRight )
  212. {
  213. INVERT( m_start.x );
  214. INVERT( m_end.x );
  215. }
  216. else
  217. {
  218. INVERT( m_start.y );
  219. INVERT( m_end.y );
  220. }
  221. m_text.SetMirrored( !m_text.IsMirrored() );
  222. Update();
  223. }
  224. void PCB_DIMENSION_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame,
  225. std::vector<MSG_PANEL_ITEM>& aList )
  226. {
  227. // for now, display only the text within the DIMENSION using class PCB_TEXT.
  228. wxString msg;
  229. wxCHECK_RET( m_parent != nullptr, wxT( "PCB_TEXT::GetMsgPanelInfo() m_Parent is NULL." ) );
  230. aList.emplace_back( _( "Dimension" ), m_text.GetShownText() );
  231. aList.emplace_back( _( "Prefix" ), GetPrefix() );
  232. if( GetOverrideTextEnabled() )
  233. {
  234. aList.emplace_back( _( "Override Text" ), GetOverrideText() );
  235. }
  236. else
  237. {
  238. aList.emplace_back( _( "Value" ), GetValueText() );
  239. switch( GetPrecision() )
  240. {
  241. case 6:
  242. msg = wxT( "0.00 in / 0 mils / 0.0 mm" );
  243. break;
  244. case 7:
  245. msg = wxT( "0.000 in / 0 mils / 0.00 mm" );
  246. break;
  247. case 8:
  248. msg = wxT( "0.0000 in / 0.0 mils / 0.000 mm" );
  249. break;
  250. case 9:
  251. msg = wxT( "0.00000 in / 0.00 mils / 0.0000 mm" );
  252. break;
  253. default:
  254. msg = wxT( "%" ) + wxString::Format( wxT( "1.%df" ), GetPrecision() );
  255. }
  256. aList.emplace_back( _( "Precision" ), wxString::Format( msg, 0.0 ) );
  257. }
  258. aList.emplace_back( _( "Suffix" ), GetSuffix() );
  259. EDA_UNITS units;
  260. GetUnits( units );
  261. aList.emplace_back( _( "Units" ), GetAbbreviatedUnitsLabel( units ).Trim( false ) );
  262. aList.emplace_back( _( "Font" ), m_text.GetDrawFont()->GetName() );
  263. aList.emplace_back( _( "Text Thickness" ),
  264. MessageTextFromValue( units, m_text.GetTextThickness() ) );
  265. aList.emplace_back( _( "Text Width" ), MessageTextFromValue( units, m_text.GetTextWidth() ) );
  266. aList.emplace_back( _( "Text Height" ), MessageTextFromValue( units, m_text.GetTextHeight() ) );
  267. ORIGIN_TRANSFORMS originTransforms = aFrame->GetOriginTransforms();
  268. units = aFrame->GetUserUnits();
  269. if( Type() == PCB_DIM_CENTER_T || Type() == PCB_FP_DIM_CENTER_T )
  270. {
  271. VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
  272. wxString start = wxString::Format( wxT( "@(%s, %s)" ),
  273. MessageTextFromValue( units, startCoord.x ),
  274. MessageTextFromValue( units, startCoord.y ) );
  275. aList.emplace_back( start, wxEmptyString );
  276. }
  277. else
  278. {
  279. VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
  280. wxString start = wxString::Format( wxT( "@(%s, %s)" ),
  281. MessageTextFromValue( units, startCoord.x ),
  282. MessageTextFromValue( units, startCoord.y ) );
  283. VECTOR2I endCoord = originTransforms.ToDisplayAbs( GetEnd() );
  284. wxString end = wxString::Format( wxT( "@(%s, %s)" ),
  285. MessageTextFromValue( units, endCoord.x ),
  286. MessageTextFromValue( units, endCoord.y ) );
  287. aList.emplace_back( start, end );
  288. }
  289. if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
  290. aList.emplace_back( _( "Status" ), _( "Locked" ) );
  291. aList.emplace_back( _( "Layer" ), GetLayerName() );
  292. }
  293. std::shared_ptr<SHAPE> PCB_DIMENSION_BASE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  294. {
  295. std::shared_ptr<SHAPE_COMPOUND> effectiveShape = std::make_shared<SHAPE_COMPOUND>();
  296. effectiveShape->AddShape( Text().GetEffectiveTextShape()->Clone() );
  297. for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
  298. effectiveShape->AddShape( shape->Clone() );
  299. return effectiveShape;
  300. }
  301. bool PCB_DIMENSION_BASE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  302. {
  303. if( m_text.TextHitTest( aPosition ) )
  304. return true;
  305. int dist_max = aAccuracy + ( m_lineThickness / 2 );
  306. // Locate SEGMENTS
  307. for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
  308. {
  309. if( shape->Collide( aPosition, dist_max ) )
  310. return true;
  311. }
  312. return false;
  313. }
  314. bool PCB_DIMENSION_BASE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
  315. {
  316. EDA_RECT arect = aRect;
  317. arect.Inflate( aAccuracy );
  318. EDA_RECT rect = GetBoundingBox();
  319. if( aAccuracy )
  320. rect.Inflate( aAccuracy );
  321. if( aContained )
  322. return arect.Contains( rect );
  323. return arect.Intersects( rect );
  324. }
  325. const EDA_RECT PCB_DIMENSION_BASE::GetBoundingBox() const
  326. {
  327. EDA_RECT bBox;
  328. int xmin, xmax, ymin, ymax;
  329. bBox = m_text.GetTextBox();
  330. xmin = bBox.GetX();
  331. xmax = bBox.GetRight();
  332. ymin = bBox.GetY();
  333. ymax = bBox.GetBottom();
  334. for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
  335. {
  336. BOX2I shapeBox = shape->BBox();
  337. shapeBox.Inflate( m_lineThickness / 2 );
  338. xmin = std::min( xmin, shapeBox.GetOrigin().x );
  339. xmax = std::max( xmax, shapeBox.GetEnd().x );
  340. ymin = std::min( ymin, shapeBox.GetOrigin().y );
  341. ymax = std::max( ymax, shapeBox.GetEnd().y );
  342. }
  343. bBox.SetX( xmin );
  344. bBox.SetY( ymin );
  345. bBox.SetWidth( xmax - xmin + 1 );
  346. bBox.SetHeight( ymax - ymin + 1 );
  347. bBox.Normalize();
  348. return bBox;
  349. }
  350. wxString PCB_DIMENSION_BASE::GetSelectMenuText( EDA_UNITS aUnits ) const
  351. {
  352. return wxString::Format( _( "Dimension '%s' on %s" ), GetText(), GetLayerName() );
  353. }
  354. const BOX2I PCB_DIMENSION_BASE::ViewBBox() const
  355. {
  356. BOX2I dimBBox = BOX2I( VECTOR2I( GetBoundingBox().GetPosition() ),
  357. VECTOR2I( GetBoundingBox().GetSize() ) );
  358. dimBBox.Merge( m_text.ViewBBox() );
  359. return dimBBox;
  360. }
  361. OPT_VECTOR2I PCB_DIMENSION_BASE::segPolyIntersection( const SHAPE_POLY_SET& aPoly, const SEG& aSeg,
  362. bool aStart )
  363. {
  364. VECTOR2I start( aStart ? aSeg.A : aSeg.B );
  365. VECTOR2I endpoint( aStart ? aSeg.B : aSeg.A );
  366. if( aPoly.Contains( start ) )
  367. return NULLOPT;
  368. for( SHAPE_POLY_SET::CONST_SEGMENT_ITERATOR seg = aPoly.CIterateSegments(); seg; ++seg )
  369. {
  370. if( OPT_VECTOR2I intersection = ( *seg ).Intersect( aSeg ) )
  371. {
  372. if( ( *intersection - start ).SquaredEuclideanNorm() <
  373. ( endpoint - start ).SquaredEuclideanNorm() )
  374. endpoint = *intersection;
  375. }
  376. }
  377. if( start == endpoint )
  378. return NULLOPT;
  379. return OPT_VECTOR2I( endpoint );
  380. }
  381. OPT_VECTOR2I PCB_DIMENSION_BASE::segCircleIntersection( CIRCLE& aCircle, SEG& aSeg, bool aStart )
  382. {
  383. VECTOR2I start( aStart ? aSeg.A : aSeg.B );
  384. VECTOR2I endpoint( aStart ? aSeg.B : aSeg.A );
  385. if( aCircle.Contains( start ) )
  386. return NULLOPT;
  387. std::vector<VECTOR2I> intersections = aCircle.Intersect( aSeg );
  388. for( VECTOR2I& intersection : aCircle.Intersect( aSeg ) )
  389. {
  390. if( ( intersection - start ).SquaredEuclideanNorm() <
  391. ( endpoint - start ).SquaredEuclideanNorm() )
  392. endpoint = intersection;
  393. }
  394. if( start == endpoint )
  395. return NULLOPT;
  396. return OPT_VECTOR2I( endpoint );
  397. }
  398. void PCB_DIMENSION_BASE::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
  399. PCB_LAYER_ID aLayer, int aClearance,
  400. int aError, ERROR_LOC aErrorLoc,
  401. bool aIgnoreLineWidth ) const
  402. {
  403. wxASSERT_MSG( !aIgnoreLineWidth, wxT( "IgnoreLineWidth has no meaning for dimensions." ) );
  404. for( const std::shared_ptr<SHAPE>& shape : m_shapes )
  405. {
  406. const SHAPE_CIRCLE* circle = dynamic_cast<const SHAPE_CIRCLE*>( shape.get() );
  407. const SHAPE_SEGMENT* seg = dynamic_cast<const SHAPE_SEGMENT*>( shape.get() );
  408. if( circle )
  409. {
  410. TransformCircleToPolygon( aCornerBuffer, circle->GetCenter(),
  411. circle->GetRadius() + m_lineThickness / 2 + aClearance,
  412. aError, aErrorLoc );
  413. }
  414. else if( seg )
  415. {
  416. TransformOvalToPolygon( aCornerBuffer, seg->GetSeg().A,
  417. seg->GetSeg().B, m_lineThickness + 2 * aClearance,
  418. aError, aErrorLoc );
  419. }
  420. else
  421. {
  422. wxFAIL_MSG( wxT( "PCB_DIMENSION_BASE::TransformShapeWithClearanceToPolygon unexpected "
  423. "shape type." ) );
  424. }
  425. }
  426. }
  427. PCB_DIM_ALIGNED::PCB_DIM_ALIGNED( BOARD_ITEM* aParent, KICAD_T aType ) :
  428. PCB_DIMENSION_BASE( aParent, aType ),
  429. m_height( 0 )
  430. {
  431. // To preserve look of old dimensions, initialize extension height based on default arrow length
  432. m_extensionHeight = static_cast<int>( m_arrowLength * s_arrowAngle.Sin() );
  433. }
  434. EDA_ITEM* PCB_DIM_ALIGNED::Clone() const
  435. {
  436. return new PCB_DIM_ALIGNED( *this );
  437. }
  438. void PCB_DIM_ALIGNED::SwapData( BOARD_ITEM* aImage )
  439. {
  440. wxASSERT( aImage->Type() == Type() );
  441. m_shapes.clear();
  442. static_cast<PCB_DIM_ALIGNED*>( aImage )->m_shapes.clear();
  443. std::swap( *static_cast<PCB_DIM_ALIGNED*>( this ), *static_cast<PCB_DIM_ALIGNED*>( aImage ) );
  444. Update();
  445. }
  446. BITMAPS PCB_DIM_ALIGNED::GetMenuImage() const
  447. {
  448. return BITMAPS::add_aligned_dimension;
  449. }
  450. void PCB_DIM_ALIGNED::UpdateHeight( const VECTOR2I& aCrossbarStart, const VECTOR2I& aCrossbarEnd )
  451. {
  452. VECTOR2D height( aCrossbarStart - GetStart() );
  453. VECTOR2D crossBar( aCrossbarEnd - aCrossbarStart );
  454. if( height.Cross( crossBar ) > 0 )
  455. m_height = -height.EuclideanNorm();
  456. else
  457. m_height = height.EuclideanNorm();
  458. Update();
  459. }
  460. void PCB_DIM_ALIGNED::updateGeometry()
  461. {
  462. m_shapes.clear();
  463. VECTOR2I dimension( m_end - m_start );
  464. m_measuredValue = KiROUND( dimension.EuclideanNorm() );
  465. VECTOR2I extension;
  466. if( m_height > 0 )
  467. extension = VECTOR2I( -dimension.y, dimension.x );
  468. else
  469. extension = VECTOR2I( dimension.y, -dimension.x );
  470. // Add extension lines
  471. int extensionHeight = std::abs( m_height ) - m_extensionOffset + m_extensionHeight;
  472. VECTOR2I extStart( m_start );
  473. extStart += extension.Resize( m_extensionOffset );
  474. addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
  475. extStart = VECTOR2I( m_end );
  476. extStart += extension.Resize( m_extensionOffset );
  477. addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
  478. // Add crossbar
  479. VECTOR2I crossBarDistance = sign( m_height ) * extension.Resize( m_height );
  480. m_crossBarStart = m_start + crossBarDistance;
  481. m_crossBarEnd = m_end + crossBarDistance;
  482. // Update text after calculating crossbar position but before adding crossbar lines
  483. updateText();
  484. // Now that we have the text updated, we can determine how to draw the crossbar.
  485. // First we need to create an appropriate bounding polygon to collide with
  486. EDA_RECT textBox = m_text.GetTextBox().Inflate( m_text.GetTextWidth() / 2,
  487. - m_text.GetEffectiveTextPenWidth() );
  488. SHAPE_POLY_SET polyBox;
  489. polyBox.NewOutline();
  490. polyBox.Append( textBox.GetOrigin() );
  491. polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
  492. polyBox.Append( textBox.GetEnd() );
  493. polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
  494. polyBox.Rotate( m_text.GetTextAngle(), textBox.GetCenter() );
  495. // The ideal crossbar, if the text doesn't collide
  496. SEG crossbar( m_crossBarStart, m_crossBarEnd );
  497. // Now we can draw 0, 1, or 2 crossbar lines depending on how the polygon collides
  498. bool containsA = polyBox.Contains( crossbar.A );
  499. bool containsB = polyBox.Contains( crossbar.B );
  500. OPT_VECTOR2I endpointA = segPolyIntersection( polyBox, crossbar );
  501. OPT_VECTOR2I endpointB = segPolyIntersection( polyBox, crossbar, false );
  502. if( endpointA )
  503. m_shapes.emplace_back( new SHAPE_SEGMENT( crossbar.A, *endpointA ) );
  504. if( endpointB )
  505. m_shapes.emplace_back( new SHAPE_SEGMENT( *endpointB, crossbar.B ) );
  506. if( !containsA && !containsB && !endpointA && !endpointB )
  507. m_shapes.emplace_back( new SHAPE_SEGMENT( crossbar ) );
  508. // Add arrows
  509. VECTOR2I arrowEndPos( m_arrowLength, 0 );
  510. VECTOR2I arrowEndNeg( m_arrowLength, 0 );
  511. RotatePoint( arrowEndPos, -EDA_ANGLE( dimension ) + s_arrowAngle );
  512. RotatePoint( arrowEndNeg, -EDA_ANGLE( dimension ) - s_arrowAngle );
  513. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarStart, m_crossBarStart + arrowEndPos ) );
  514. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarStart, m_crossBarStart + arrowEndNeg ) );
  515. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarEnd, m_crossBarEnd - arrowEndPos ) );
  516. m_shapes.emplace_back( new SHAPE_SEGMENT( m_crossBarEnd, m_crossBarEnd - arrowEndNeg ) );
  517. }
  518. void PCB_DIM_ALIGNED::updateText()
  519. {
  520. VECTOR2I crossbarCenter( ( m_crossBarEnd - m_crossBarStart ) / 2 );
  521. if( m_textPosition == DIM_TEXT_POSITION::OUTSIDE )
  522. {
  523. int textOffsetDistance = m_text.GetEffectiveTextPenWidth() + m_text.GetTextHeight();
  524. EDA_ANGLE rotation;
  525. if( crossbarCenter.x == 0 )
  526. rotation = ANGLE_90 * sign( -crossbarCenter.y );
  527. else if( crossbarCenter.x < 0 )
  528. rotation = -ANGLE_90;
  529. else
  530. rotation = ANGLE_90;
  531. VECTOR2I textOffset = crossbarCenter;
  532. RotatePoint( textOffset, rotation );
  533. textOffset = crossbarCenter + textOffset.Resize( textOffsetDistance );
  534. m_text.SetTextPos( m_crossBarStart + textOffset );
  535. }
  536. else if( m_textPosition == DIM_TEXT_POSITION::INLINE )
  537. {
  538. m_text.SetTextPos( m_crossBarStart + crossbarCenter );
  539. }
  540. if( m_keepTextAligned )
  541. {
  542. EDA_ANGLE textAngle = FULL_CIRCLE - EDA_ANGLE( crossbarCenter );
  543. textAngle.Normalize();
  544. if( textAngle > ANGLE_90 && textAngle <= ANGLE_270 )
  545. textAngle -= ANGLE_180;
  546. m_text.SetTextAngle( textAngle );
  547. }
  548. PCB_DIMENSION_BASE::updateText();
  549. }
  550. void PCB_DIM_ALIGNED::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  551. {
  552. PCB_DIMENSION_BASE::GetMsgPanelInfo( aFrame, aList );
  553. aList.emplace_back( _( "Height" ), MessageTextFromValue( aFrame->GetUserUnits(), 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. EDA_RECT 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. EDA_RECT 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 = boost::make_optional( false, VECTOR2I() );;
  765. OPT_VECTOR2I textSegEnd = boost::make_optional( false, VECTOR2I() );
  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. wxString msg;
  816. aList.emplace_back( _( "Leader" ), m_text.GetShownText() );
  817. ORIGIN_TRANSFORMS originTransforms = aFrame->GetOriginTransforms();
  818. EDA_UNITS units = aFrame->GetUserUnits();
  819. VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
  820. wxString start = wxString::Format( wxT( "@(%s, %s)" ),
  821. MessageTextFromValue( units, startCoord.x ),
  822. MessageTextFromValue( units, startCoord.y ) );
  823. aList.emplace_back( start, wxEmptyString );
  824. aList.emplace_back( _( "Layer" ), GetLayerName() );
  825. }
  826. PCB_DIM_RADIAL::PCB_DIM_RADIAL( BOARD_ITEM* aParent, bool aInFP ) :
  827. PCB_DIMENSION_BASE( aParent, aInFP ? PCB_FP_DIM_RADIAL_T : PCB_DIM_RADIAL_T )
  828. {
  829. m_unitsFormat = DIM_UNITS_FORMAT::NO_SUFFIX;
  830. m_overrideTextEnabled = false;
  831. m_keepTextAligned = true;
  832. m_isDiameter = false;
  833. m_prefix = "R ";
  834. m_leaderLength = m_arrowLength * 3;
  835. }
  836. EDA_ITEM* PCB_DIM_RADIAL::Clone() const
  837. {
  838. return new PCB_DIM_RADIAL( *this );
  839. }
  840. void PCB_DIM_RADIAL::SwapData( BOARD_ITEM* aImage )
  841. {
  842. wxASSERT( aImage->Type() == Type() );
  843. m_shapes.clear();
  844. static_cast<PCB_DIM_RADIAL*>( aImage )->m_shapes.clear();
  845. std::swap( *static_cast<PCB_DIM_RADIAL*>( this ), *static_cast<PCB_DIM_RADIAL*>( aImage ) );
  846. Update();
  847. }
  848. BITMAPS PCB_DIM_RADIAL::GetMenuImage() const
  849. {
  850. return BITMAPS::add_radial_dimension;
  851. }
  852. VECTOR2I PCB_DIM_RADIAL::GetKnee() const
  853. {
  854. VECTOR2I radial( m_end - m_start );
  855. return m_end + radial.Resize( m_leaderLength );
  856. }
  857. void PCB_DIM_RADIAL::updateText()
  858. {
  859. if( m_keepTextAligned )
  860. {
  861. VECTOR2I textLine( Text().GetPosition() - GetKnee() );
  862. EDA_ANGLE textAngle = FULL_CIRCLE - EDA_ANGLE( textLine );
  863. textAngle.Normalize();
  864. if( textAngle > ANGLE_90 && textAngle <= ANGLE_270 )
  865. textAngle -= ANGLE_180;
  866. // Round to nearest degree
  867. textAngle = EDA_ANGLE( KiROUND( textAngle.AsDegrees() ), DEGREES_T );
  868. m_text.SetTextAngle( textAngle );
  869. }
  870. PCB_DIMENSION_BASE::updateText();
  871. }
  872. void PCB_DIM_RADIAL::updateGeometry()
  873. {
  874. m_shapes.clear();
  875. VECTOR2I center( m_start );
  876. VECTOR2I centerArm( 0, m_arrowLength );
  877. m_shapes.emplace_back( new SHAPE_SEGMENT( center - centerArm, center + centerArm ) );
  878. RotatePoint( centerArm, -ANGLE_90 );
  879. m_shapes.emplace_back( new SHAPE_SEGMENT( center - centerArm, center + centerArm ) );
  880. VECTOR2I radius( m_end - m_start );
  881. if( m_isDiameter )
  882. m_measuredValue = KiROUND( radius.EuclideanNorm() * 2 );
  883. else
  884. m_measuredValue = KiROUND( radius.EuclideanNorm() );
  885. updateText();
  886. // Now that we have the text updated, we can determine how to draw the second line
  887. // First we need to create an appropriate bounding polygon to collide with
  888. EDA_RECT textBox = m_text.GetTextBox().Inflate( m_text.GetTextWidth() / 2,
  889. m_text.GetEffectiveTextPenWidth() );
  890. SHAPE_POLY_SET polyBox;
  891. polyBox.NewOutline();
  892. polyBox.Append( textBox.GetOrigin() );
  893. polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
  894. polyBox.Append( textBox.GetEnd() );
  895. polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
  896. polyBox.Rotate( m_text.GetTextAngle(), textBox.GetCenter() );
  897. VECTOR2I radial( m_end - m_start );
  898. radial = radial.Resize( m_leaderLength );
  899. SEG arrowSeg( m_end, m_end + radial );
  900. SEG textSeg( arrowSeg.B, m_text.GetPosition() );
  901. OPT_VECTOR2I arrowSegEnd = segPolyIntersection( polyBox, arrowSeg );
  902. OPT_VECTOR2I textSegEnd = segPolyIntersection( polyBox, textSeg );
  903. if( arrowSegEnd )
  904. arrowSeg.B = arrowSegEnd.get();
  905. if( textSegEnd )
  906. textSeg.B = textSegEnd.get();
  907. m_shapes.emplace_back( new SHAPE_SEGMENT( arrowSeg ) );
  908. // Add arrows
  909. VECTOR2I arrowEndPos( m_arrowLength, 0 );
  910. VECTOR2I arrowEndNeg( m_arrowLength, 0 );
  911. RotatePoint( arrowEndPos, -EDA_ANGLE( radial ) + s_arrowAngle );
  912. RotatePoint( arrowEndNeg, -EDA_ANGLE( radial ) - s_arrowAngle );
  913. m_shapes.emplace_back( new SHAPE_SEGMENT( m_end, m_end + arrowEndPos ) );
  914. m_shapes.emplace_back( new SHAPE_SEGMENT( m_end, m_end + arrowEndNeg ) );
  915. m_shapes.emplace_back( new SHAPE_SEGMENT( textSeg ) );
  916. }
  917. PCB_DIM_CENTER::PCB_DIM_CENTER( BOARD_ITEM* aParent, bool aInFP ) :
  918. PCB_DIMENSION_BASE( aParent, aInFP ? PCB_FP_DIM_CENTER_T : PCB_DIM_CENTER_T )
  919. {
  920. m_unitsFormat = DIM_UNITS_FORMAT::NO_SUFFIX;
  921. m_overrideTextEnabled = true;
  922. }
  923. EDA_ITEM* PCB_DIM_CENTER::Clone() const
  924. {
  925. return new PCB_DIM_CENTER( *this );
  926. }
  927. void PCB_DIM_CENTER::SwapData( BOARD_ITEM* aImage )
  928. {
  929. wxASSERT( aImage->Type() == Type() );
  930. std::swap( *static_cast<PCB_DIM_CENTER*>( this ), *static_cast<PCB_DIM_CENTER*>( aImage ) );
  931. }
  932. BITMAPS PCB_DIM_CENTER::GetMenuImage() const
  933. {
  934. return BITMAPS::add_center_dimension;
  935. }
  936. const EDA_RECT PCB_DIM_CENTER::GetBoundingBox() const
  937. {
  938. int halfWidth = VECTOR2I( m_end - m_start ).x + ( m_lineThickness / 2.0 );
  939. EDA_RECT bBox;
  940. bBox.SetX( m_start.x - halfWidth );
  941. bBox.SetY( m_start.y - halfWidth );
  942. bBox.SetWidth( halfWidth * 2 );
  943. bBox.SetHeight( halfWidth * 2 );
  944. bBox.Normalize();
  945. return bBox;
  946. }
  947. const BOX2I PCB_DIM_CENTER::ViewBBox() const
  948. {
  949. return BOX2I( VECTOR2I( GetBoundingBox().GetPosition() ),
  950. VECTOR2I( GetBoundingBox().GetSize() ) );
  951. }
  952. void PCB_DIM_CENTER::updateGeometry()
  953. {
  954. m_shapes.clear();
  955. VECTOR2I center( m_start );
  956. VECTOR2I arm( m_end - m_start );
  957. m_shapes.emplace_back( new SHAPE_SEGMENT( center - arm, center + arm ) );
  958. RotatePoint( arm, -ANGLE_90 );
  959. m_shapes.emplace_back( new SHAPE_SEGMENT( center - arm, center + arm ) );
  960. }
  961. static struct DIMENSION_DESC
  962. {
  963. DIMENSION_DESC()
  964. {
  965. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  966. REGISTER_TYPE( PCB_DIMENSION_BASE );
  967. propMgr.InheritsAfter( TYPE_HASH( PCB_DIMENSION_BASE ), TYPE_HASH( BOARD_ITEM ) );
  968. // TODO: add dimension properties:
  969. //propMgr.AddProperty( new PROPERTY<DIMENSION, int>( _HKI( "Height" ),
  970. //&DIMENSION::SetHeight, &DIMENSION::GetHeight, PROPERTY_DISPLAY::DISTANCE ) );
  971. }
  972. } _DIMENSION_DESC;