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.

2047 lines
68 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) 2023 CERN
  8. * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, you may find one here:
  22. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  23. * or you may search the http://www.gnu.org website for the version 2 license,
  24. * or you may write to the Free Software Foundation, Inc.,
  25. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  26. */
  27. #include <bitmaps.h>
  28. #include <pcb_edit_frame.h>
  29. #include <base_units.h>
  30. #include <convert_basic_shapes_to_polygon.h>
  31. #include <font/font.h>
  32. #include <board.h>
  33. #include <pcb_dimension.h>
  34. #include <pcb_text.h>
  35. #include <geometry/shape_compound.h>
  36. #include <geometry/shape_circle.h>
  37. #include <geometry/shape_segment.h>
  38. #include <settings/color_settings.h>
  39. #include <settings/settings_manager.h>
  40. #include <trigo.h>
  41. #include <api/api_enums.h>
  42. #include <api/api_utils.h>
  43. #include <api/board/board_types.pb.h>
  44. static const int INWARD_ARROW_LENGTH_TO_HEAD_RATIO = 2;
  45. static const EDA_ANGLE s_arrowAngle( 27.5, DEGREES_T );
  46. /**
  47. * Find the intersection between a given segment and polygon outline.
  48. *
  49. * @param aPoly is the polygon to collide.
  50. * @param aSeg is the segment to collide.
  51. * @param aStart if true will start from aSeg.A, otherwise aSeg.B.
  52. * @return a point on aSeg that collides with aPoly closest to the start, if one exists.
  53. */
  54. static OPT_VECTOR2I segPolyIntersection( const SHAPE_POLY_SET& aPoly, const SEG& aSeg,
  55. bool aStart = true )
  56. {
  57. VECTOR2I start( aStart ? aSeg.A : aSeg.B );
  58. VECTOR2I endpoint( aStart ? aSeg.B : aSeg.A );
  59. if( aPoly.Contains( start ) )
  60. return std::nullopt;
  61. for( SHAPE_POLY_SET::CONST_SEGMENT_ITERATOR seg = aPoly.CIterateSegments(); seg; ++seg )
  62. {
  63. if( OPT_VECTOR2I intersection = ( *seg ).Intersect( aSeg ) )
  64. {
  65. if( ( *intersection - start ).SquaredEuclideanNorm()
  66. < ( endpoint - start ).SquaredEuclideanNorm() )
  67. endpoint = *intersection;
  68. }
  69. }
  70. if( start == endpoint )
  71. return std::nullopt;
  72. return OPT_VECTOR2I( endpoint );
  73. }
  74. static OPT_VECTOR2I segCircleIntersection( CIRCLE& aCircle, SEG& aSeg, bool aStart = true )
  75. {
  76. VECTOR2I start( aStart ? aSeg.A : aSeg.B );
  77. VECTOR2I endpoint( aStart ? aSeg.B : aSeg.A );
  78. if( aCircle.Contains( start ) )
  79. return std::nullopt;
  80. std::vector<VECTOR2I> intersections = aCircle.Intersect( aSeg );
  81. for( VECTOR2I& intersection : aCircle.Intersect( aSeg ) )
  82. {
  83. if( ( intersection - start ).SquaredEuclideanNorm()
  84. < ( endpoint - start ).SquaredEuclideanNorm() )
  85. endpoint = intersection;
  86. }
  87. if( start == endpoint )
  88. return std::nullopt;
  89. return OPT_VECTOR2I( endpoint );
  90. }
  91. /**
  92. * Knockout a polygon from a segment. This function will add 0, 1 or 2 segments to the
  93. * vector, depending on how the polygon intersects the segment.
  94. */
  95. static void CollectKnockedOutSegments( const SHAPE_POLY_SET& aPoly, const SEG& aSeg,
  96. std::vector<std::shared_ptr<SHAPE>>& aSegmentsAfterKnockout )
  97. {
  98. // Now we can draw 0, 1, or 2 crossbar lines depending on how the polygon collides
  99. const bool containsA = aPoly.Contains( aSeg.A );
  100. const bool containsB = aPoly.Contains( aSeg.B );
  101. const OPT_VECTOR2I endpointA = segPolyIntersection( aPoly, aSeg );
  102. const OPT_VECTOR2I endpointB = segPolyIntersection( aPoly, aSeg, false );
  103. if( endpointA )
  104. aSegmentsAfterKnockout.emplace_back( new SHAPE_SEGMENT( aSeg.A, *endpointA ) );
  105. if( endpointB )
  106. aSegmentsAfterKnockout.emplace_back( new SHAPE_SEGMENT( *endpointB, aSeg.B ) );
  107. if( !containsA && !containsB && !endpointA && !endpointB )
  108. aSegmentsAfterKnockout.emplace_back( new SHAPE_SEGMENT( aSeg ) );
  109. }
  110. PCB_DIMENSION_BASE::PCB_DIMENSION_BASE( BOARD_ITEM* aParent, KICAD_T aType ) :
  111. PCB_TEXT( aParent, aType ),
  112. m_overrideTextEnabled( false ),
  113. m_units( EDA_UNITS::INCHES ),
  114. m_autoUnits( false ),
  115. m_unitsFormat( DIM_UNITS_FORMAT::BARE_SUFFIX ),
  116. m_arrowDirection( DIM_ARROW_DIRECTION::OUTWARD ),
  117. m_precision( DIM_PRECISION::X_XXXX ),
  118. m_suppressZeroes( false ),
  119. m_lineThickness( pcbIUScale.mmToIU( 0.2 ) ),
  120. m_arrowLength( pcbIUScale.MilsToIU( 50 ) ),
  121. m_extensionOffset( 0 ),
  122. m_textPosition( DIM_TEXT_POSITION::OUTSIDE ),
  123. m_keepTextAligned( true ),
  124. m_measuredValue( 0 ),
  125. m_inClearRenderCache( false )
  126. {
  127. m_layer = Dwgs_User;
  128. }
  129. bool PCB_DIMENSION_BASE::operator==( const BOARD_ITEM& aOther ) const
  130. {
  131. if( Type() != aOther.Type() )
  132. return false;
  133. const PCB_DIMENSION_BASE& other = static_cast<const PCB_DIMENSION_BASE&>( aOther );
  134. return *this == other;
  135. }
  136. bool PCB_DIMENSION_BASE::operator==( const PCB_DIMENSION_BASE& aOther ) const
  137. {
  138. if( m_textPosition != aOther.m_textPosition )
  139. return false;
  140. if( m_keepTextAligned != aOther.m_keepTextAligned )
  141. return false;
  142. if( m_units != aOther.m_units )
  143. return false;
  144. if( m_autoUnits != aOther.m_autoUnits )
  145. return false;
  146. if( m_unitsFormat != aOther.m_unitsFormat )
  147. return false;
  148. if( m_precision != aOther.m_precision )
  149. return false;
  150. if( m_suppressZeroes != aOther.m_suppressZeroes )
  151. return false;
  152. if( m_lineThickness != aOther.m_lineThickness )
  153. return false;
  154. if( m_arrowLength != aOther.m_arrowLength )
  155. return false;
  156. if( m_extensionOffset != aOther.m_extensionOffset )
  157. return false;
  158. if( m_measuredValue != aOther.m_measuredValue )
  159. return false;
  160. return EDA_TEXT::operator==( aOther );
  161. }
  162. double PCB_DIMENSION_BASE::Similarity( const BOARD_ITEM& aOther ) const
  163. {
  164. if( m_Uuid == aOther.m_Uuid )
  165. return 1.0;
  166. if( Type() != aOther.Type() )
  167. return 0.0;
  168. const PCB_DIMENSION_BASE& other = static_cast<const PCB_DIMENSION_BASE&>( aOther );
  169. double similarity = 1.0;
  170. if( m_textPosition != other.m_textPosition )
  171. similarity *= 0.9;
  172. if( m_keepTextAligned != other.m_keepTextAligned )
  173. similarity *= 0.9;
  174. if( m_units != other.m_units )
  175. similarity *= 0.9;
  176. if( m_autoUnits != other.m_autoUnits )
  177. similarity *= 0.9;
  178. if( m_unitsFormat != other.m_unitsFormat )
  179. similarity *= 0.9;
  180. if( m_precision != other.m_precision )
  181. similarity *= 0.9;
  182. if( m_suppressZeroes != other.m_suppressZeroes )
  183. similarity *= 0.9;
  184. if( m_lineThickness != other.m_lineThickness )
  185. similarity *= 0.9;
  186. if( m_arrowLength != other.m_arrowLength )
  187. similarity *= 0.9;
  188. if( m_extensionOffset != other.m_extensionOffset )
  189. similarity *= 0.9;
  190. if( m_measuredValue != other.m_measuredValue )
  191. similarity *= 0.9;
  192. similarity *= EDA_TEXT::Similarity( other );
  193. return similarity;
  194. }
  195. void PCB_DIMENSION_BASE::Serialize( google::protobuf::Any &aContainer ) const
  196. {
  197. using namespace kiapi::common;
  198. using namespace kiapi::board::types;
  199. Dimension dimension;
  200. dimension.mutable_id()->set_value( m_Uuid.AsStdString() );
  201. dimension.set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( GetLayer() ) );
  202. dimension.set_locked( IsLocked() ? types::LockedState::LS_LOCKED
  203. : types::LockedState::LS_UNLOCKED );
  204. google::protobuf::Any any;
  205. EDA_TEXT::Serialize( any );
  206. any.UnpackTo( dimension.mutable_text() );
  207. types::Text* text = dimension.mutable_text();
  208. text->set_text( GetValueText() );
  209. dimension.set_override_text_enabled( m_overrideTextEnabled );
  210. dimension.set_override_text( m_valueString.ToUTF8() );
  211. dimension.set_prefix( m_prefix.ToUTF8() );
  212. dimension.set_suffix( m_suffix.ToUTF8() );
  213. dimension.set_unit( ToProtoEnum<DIM_UNITS_MODE, DimensionUnit>( GetUnitsMode() ) );
  214. dimension.set_unit_format(
  215. ToProtoEnum<DIM_UNITS_FORMAT, DimensionUnitFormat>( m_unitsFormat ) );
  216. dimension.set_arrow_direction(
  217. ToProtoEnum<DIM_ARROW_DIRECTION, DimensionArrowDirection>( m_arrowDirection ) );
  218. dimension.set_precision( ToProtoEnum<DIM_PRECISION, DimensionPrecision>( m_precision ) );
  219. dimension.set_suppress_trailing_zeroes( m_suppressZeroes );
  220. dimension.mutable_line_thickness()->set_value_nm( m_lineThickness );
  221. dimension.mutable_arrow_length()->set_value_nm( m_arrowLength );
  222. dimension.mutable_extension_offset()->set_value_nm( m_extensionOffset );
  223. dimension.set_text_position(
  224. ToProtoEnum<DIM_TEXT_POSITION, DimensionTextPosition>( m_textPosition ) );
  225. dimension.set_keep_text_aligned( m_keepTextAligned );
  226. aContainer.PackFrom( dimension );
  227. }
  228. bool PCB_DIMENSION_BASE::Deserialize( const google::protobuf::Any &aContainer )
  229. {
  230. using namespace kiapi::common;
  231. kiapi::board::types::Dimension dimension;
  232. if( !aContainer.UnpackTo( &dimension ) )
  233. return false;
  234. SetLayer( FromProtoEnum<PCB_LAYER_ID, kiapi::board::types::BoardLayer>( dimension.layer() ) );
  235. const_cast<KIID&>( m_Uuid ) = KIID( dimension.id().value() );
  236. SetLocked( dimension.locked() == types::LockedState::LS_LOCKED );
  237. google::protobuf::Any any;
  238. any.PackFrom( dimension.text() );
  239. EDA_TEXT::Deserialize( any );
  240. SetOverrideTextEnabled( dimension.override_text_enabled() );
  241. SetOverrideText( wxString::FromUTF8( dimension.override_text() ) );
  242. SetPrefix( wxString::FromUTF8( dimension.prefix() ) );
  243. SetSuffix( wxString::FromUTF8( dimension.suffix() ) );
  244. SetUnitsMode( FromProtoEnum<DIM_UNITS_MODE>( dimension.unit() ) );
  245. SetUnitsFormat( FromProtoEnum<DIM_UNITS_FORMAT>( dimension.unit_format() ) );
  246. SetArrowDirection( FromProtoEnum<DIM_ARROW_DIRECTION>( dimension.arrow_direction() ) );
  247. SetPrecision( FromProtoEnum<DIM_PRECISION>( dimension.precision() ) );
  248. SetSuppressZeroes( dimension.suppress_trailing_zeroes() );
  249. SetLineThickness( dimension.line_thickness().value_nm() );
  250. SetArrowLength( dimension.arrow_length().value_nm() );
  251. SetExtensionOffset( dimension.extension_offset().value_nm() );
  252. SetTextPositionMode( FromProtoEnum<DIM_TEXT_POSITION>( dimension.text_position() ) );
  253. SetKeepTextAligned( dimension.keep_text_aligned() );
  254. Update();
  255. return true;
  256. }
  257. void PCB_DIMENSION_BASE::drawAnArrow( VECTOR2I startPoint, EDA_ANGLE anAngle, int aLength )
  258. {
  259. if( aLength )
  260. {
  261. VECTOR2I tailEnd( aLength, 0 );
  262. RotatePoint( tailEnd, -anAngle );
  263. m_shapes.emplace_back( new SHAPE_SEGMENT( startPoint, startPoint + tailEnd ) );
  264. }
  265. VECTOR2I arrowEndPos( m_arrowLength, 0 );
  266. VECTOR2I arrowEndNeg( m_arrowLength, 0 );
  267. RotatePoint( arrowEndPos, -anAngle + s_arrowAngle );
  268. RotatePoint( arrowEndNeg, -anAngle - s_arrowAngle );
  269. m_shapes.emplace_back( new SHAPE_SEGMENT( startPoint, startPoint + arrowEndPos ) );
  270. m_shapes.emplace_back( new SHAPE_SEGMENT( startPoint, startPoint + arrowEndNeg ) );
  271. }
  272. void PCB_DIMENSION_BASE::updateText()
  273. {
  274. wxString text = m_overrideTextEnabled ? m_valueString : GetValueText();
  275. switch( m_unitsFormat )
  276. {
  277. case DIM_UNITS_FORMAT::NO_SUFFIX: // no units
  278. break;
  279. case DIM_UNITS_FORMAT::BARE_SUFFIX: // normal
  280. text += EDA_UNIT_UTILS::GetText( m_units );
  281. break;
  282. case DIM_UNITS_FORMAT::PAREN_SUFFIX: // parenthetical
  283. text += wxT( " (" ) + EDA_UNIT_UTILS::GetText( m_units ).Trim( false ) + wxT( ")" );
  284. break;
  285. }
  286. text.Prepend( m_prefix );
  287. text.Append( m_suffix );
  288. SetText( text );
  289. }
  290. void PCB_DIMENSION_BASE::ClearRenderCache()
  291. {
  292. PCB_TEXT::ClearRenderCache();
  293. // We use EDA_TEXT::ClearRenderCache() as a signal that the properties of the EDA_TEXT
  294. // have changed and we may need to update the dimension text
  295. if( !m_inClearRenderCache )
  296. {
  297. m_inClearRenderCache = true;
  298. Update();
  299. m_inClearRenderCache = false;
  300. }
  301. }
  302. template<typename ShapeType>
  303. void PCB_DIMENSION_BASE::addShape( const ShapeType& aShape )
  304. {
  305. m_shapes.push_back( std::make_shared<ShapeType>( aShape ) );
  306. }
  307. wxString PCB_DIMENSION_BASE::GetValueText() const
  308. {
  309. struct lconv* lc = localeconv();
  310. wxChar sep = lc->decimal_point[0];
  311. int val = GetMeasuredValue();
  312. int precision = static_cast<int>( m_precision );
  313. wxString text;
  314. if( precision >= 6 )
  315. {
  316. switch( m_units )
  317. {
  318. case EDA_UNITS::INCHES: precision = precision - 4; break;
  319. case EDA_UNITS::MILS: precision = std::max( 0, precision - 7 ); break;
  320. case EDA_UNITS::MILLIMETRES: precision = precision - 5; break;
  321. default: precision = precision - 4; break;
  322. }
  323. }
  324. wxString format = wxT( "%." ) + wxString::Format( wxT( "%i" ), precision ) + wxT( "f" );
  325. text.Printf( format, EDA_UNIT_UTILS::UI::ToUserUnit( pcbIUScale, m_units, val ) );
  326. if( m_suppressZeroes )
  327. {
  328. while( text.Last() == '0' )
  329. {
  330. text.RemoveLast();
  331. if( text.Last() == '.' || text.Last() == sep )
  332. {
  333. text.RemoveLast();
  334. break;
  335. }
  336. }
  337. }
  338. return text;
  339. }
  340. void PCB_DIMENSION_BASE::SetPrefix( const wxString& aPrefix )
  341. {
  342. m_prefix = aPrefix;
  343. }
  344. void PCB_DIMENSION_BASE::SetSuffix( const wxString& aSuffix )
  345. {
  346. m_suffix = aSuffix;
  347. }
  348. void PCB_DIMENSION_BASE::SetUnits( EDA_UNITS aUnits )
  349. {
  350. m_units = aUnits;
  351. }
  352. DIM_UNITS_MODE PCB_DIMENSION_BASE::GetUnitsMode() const
  353. {
  354. if( m_autoUnits )
  355. {
  356. return DIM_UNITS_MODE::AUTOMATIC;
  357. }
  358. else
  359. {
  360. switch( m_units )
  361. {
  362. default:
  363. case EDA_UNITS::INCHES: return DIM_UNITS_MODE::INCHES;
  364. case EDA_UNITS::MILLIMETRES: return DIM_UNITS_MODE::MILLIMETRES;
  365. case EDA_UNITS::MILS: return DIM_UNITS_MODE::MILS;
  366. }
  367. }
  368. }
  369. void PCB_DIMENSION_BASE::SetUnitsMode( DIM_UNITS_MODE aMode )
  370. {
  371. switch( aMode )
  372. {
  373. case DIM_UNITS_MODE::INCHES:
  374. m_autoUnits = false;
  375. m_units = EDA_UNITS::INCHES;
  376. break;
  377. case DIM_UNITS_MODE::MILS:
  378. m_autoUnits = false;
  379. m_units = EDA_UNITS::MILS;
  380. break;
  381. case DIM_UNITS_MODE::MILLIMETRES:
  382. m_autoUnits = false;
  383. m_units = EDA_UNITS::MILLIMETRES;
  384. break;
  385. case DIM_UNITS_MODE::AUTOMATIC:
  386. m_autoUnits = true;
  387. m_units = GetBoard() ? GetBoard()->GetUserUnits() : EDA_UNITS::MILLIMETRES;
  388. break;
  389. }
  390. }
  391. void PCB_DIMENSION_BASE::ChangeTextAngleDegrees( double aDegrees )
  392. {
  393. SetTextAngleDegrees( aDegrees );
  394. // Create or repair any knockouts
  395. Update();
  396. }
  397. void PCB_DIMENSION_BASE::ChangeKeepTextAligned( bool aKeepAligned )
  398. {
  399. SetKeepTextAligned( aKeepAligned );
  400. // Re-align the text and repair any knockouts
  401. Update();
  402. }
  403. void PCB_DIMENSION_BASE::Move( const VECTOR2I& offset )
  404. {
  405. PCB_TEXT::Offset( offset );
  406. m_start += offset;
  407. m_end += offset;
  408. Update();
  409. }
  410. void PCB_DIMENSION_BASE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  411. {
  412. EDA_ANGLE newAngle = GetTextAngle() + aAngle;
  413. newAngle.Normalize();
  414. SetTextAngle( newAngle );
  415. VECTOR2I pt = GetTextPos();
  416. RotatePoint( pt, aRotCentre, aAngle );
  417. SetTextPos( pt );
  418. RotatePoint( m_start, aRotCentre, aAngle );
  419. RotatePoint( m_end, aRotCentre, aAngle );
  420. Update();
  421. }
  422. void PCB_DIMENSION_BASE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
  423. {
  424. Mirror( aCentre, aFlipDirection );
  425. SetLayer( GetBoard()->FlipLayer( GetLayer() ) );
  426. }
  427. void PCB_DIMENSION_BASE::Mirror( const VECTOR2I& axis_pos, FLIP_DIRECTION aFlipDirection )
  428. {
  429. VECTOR2I newPos = GetTextPos();
  430. MIRROR( newPos, axis_pos, aFlipDirection );
  431. SetTextPos( newPos );
  432. // invert angle
  433. SetTextAngle( -GetTextAngle() );
  434. MIRROR( m_start, axis_pos, aFlipDirection );
  435. MIRROR( m_end, axis_pos, aFlipDirection );
  436. if( IsSideSpecific() )
  437. SetMirrored( !IsMirrored() );
  438. Update();
  439. }
  440. void PCB_DIMENSION_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame,
  441. std::vector<MSG_PANEL_ITEM>& aList )
  442. {
  443. // for now, display only the text within the DIMENSION using class PCB_TEXT.
  444. wxString msg;
  445. wxCHECK_RET( m_parent != nullptr, wxT( "PCB_TEXT::GetMsgPanelInfo() m_Parent is NULL." ) );
  446. // Don't use GetShownText(); we want to see the variable references here
  447. aList.emplace_back( _( "Dimension" ), KIUI::EllipsizeStatusText( aFrame, GetText() ) );
  448. aList.emplace_back( _( "Prefix" ), GetPrefix() );
  449. if( GetOverrideTextEnabled() )
  450. {
  451. aList.emplace_back( _( "Override Text" ), GetOverrideText() );
  452. }
  453. else
  454. {
  455. aList.emplace_back( _( "Value" ), GetValueText() );
  456. switch( GetPrecision() )
  457. {
  458. case DIM_PRECISION::V_VV: msg = wxT( "0.00 in / 0 mils / 0.0 mm" ); break;
  459. case DIM_PRECISION::V_VVV: msg = wxT( "0.000 in / 0 mils / 0.00 mm" ); break;
  460. case DIM_PRECISION::V_VVVV: msg = wxT( "0.0000 in / 0.0 mils / 0.000 mm" ); break;
  461. case DIM_PRECISION::V_VVVVV: msg = wxT( "0.00000 in / 0.00 mils / 0.0000 mm" ); break;
  462. default: msg = wxT( "%" ) + wxString::Format( wxT( "1.%df" ), GetPrecision() );
  463. }
  464. aList.emplace_back( _( "Precision" ), wxString::Format( msg, 0.0 ) );
  465. }
  466. aList.emplace_back( _( "Suffix" ), GetSuffix() );
  467. // Use our own UNITS_PROVIDER to report dimension info in dimension's units rather than
  468. // in frame's units.
  469. UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::MILLIMETRES );
  470. unitsProvider.SetUserUnits( GetUnits() );
  471. aList.emplace_back( _( "Units" ), EDA_UNIT_UTILS::GetLabel( GetUnits() ) );
  472. aList.emplace_back( _( "Font" ), GetFont() ? GetFont()->GetName() : _( "Default" ) );
  473. aList.emplace_back( _( "Text Thickness" ), unitsProvider.MessageTextFromValue( GetTextThickness() ) );
  474. aList.emplace_back( _( "Text Width" ), unitsProvider.MessageTextFromValue( GetTextWidth() ) );
  475. aList.emplace_back( _( "Text Height" ), unitsProvider.MessageTextFromValue( GetTextHeight() ) );
  476. ORIGIN_TRANSFORMS& originTransforms = aFrame->GetOriginTransforms();
  477. if( Type() == PCB_DIM_CENTER_T )
  478. {
  479. VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
  480. wxString start = wxString::Format( wxT( "@(%s, %s)" ),
  481. aFrame->MessageTextFromValue( startCoord.x ),
  482. aFrame->MessageTextFromValue( startCoord.y ) );
  483. aList.emplace_back( start, wxEmptyString );
  484. }
  485. else
  486. {
  487. VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
  488. wxString start = wxString::Format( wxT( "@(%s, %s)" ),
  489. aFrame->MessageTextFromValue( startCoord.x ),
  490. aFrame->MessageTextFromValue( startCoord.y ) );
  491. VECTOR2I endCoord = originTransforms.ToDisplayAbs( GetEnd() );
  492. wxString end = wxString::Format( wxT( "@(%s, %s)" ),
  493. aFrame->MessageTextFromValue( endCoord.x ),
  494. aFrame->MessageTextFromValue( endCoord.y ) );
  495. aList.emplace_back( start, end );
  496. }
  497. if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
  498. aList.emplace_back( _( "Status" ), _( "Locked" ) );
  499. aList.emplace_back( _( "Layer" ), GetLayerName() );
  500. }
  501. std::shared_ptr<SHAPE> PCB_DIMENSION_BASE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
  502. {
  503. std::shared_ptr<SHAPE_COMPOUND> effectiveShape = std::make_shared<SHAPE_COMPOUND>();
  504. effectiveShape->AddShape( GetEffectiveTextShape()->Clone() );
  505. for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
  506. effectiveShape->AddShape( shape->Clone() );
  507. return effectiveShape;
  508. }
  509. bool PCB_DIMENSION_BASE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  510. {
  511. if( TextHitTest( aPosition ) )
  512. return true;
  513. int dist_max = aAccuracy + ( m_lineThickness / 2 );
  514. // Locate SEGMENTS
  515. for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
  516. {
  517. if( shape->Collide( aPosition, dist_max ) )
  518. return true;
  519. }
  520. return false;
  521. }
  522. bool PCB_DIMENSION_BASE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  523. {
  524. BOX2I arect = aRect;
  525. arect.Inflate( aAccuracy );
  526. BOX2I rect = GetBoundingBox();
  527. if( aAccuracy )
  528. rect.Inflate( aAccuracy );
  529. if( aContained )
  530. return arect.Contains( rect );
  531. return arect.Intersects( rect );
  532. }
  533. const BOX2I PCB_DIMENSION_BASE::GetBoundingBox() const
  534. {
  535. BOX2I bBox;
  536. int xmin, xmax, ymin, ymax;
  537. bBox = GetTextBox();
  538. xmin = bBox.GetX();
  539. xmax = bBox.GetRight();
  540. ymin = bBox.GetY();
  541. ymax = bBox.GetBottom();
  542. for( const std::shared_ptr<SHAPE>& shape : GetShapes() )
  543. {
  544. BOX2I shapeBox = shape->BBox();
  545. shapeBox.Inflate( m_lineThickness / 2 );
  546. xmin = std::min( xmin, shapeBox.GetOrigin().x );
  547. xmax = std::max( xmax, shapeBox.GetEnd().x );
  548. ymin = std::min( ymin, shapeBox.GetOrigin().y );
  549. ymax = std::max( ymax, shapeBox.GetEnd().y );
  550. }
  551. bBox.SetX( xmin );
  552. bBox.SetY( ymin );
  553. bBox.SetWidth( xmax - xmin + 1 );
  554. bBox.SetHeight( ymax - ymin + 1 );
  555. bBox.Normalize();
  556. return bBox;
  557. }
  558. wxString PCB_DIMENSION_BASE::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  559. {
  560. return wxString::Format( _( "Dimension '%s' on %s" ),
  561. aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ),
  562. GetLayerName() );
  563. }
  564. const BOX2I PCB_DIMENSION_BASE::ViewBBox() const
  565. {
  566. BOX2I dimBBox = BOX2I( VECTOR2I( GetBoundingBox().GetPosition() ),
  567. VECTOR2I( GetBoundingBox().GetSize() ) );
  568. dimBBox.Merge( PCB_TEXT::ViewBBox() );
  569. return dimBBox;
  570. }
  571. void PCB_DIMENSION_BASE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
  572. int aClearance, int aError, ERROR_LOC aErrorLoc,
  573. bool aIgnoreLineWidth ) const
  574. {
  575. wxASSERT_MSG( !aIgnoreLineWidth, wxT( "IgnoreLineWidth has no meaning for dimensions." ) );
  576. for( const std::shared_ptr<SHAPE>& shape : m_shapes )
  577. {
  578. const SHAPE_CIRCLE* circle = dynamic_cast<const SHAPE_CIRCLE*>( shape.get() );
  579. const SHAPE_SEGMENT* seg = dynamic_cast<const SHAPE_SEGMENT*>( shape.get() );
  580. if( circle )
  581. {
  582. TransformCircleToPolygon( aBuffer, circle->GetCenter(),
  583. circle->GetRadius() + m_lineThickness / 2 + aClearance,
  584. aError, aErrorLoc );
  585. }
  586. else if( seg )
  587. {
  588. TransformOvalToPolygon( aBuffer, seg->GetSeg().A, seg->GetSeg().B,
  589. m_lineThickness + 2 * aClearance, aError, aErrorLoc );
  590. }
  591. else
  592. {
  593. wxFAIL_MSG( wxT( "PCB_DIMENSION_BASE::TransformShapeToPolygon unknown shape type." ) );
  594. }
  595. }
  596. }
  597. PCB_DIM_ALIGNED::PCB_DIM_ALIGNED( BOARD_ITEM* aParent, KICAD_T aType ) :
  598. PCB_DIMENSION_BASE( aParent, aType ),
  599. m_height( 0 )
  600. {
  601. // To preserve look of old dimensions, initialize extension height based on default arrow length
  602. m_extensionHeight = static_cast<int>( m_arrowLength * s_arrowAngle.Sin() );
  603. }
  604. EDA_ITEM* PCB_DIM_ALIGNED::Clone() const
  605. {
  606. return new PCB_DIM_ALIGNED( *this );
  607. }
  608. void PCB_DIM_ALIGNED::Serialize( google::protobuf::Any &aContainer ) const
  609. {
  610. using namespace kiapi::common;
  611. kiapi::board::types::Dimension dimension;
  612. PCB_DIMENSION_BASE::Serialize( aContainer );
  613. aContainer.UnpackTo( &dimension );
  614. PackVector2( *dimension.mutable_aligned()->mutable_start(), m_start );
  615. PackVector2( *dimension.mutable_aligned()->mutable_end(), m_end );
  616. dimension.mutable_aligned()->mutable_height()->set_value_nm( m_height );
  617. dimension.mutable_aligned()->mutable_extension_height()->set_value_nm( m_extensionHeight );
  618. aContainer.PackFrom( dimension );
  619. }
  620. bool PCB_DIM_ALIGNED::Deserialize( const google::protobuf::Any &aContainer )
  621. {
  622. using namespace kiapi::common;
  623. if( !PCB_DIMENSION_BASE::Deserialize( aContainer ) )
  624. return false;
  625. kiapi::board::types::Dimension dimension;
  626. aContainer.UnpackTo( &dimension );
  627. if( !dimension.has_aligned() )
  628. return false;
  629. SetStart( UnpackVector2( dimension.aligned().start() ) );
  630. SetEnd( UnpackVector2( dimension.aligned().end() ) );
  631. SetHeight( dimension.aligned().height().value_nm());
  632. SetExtensionHeight( dimension.aligned().extension_height().value_nm() );
  633. Update();
  634. return true;
  635. }
  636. void PCB_DIM_ALIGNED::swapData( BOARD_ITEM* aImage )
  637. {
  638. wxASSERT( aImage->Type() == Type() );
  639. m_shapes.clear();
  640. static_cast<PCB_DIM_ALIGNED*>( aImage )->m_shapes.clear();
  641. std::swap( *static_cast<PCB_DIM_ALIGNED*>( this ), *static_cast<PCB_DIM_ALIGNED*>( aImage ) );
  642. Update();
  643. }
  644. void PCB_DIM_ALIGNED::Mirror( const VECTOR2I& axis_pos, FLIP_DIRECTION aFlipDirection )
  645. {
  646. m_height = -m_height;
  647. // Call this last for the Update()
  648. PCB_DIMENSION_BASE::Mirror( axis_pos, aFlipDirection );
  649. }
  650. BITMAPS PCB_DIM_ALIGNED::GetMenuImage() const
  651. {
  652. return BITMAPS::add_aligned_dimension;
  653. }
  654. void PCB_DIM_ALIGNED::UpdateHeight( const VECTOR2I& aCrossbarStart, const VECTOR2I& aCrossbarEnd )
  655. {
  656. VECTOR2D height( aCrossbarStart - GetStart() );
  657. VECTOR2D crossBar( aCrossbarEnd - aCrossbarStart );
  658. if( height.Cross( crossBar ) > 0 )
  659. m_height = -height.EuclideanNorm();
  660. else
  661. m_height = height.EuclideanNorm();
  662. Update();
  663. }
  664. void PCB_DIM_ALIGNED::updateGeometry()
  665. {
  666. m_shapes.clear();
  667. VECTOR2I dimension( m_end - m_start );
  668. m_measuredValue = KiROUND( dimension.EuclideanNorm() );
  669. VECTOR2I extension;
  670. if( m_height > 0 )
  671. extension = VECTOR2I( -dimension.y, dimension.x );
  672. else
  673. extension = VECTOR2I( dimension.y, -dimension.x );
  674. // Add extension lines
  675. int extensionHeight = std::abs( m_height ) - m_extensionOffset + m_extensionHeight;
  676. VECTOR2I extStart( m_start );
  677. extStart += extension.Resize( m_extensionOffset );
  678. addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
  679. extStart = VECTOR2I( m_end );
  680. extStart += extension.Resize( m_extensionOffset );
  681. addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
  682. // Add crossbar
  683. VECTOR2I crossBarDistance = sign( m_height ) * extension.Resize( m_height );
  684. m_crossBarStart = m_start + crossBarDistance;
  685. m_crossBarEnd = m_end + crossBarDistance;
  686. // Update text after calculating crossbar position but before adding crossbar lines
  687. updateText();
  688. // Now that we have the text updated, we can determine how to draw the crossbar.
  689. // First we need to create an appropriate bounding polygon to collide with
  690. BOX2I textBox = GetTextBox().Inflate( GetTextWidth() / 2, - GetEffectiveTextPenWidth() );
  691. SHAPE_POLY_SET polyBox;
  692. polyBox.NewOutline();
  693. polyBox.Append( textBox.GetOrigin() );
  694. polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
  695. polyBox.Append( textBox.GetEnd() );
  696. polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
  697. polyBox.Rotate( GetTextAngle(), textBox.GetCenter() );
  698. // The ideal crossbar, if the text doesn't collide
  699. SEG crossbar( m_crossBarStart, m_crossBarEnd );
  700. CollectKnockedOutSegments( polyBox, crossbar, m_shapes );
  701. if( m_arrowDirection == DIM_ARROW_DIRECTION::INWARD )
  702. {
  703. drawAnArrow( m_crossBarStart, EDA_ANGLE( dimension ) + EDA_ANGLE( 180 ),
  704. m_arrowLength * INWARD_ARROW_LENGTH_TO_HEAD_RATIO );
  705. drawAnArrow( m_crossBarEnd, EDA_ANGLE( dimension ),
  706. m_arrowLength * INWARD_ARROW_LENGTH_TO_HEAD_RATIO );
  707. }
  708. else
  709. {
  710. drawAnArrow( m_crossBarStart, EDA_ANGLE( dimension ), 0 );
  711. drawAnArrow( m_crossBarEnd, EDA_ANGLE( dimension ) + EDA_ANGLE( 180 ), 0 );
  712. }
  713. }
  714. void PCB_DIM_ALIGNED::updateText()
  715. {
  716. VECTOR2I crossbarCenter( ( m_crossBarEnd - m_crossBarStart ) / 2 );
  717. if( m_textPosition == DIM_TEXT_POSITION::OUTSIDE )
  718. {
  719. int textOffsetDistance = GetEffectiveTextPenWidth() + GetTextHeight();
  720. EDA_ANGLE rotation;
  721. if( crossbarCenter.x == 0 )
  722. rotation = ANGLE_90 * sign( -crossbarCenter.y );
  723. else if( crossbarCenter.x < 0 )
  724. rotation = -ANGLE_90;
  725. else
  726. rotation = ANGLE_90;
  727. VECTOR2I textOffset = crossbarCenter;
  728. RotatePoint( textOffset, rotation );
  729. textOffset = crossbarCenter + textOffset.Resize( textOffsetDistance );
  730. SetTextPos( m_crossBarStart + textOffset );
  731. }
  732. else if( m_textPosition == DIM_TEXT_POSITION::INLINE )
  733. {
  734. SetTextPos( m_crossBarStart + crossbarCenter );
  735. }
  736. if( m_keepTextAligned )
  737. {
  738. EDA_ANGLE textAngle = FULL_CIRCLE - EDA_ANGLE( crossbarCenter );
  739. textAngle.Normalize();
  740. if( textAngle > ANGLE_90 && textAngle <= ANGLE_270 )
  741. textAngle -= ANGLE_180;
  742. SetTextAngle( textAngle );
  743. }
  744. PCB_DIMENSION_BASE::updateText();
  745. }
  746. void PCB_DIM_ALIGNED::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  747. {
  748. PCB_DIMENSION_BASE::GetMsgPanelInfo( aFrame, aList );
  749. // Use our own UNITS_PROVIDER to report dimension info in dimension's units rather than
  750. // in frame's units.
  751. UNITS_PROVIDER unitsProvider( pcbIUScale, EDA_UNITS::MILLIMETRES );
  752. unitsProvider.SetUserUnits( GetUnits() );
  753. aList.emplace_back( _( "Height" ), unitsProvider.MessageTextFromValue( m_height ) );
  754. }
  755. PCB_DIM_ORTHOGONAL::PCB_DIM_ORTHOGONAL( BOARD_ITEM* aParent ) :
  756. PCB_DIM_ALIGNED( aParent, PCB_DIM_ORTHOGONAL_T )
  757. {
  758. // To preserve look of old dimensions, initialize extension height based on default arrow length
  759. m_extensionHeight = static_cast<int>( m_arrowLength * s_arrowAngle.Sin() );
  760. m_orientation = DIR::HORIZONTAL;
  761. }
  762. EDA_ITEM* PCB_DIM_ORTHOGONAL::Clone() const
  763. {
  764. return new PCB_DIM_ORTHOGONAL( *this );
  765. }
  766. void PCB_DIM_ORTHOGONAL::Serialize( google::protobuf::Any &aContainer ) const
  767. {
  768. using namespace kiapi::common;
  769. kiapi::board::types::Dimension dimension;
  770. PCB_DIMENSION_BASE::Serialize( aContainer );
  771. aContainer.UnpackTo( &dimension );
  772. PackVector2( *dimension.mutable_orthogonal()->mutable_start(), m_start );
  773. PackVector2( *dimension.mutable_orthogonal()->mutable_end(), m_end );
  774. dimension.mutable_orthogonal()->mutable_height()->set_value_nm( m_height );
  775. dimension.mutable_orthogonal()->mutable_extension_height()->set_value_nm( m_extensionHeight );
  776. dimension.mutable_orthogonal()->set_alignment( m_orientation == DIR::VERTICAL
  777. ? types::AxisAlignment::AA_Y_AXIS
  778. : types::AxisAlignment::AA_X_AXIS );
  779. aContainer.PackFrom( dimension );
  780. }
  781. bool PCB_DIM_ORTHOGONAL::Deserialize( const google::protobuf::Any &aContainer )
  782. {
  783. using namespace kiapi::common;
  784. if( !PCB_DIMENSION_BASE::Deserialize( aContainer ) )
  785. return false;
  786. kiapi::board::types::Dimension dimension;
  787. aContainer.UnpackTo( &dimension );
  788. if( !dimension.has_orthogonal() )
  789. return false;
  790. SetStart( UnpackVector2( dimension.orthogonal().start() ) );
  791. SetEnd( UnpackVector2( dimension.orthogonal().end() ) );
  792. SetHeight( dimension.orthogonal().height().value_nm());
  793. SetExtensionHeight( dimension.orthogonal().extension_height().value_nm() );
  794. SetOrientation( dimension.orthogonal().alignment() == types::AxisAlignment::AA_Y_AXIS
  795. ? DIR::VERTICAL
  796. : DIR::HORIZONTAL );
  797. Update();
  798. return true;
  799. }
  800. void PCB_DIM_ORTHOGONAL::swapData( BOARD_ITEM* aImage )
  801. {
  802. wxASSERT( aImage->Type() == Type() );
  803. m_shapes.clear();
  804. static_cast<PCB_DIM_ORTHOGONAL*>( aImage )->m_shapes.clear();
  805. std::swap( *static_cast<PCB_DIM_ORTHOGONAL*>( this ),
  806. *static_cast<PCB_DIM_ORTHOGONAL*>( aImage ) );
  807. Update();
  808. }
  809. void PCB_DIM_ORTHOGONAL::Mirror( const VECTOR2I& axis_pos, FLIP_DIRECTION aFlipDirection )
  810. {
  811. // Only reverse the height if the height is aligned with the flip
  812. if( m_orientation == DIR::HORIZONTAL && aFlipDirection == FLIP_DIRECTION::TOP_BOTTOM )
  813. m_height = -m_height;
  814. else if( m_orientation == DIR::VERTICAL && aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
  815. m_height = -m_height;
  816. // Call this last, as we need the Update()
  817. PCB_DIMENSION_BASE::Mirror( axis_pos, aFlipDirection );
  818. }
  819. BITMAPS PCB_DIM_ORTHOGONAL::GetMenuImage() const
  820. {
  821. return BITMAPS::add_orthogonal_dimension;
  822. }
  823. void PCB_DIM_ORTHOGONAL::updateGeometry()
  824. {
  825. m_shapes.clear();
  826. int measurement = ( m_orientation == DIR::HORIZONTAL ? m_end.x - m_start.x :
  827. m_end.y - m_start.y );
  828. m_measuredValue = KiROUND( std::abs( measurement ) );
  829. VECTOR2I extension;
  830. if( m_orientation == DIR::HORIZONTAL )
  831. extension = VECTOR2I( 0, m_height );
  832. else
  833. extension = VECTOR2I( m_height, 0 );
  834. // Add first extension line
  835. int extensionHeight = std::abs( m_height ) - m_extensionOffset + m_extensionHeight;
  836. VECTOR2I extStart( m_start );
  837. extStart += extension.Resize( m_extensionOffset );
  838. addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
  839. // Add crossbar
  840. VECTOR2I crossBarDistance = sign( m_height ) * extension.Resize( m_height );
  841. m_crossBarStart = m_start + crossBarDistance;
  842. if( m_orientation == DIR::HORIZONTAL )
  843. m_crossBarEnd = VECTOR2I( m_end.x, m_crossBarStart.y );
  844. else
  845. m_crossBarEnd = VECTOR2I( m_crossBarStart.x, m_end.y );
  846. // Add second extension line (m_end to crossbar end)
  847. if( m_orientation == DIR::HORIZONTAL )
  848. extension = VECTOR2I( 0, m_end.y - m_crossBarEnd.y );
  849. else
  850. extension = VECTOR2I( m_end.x - m_crossBarEnd.x, 0 );
  851. extensionHeight = extension.EuclideanNorm() - m_extensionOffset + m_extensionHeight;
  852. extStart = VECTOR2I( m_crossBarEnd );
  853. extStart -= extension.Resize( m_extensionHeight );
  854. addShape( SHAPE_SEGMENT( extStart, extStart + extension.Resize( extensionHeight ) ) );
  855. // Update text after calculating crossbar position but before adding crossbar lines
  856. updateText();
  857. // Now that we have the text updated, we can determine how to draw the crossbar.
  858. // First we need to create an appropriate bounding polygon to collide with
  859. BOX2I textBox = GetTextBox().Inflate( GetTextWidth() / 2, GetEffectiveTextPenWidth() );
  860. SHAPE_POLY_SET polyBox;
  861. polyBox.NewOutline();
  862. polyBox.Append( textBox.GetOrigin() );
  863. polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
  864. polyBox.Append( textBox.GetEnd() );
  865. polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
  866. polyBox.Rotate( GetTextAngle(), textBox.GetCenter() );
  867. // The ideal crossbar, if the text doesn't collide
  868. SEG crossbar( m_crossBarStart, m_crossBarEnd );
  869. CollectKnockedOutSegments( polyBox, crossbar, m_shapes );
  870. EDA_ANGLE crossBarAngle( m_crossBarEnd - m_crossBarStart );
  871. if( m_arrowDirection == DIM_ARROW_DIRECTION::INWARD )
  872. {
  873. // Arrows with fixed length.
  874. drawAnArrow( m_crossBarStart, crossBarAngle + EDA_ANGLE( 180 ),
  875. m_arrowLength * INWARD_ARROW_LENGTH_TO_HEAD_RATIO );
  876. drawAnArrow( m_crossBarEnd, crossBarAngle, m_arrowLength * INWARD_ARROW_LENGTH_TO_HEAD_RATIO );
  877. }
  878. else
  879. {
  880. drawAnArrow( m_crossBarStart, crossBarAngle, 0 );
  881. drawAnArrow( m_crossBarEnd, crossBarAngle + EDA_ANGLE( 180 ), 0 );
  882. }
  883. }
  884. void PCB_DIM_ORTHOGONAL::updateText()
  885. {
  886. VECTOR2I crossbarCenter( ( m_crossBarEnd - m_crossBarStart ) / 2 );
  887. if( m_textPosition == DIM_TEXT_POSITION::OUTSIDE )
  888. {
  889. int textOffsetDistance = GetEffectiveTextPenWidth() + GetTextHeight();
  890. VECTOR2I textOffset;
  891. if( m_orientation == DIR::HORIZONTAL )
  892. textOffset.y = -textOffsetDistance;
  893. else
  894. textOffset.x = -textOffsetDistance;
  895. textOffset += crossbarCenter;
  896. SetTextPos( m_crossBarStart + textOffset );
  897. }
  898. else if( m_textPosition == DIM_TEXT_POSITION::INLINE )
  899. {
  900. SetTextPos( m_crossBarStart + crossbarCenter );
  901. }
  902. if( m_keepTextAligned )
  903. {
  904. if( abs( crossbarCenter.x ) > abs( crossbarCenter.y ) )
  905. SetTextAngle( ANGLE_HORIZONTAL );
  906. else
  907. SetTextAngle( ANGLE_VERTICAL );
  908. }
  909. PCB_DIM_ALIGNED::updateText();
  910. }
  911. void PCB_DIM_ORTHOGONAL::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
  912. {
  913. EDA_ANGLE angle( aAngle );
  914. // restrict angle to -179.9 to 180.0 degrees
  915. angle.Normalize180();
  916. // adjust orientation and height to new angle
  917. // we can only handle the cases of -90, 0, 90, 180 degrees exactly;
  918. // in the other cases we will use the nearest 90 degree angle to
  919. // choose at least an approximate axis for the target orientation
  920. // In case of exactly 45 or 135 degrees, we will round towards zero for consistency
  921. if( angle > ANGLE_45 && angle <= ANGLE_135 )
  922. {
  923. // about 90 degree
  924. if( m_orientation == DIR::HORIZONTAL )
  925. {
  926. m_orientation = DIR::VERTICAL;
  927. }
  928. else
  929. {
  930. m_orientation = DIR::HORIZONTAL;
  931. m_height = -m_height;
  932. }
  933. }
  934. else if( angle < -ANGLE_45 && angle >= -ANGLE_135 )
  935. {
  936. // about -90 degree
  937. if( m_orientation == DIR::HORIZONTAL )
  938. {
  939. m_orientation = DIR::VERTICAL;
  940. m_height = -m_height;
  941. }
  942. else
  943. {
  944. m_orientation = DIR::HORIZONTAL;
  945. }
  946. }
  947. else if( angle > ANGLE_135 || angle < -ANGLE_135 )
  948. {
  949. // about 180 degree
  950. m_height = -m_height;
  951. }
  952. // this will update m_crossBarStart and m_crossbarEnd
  953. PCB_DIMENSION_BASE::Rotate( aRotCentre, angle );
  954. }
  955. PCB_DIM_LEADER::PCB_DIM_LEADER( BOARD_ITEM* aParent ) :
  956. PCB_DIMENSION_BASE( aParent, PCB_DIM_LEADER_T ),
  957. m_textBorder( DIM_TEXT_BORDER::NONE )
  958. {
  959. m_unitsFormat = DIM_UNITS_FORMAT::NO_SUFFIX;
  960. m_overrideTextEnabled = true;
  961. m_keepTextAligned = false;
  962. SetOverrideText( _( "Leader" ) );
  963. }
  964. void PCB_DIM_LEADER::Serialize( google::protobuf::Any &aContainer ) const
  965. {
  966. using namespace kiapi::common;
  967. kiapi::board::types::Dimension dimension;
  968. PCB_DIMENSION_BASE::Serialize( aContainer );
  969. aContainer.UnpackTo( &dimension );
  970. PackVector2( *dimension.mutable_leader()->mutable_start(), m_start );
  971. PackVector2( *dimension.mutable_leader()->mutable_end(), m_end );
  972. dimension.mutable_leader()->set_border_style(
  973. ToProtoEnum<DIM_TEXT_BORDER, kiapi::board::types::DimensionTextBorderStyle>(
  974. m_textBorder ) );
  975. aContainer.PackFrom( dimension );
  976. }
  977. bool PCB_DIM_LEADER::Deserialize( const google::protobuf::Any &aContainer )
  978. {
  979. using namespace kiapi::common;
  980. if( !PCB_DIMENSION_BASE::Deserialize( aContainer ) )
  981. return false;
  982. kiapi::board::types::Dimension dimension;
  983. aContainer.UnpackTo( &dimension );
  984. if( !dimension.has_leader() )
  985. return false;
  986. SetStart( UnpackVector2( dimension.leader().start() ) );
  987. SetEnd( UnpackVector2( dimension.leader().end() ) );
  988. SetTextBorder( FromProtoEnum<DIM_TEXT_BORDER>( dimension.leader().border_style() ) );
  989. Update();
  990. return true;
  991. }
  992. EDA_ITEM* PCB_DIM_LEADER::Clone() const
  993. {
  994. return new PCB_DIM_LEADER( *this );
  995. }
  996. void PCB_DIM_LEADER::swapData( BOARD_ITEM* aImage )
  997. {
  998. wxASSERT( aImage->Type() == Type() );
  999. m_shapes.clear();
  1000. static_cast<PCB_DIM_LEADER*>( aImage )->m_shapes.clear();
  1001. std::swap( *static_cast<PCB_DIM_LEADER*>( this ), *static_cast<PCB_DIM_LEADER*>( aImage ) );
  1002. Update();
  1003. }
  1004. BITMAPS PCB_DIM_LEADER::GetMenuImage() const
  1005. {
  1006. return BITMAPS::add_leader;
  1007. }
  1008. void PCB_DIM_LEADER::updateText()
  1009. {
  1010. // Our geometry is dependent on the size of the text, so just update the whole shebang
  1011. updateGeometry();
  1012. }
  1013. void PCB_DIM_LEADER::updateGeometry()
  1014. {
  1015. m_shapes.clear();
  1016. PCB_DIMENSION_BASE::updateText();
  1017. // Now that we have the text updated, we can determine how to draw the second line
  1018. // First we need to create an appropriate bounding polygon to collide with
  1019. BOX2I textBox = GetTextBox().Inflate( GetTextWidth() / 2, GetEffectiveTextPenWidth() * 2 );
  1020. SHAPE_POLY_SET polyBox;
  1021. polyBox.NewOutline();
  1022. polyBox.Append( textBox.GetOrigin() );
  1023. polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
  1024. polyBox.Append( textBox.GetEnd() );
  1025. polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
  1026. polyBox.Rotate( GetTextAngle(), textBox.GetCenter() );
  1027. VECTOR2I firstLine( m_end - m_start );
  1028. VECTOR2I start( m_start );
  1029. start += firstLine.Resize( m_extensionOffset );
  1030. SEG arrowSeg( m_start, m_end );
  1031. SEG textSeg( m_end, GetTextPos() );
  1032. OPT_VECTOR2I arrowSegEnd;
  1033. OPT_VECTOR2I textSegEnd;
  1034. if( m_textBorder == DIM_TEXT_BORDER::CIRCLE )
  1035. {
  1036. double penWidth = GetEffectiveTextPenWidth() / 2.0;
  1037. double radius = ( textBox.GetWidth() / 2.0 ) - penWidth;
  1038. CIRCLE circle( textBox.GetCenter(), radius );
  1039. arrowSegEnd = segCircleIntersection( circle, arrowSeg );
  1040. textSegEnd = segCircleIntersection( circle, textSeg );
  1041. }
  1042. else
  1043. {
  1044. arrowSegEnd = segPolyIntersection( polyBox, arrowSeg );
  1045. textSegEnd = segPolyIntersection( polyBox, textSeg );
  1046. }
  1047. if( !arrowSegEnd )
  1048. arrowSegEnd = m_end;
  1049. m_shapes.emplace_back( new SHAPE_SEGMENT( start, *arrowSegEnd ) );
  1050. drawAnArrow( start, EDA_ANGLE( firstLine ), 0 );
  1051. if( !GetText().IsEmpty() )
  1052. {
  1053. switch( m_textBorder )
  1054. {
  1055. case DIM_TEXT_BORDER::RECTANGLE:
  1056. {
  1057. for( SHAPE_POLY_SET::SEGMENT_ITERATOR seg = polyBox.IterateSegments(); seg; seg++ )
  1058. m_shapes.emplace_back( new SHAPE_SEGMENT( *seg ) );
  1059. break;
  1060. }
  1061. case DIM_TEXT_BORDER::CIRCLE:
  1062. {
  1063. double penWidth = GetEffectiveTextPenWidth() / 2.0;
  1064. double radius = ( textBox.GetWidth() / 2.0 ) - penWidth;
  1065. m_shapes.emplace_back( new SHAPE_CIRCLE( textBox.GetCenter(), radius ) );
  1066. break;
  1067. }
  1068. default:
  1069. break;
  1070. }
  1071. }
  1072. if( textSegEnd && *arrowSegEnd == m_end )
  1073. m_shapes.emplace_back( new SHAPE_SEGMENT( m_end, *textSegEnd ) );
  1074. }
  1075. void PCB_DIM_LEADER::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  1076. {
  1077. // Don't use GetShownText(); we want to see the variable references here
  1078. aList.emplace_back( _( "Leader" ), KIUI::EllipsizeStatusText( aFrame, GetText() ) );
  1079. ORIGIN_TRANSFORMS& originTransforms = aFrame->GetOriginTransforms();
  1080. VECTOR2I startCoord = originTransforms.ToDisplayAbs( GetStart() );
  1081. wxString start = wxString::Format( wxT( "@(%s, %s)" ),
  1082. aFrame->MessageTextFromValue( startCoord.x ),
  1083. aFrame->MessageTextFromValue( startCoord.y ) );
  1084. aList.emplace_back( start, wxEmptyString );
  1085. aList.emplace_back( _( "Layer" ), GetLayerName() );
  1086. }
  1087. PCB_DIM_RADIAL::PCB_DIM_RADIAL( BOARD_ITEM* aParent ) :
  1088. PCB_DIMENSION_BASE( aParent, PCB_DIM_RADIAL_T )
  1089. {
  1090. m_unitsFormat = DIM_UNITS_FORMAT::NO_SUFFIX;
  1091. m_overrideTextEnabled = false;
  1092. m_keepTextAligned = true;
  1093. m_prefix = "R ";
  1094. m_leaderLength = m_arrowLength * 3;
  1095. }
  1096. void PCB_DIM_RADIAL::Serialize( google::protobuf::Any &aContainer ) const
  1097. {
  1098. using namespace kiapi::common;
  1099. kiapi::board::types::Dimension dimension;
  1100. PCB_DIMENSION_BASE::Serialize( aContainer );
  1101. aContainer.UnpackTo( &dimension );
  1102. PackVector2( *dimension.mutable_radial()->mutable_center(), m_start );
  1103. PackVector2( *dimension.mutable_radial()->mutable_radius_point(), m_end );
  1104. dimension.mutable_radial()->mutable_leader_length()->set_value_nm( m_leaderLength );
  1105. aContainer.PackFrom( dimension );
  1106. }
  1107. bool PCB_DIM_RADIAL::Deserialize( const google::protobuf::Any &aContainer )
  1108. {
  1109. using namespace kiapi::common;
  1110. if( !PCB_DIMENSION_BASE::Deserialize( aContainer ) )
  1111. return false;
  1112. kiapi::board::types::Dimension dimension;
  1113. aContainer.UnpackTo( &dimension );
  1114. if( !dimension.has_radial() )
  1115. return false;
  1116. SetStart( UnpackVector2( dimension.radial().center() ) );
  1117. SetEnd( UnpackVector2( dimension.radial().radius_point() ) );
  1118. SetLeaderLength( dimension.radial().leader_length().value_nm() );
  1119. Update();
  1120. return true;
  1121. }
  1122. EDA_ITEM* PCB_DIM_RADIAL::Clone() const
  1123. {
  1124. return new PCB_DIM_RADIAL( *this );
  1125. }
  1126. void PCB_DIM_RADIAL::swapData( BOARD_ITEM* aImage )
  1127. {
  1128. wxASSERT( aImage->Type() == Type() );
  1129. m_shapes.clear();
  1130. static_cast<PCB_DIM_RADIAL*>( aImage )->m_shapes.clear();
  1131. std::swap( *static_cast<PCB_DIM_RADIAL*>( this ), *static_cast<PCB_DIM_RADIAL*>( aImage ) );
  1132. Update();
  1133. }
  1134. BITMAPS PCB_DIM_RADIAL::GetMenuImage() const
  1135. {
  1136. return BITMAPS::add_radial_dimension;
  1137. }
  1138. VECTOR2I PCB_DIM_RADIAL::GetKnee() const
  1139. {
  1140. VECTOR2I radial( m_end - m_start );
  1141. return m_end + radial.Resize( m_leaderLength );
  1142. }
  1143. void PCB_DIM_RADIAL::updateText()
  1144. {
  1145. if( m_keepTextAligned )
  1146. {
  1147. VECTOR2I textLine( GetTextPos() - GetKnee() );
  1148. EDA_ANGLE textAngle = FULL_CIRCLE - EDA_ANGLE( textLine );
  1149. textAngle.Normalize();
  1150. if( textAngle > ANGLE_90 && textAngle <= ANGLE_270 )
  1151. textAngle -= ANGLE_180;
  1152. // Round to nearest degree
  1153. textAngle = EDA_ANGLE( KiROUND( textAngle.AsDegrees() ), DEGREES_T );
  1154. SetTextAngle( textAngle );
  1155. }
  1156. PCB_DIMENSION_BASE::updateText();
  1157. }
  1158. void PCB_DIM_RADIAL::updateGeometry()
  1159. {
  1160. m_shapes.clear();
  1161. VECTOR2I center( m_start );
  1162. VECTOR2I centerArm( 0, m_arrowLength );
  1163. m_shapes.emplace_back( new SHAPE_SEGMENT( center - centerArm, center + centerArm ) );
  1164. RotatePoint( centerArm, -ANGLE_90 );
  1165. m_shapes.emplace_back( new SHAPE_SEGMENT( center - centerArm, center + centerArm ) );
  1166. VECTOR2I radius( m_end - m_start );
  1167. m_measuredValue = KiROUND( radius.EuclideanNorm() );
  1168. updateText();
  1169. // Now that we have the text updated, we can determine how to draw the second line
  1170. // First we need to create an appropriate bounding polygon to collide with
  1171. BOX2I textBox = GetTextBox().Inflate( GetTextWidth() / 2, GetEffectiveTextPenWidth() );
  1172. SHAPE_POLY_SET polyBox;
  1173. polyBox.NewOutline();
  1174. polyBox.Append( textBox.GetOrigin() );
  1175. polyBox.Append( textBox.GetOrigin().x, textBox.GetEnd().y );
  1176. polyBox.Append( textBox.GetEnd() );
  1177. polyBox.Append( textBox.GetEnd().x, textBox.GetOrigin().y );
  1178. polyBox.Rotate( GetTextAngle(), textBox.GetCenter() );
  1179. VECTOR2I radial( m_end - m_start );
  1180. radial = radial.Resize( m_leaderLength );
  1181. SEG arrowSeg( m_end, m_end + radial );
  1182. SEG textSeg( arrowSeg.B, GetTextPos() );
  1183. CollectKnockedOutSegments( polyBox, arrowSeg, m_shapes );
  1184. CollectKnockedOutSegments( polyBox, textSeg, m_shapes );
  1185. drawAnArrow( m_end, EDA_ANGLE( radial ), 0 );
  1186. }
  1187. PCB_DIM_CENTER::PCB_DIM_CENTER( BOARD_ITEM* aParent ) :
  1188. PCB_DIMENSION_BASE( aParent, PCB_DIM_CENTER_T )
  1189. {
  1190. m_unitsFormat = DIM_UNITS_FORMAT::NO_SUFFIX;
  1191. m_overrideTextEnabled = true;
  1192. }
  1193. void PCB_DIM_CENTER::Serialize( google::protobuf::Any &aContainer ) const
  1194. {
  1195. using namespace kiapi::common;
  1196. kiapi::board::types::Dimension dimension;
  1197. PCB_DIMENSION_BASE::Serialize( aContainer );
  1198. aContainer.UnpackTo( &dimension );
  1199. PackVector2( *dimension.mutable_center()->mutable_center(), m_start );
  1200. PackVector2( *dimension.mutable_center()->mutable_end(), m_end );
  1201. aContainer.PackFrom( dimension );
  1202. }
  1203. bool PCB_DIM_CENTER::Deserialize( const google::protobuf::Any &aContainer )
  1204. {
  1205. using namespace kiapi::common;
  1206. if( !PCB_DIMENSION_BASE::Deserialize( aContainer ) )
  1207. return false;
  1208. kiapi::board::types::Dimension dimension;
  1209. aContainer.UnpackTo( &dimension );
  1210. if( !dimension.has_center() )
  1211. return false;
  1212. SetStart( UnpackVector2( dimension.center().center() ) );
  1213. SetEnd( UnpackVector2( dimension.center().end() ) );
  1214. Update();
  1215. return true;
  1216. }
  1217. EDA_ITEM* PCB_DIM_CENTER::Clone() const
  1218. {
  1219. return new PCB_DIM_CENTER( *this );
  1220. }
  1221. void PCB_DIM_CENTER::swapData( BOARD_ITEM* aImage )
  1222. {
  1223. wxASSERT( aImage->Type() == Type() );
  1224. std::swap( *static_cast<PCB_DIM_CENTER*>( this ), *static_cast<PCB_DIM_CENTER*>( aImage ) );
  1225. }
  1226. BITMAPS PCB_DIM_CENTER::GetMenuImage() const
  1227. {
  1228. return BITMAPS::add_center_dimension;
  1229. }
  1230. const BOX2I PCB_DIM_CENTER::GetBoundingBox() const
  1231. {
  1232. int halfWidth = VECTOR2I( m_end - m_start ).x + ( m_lineThickness / 2.0 );
  1233. BOX2I bBox;
  1234. bBox.SetX( m_start.x - halfWidth );
  1235. bBox.SetY( m_start.y - halfWidth );
  1236. bBox.SetWidth( halfWidth * 2 );
  1237. bBox.SetHeight( halfWidth * 2 );
  1238. bBox.Normalize();
  1239. return bBox;
  1240. }
  1241. const BOX2I PCB_DIM_CENTER::ViewBBox() const
  1242. {
  1243. return BOX2I( VECTOR2I( GetBoundingBox().GetPosition() ),
  1244. VECTOR2I( GetBoundingBox().GetSize() ) );
  1245. }
  1246. void PCB_DIM_CENTER::updateGeometry()
  1247. {
  1248. m_shapes.clear();
  1249. VECTOR2I center( m_start );
  1250. VECTOR2I arm( m_end - m_start );
  1251. m_shapes.emplace_back( new SHAPE_SEGMENT( center - arm, center + arm ) );
  1252. RotatePoint( arm, -ANGLE_90 );
  1253. m_shapes.emplace_back( new SHAPE_SEGMENT( center - arm, center + arm ) );
  1254. updateText();
  1255. }
  1256. static struct DIMENSION_DESC
  1257. {
  1258. DIMENSION_DESC()
  1259. {
  1260. ENUM_MAP<DIM_PRECISION>::Instance()
  1261. .Map( DIM_PRECISION::X, _HKI( "0" ) )
  1262. .Map( DIM_PRECISION::X_X, _HKI( "0.0" ) )
  1263. .Map( DIM_PRECISION::X_XX, _HKI( "0.00" ) )
  1264. .Map( DIM_PRECISION::X_XXX, _HKI( "0.000" ) )
  1265. .Map( DIM_PRECISION::X_XXXX, _HKI( "0.0000" ) )
  1266. .Map( DIM_PRECISION::X_XXXXX, _HKI( "0.00000" ) )
  1267. .Map( DIM_PRECISION::V_VV, _HKI( "0.00 in / 0 mils / 0.0 mm" ) )
  1268. .Map( DIM_PRECISION::V_VVV, _HKI( "0.000 / 0 / 0.00" ) )
  1269. .Map( DIM_PRECISION::V_VVVV, _HKI( "0.0000 / 0.0 / 0.000" ) )
  1270. .Map( DIM_PRECISION::V_VVVVV, _HKI( "0.00000 / 0.00 / 0.0000" ) );
  1271. ENUM_MAP<DIM_UNITS_FORMAT>::Instance()
  1272. .Map( DIM_UNITS_FORMAT::NO_SUFFIX, _HKI( "1234.0" ) )
  1273. .Map( DIM_UNITS_FORMAT::BARE_SUFFIX, _HKI( "1234.0 mm" ) )
  1274. .Map( DIM_UNITS_FORMAT::PAREN_SUFFIX, _HKI( "1234.0 (mm)" ) );
  1275. ENUM_MAP<DIM_UNITS_MODE>::Instance()
  1276. .Map( DIM_UNITS_MODE::INCHES, _HKI( "Inches" ) )
  1277. .Map( DIM_UNITS_MODE::MILS, _HKI( "Mils" ) )
  1278. .Map( DIM_UNITS_MODE::MILLIMETRES, _HKI( "Millimeters" ) )
  1279. .Map( DIM_UNITS_MODE::AUTOMATIC, _HKI( "Automatic" ) );
  1280. ENUM_MAP<DIM_ARROW_DIRECTION>::Instance()
  1281. .Map( DIM_ARROW_DIRECTION::INWARD, _HKI( "Inward" ) )
  1282. .Map( DIM_ARROW_DIRECTION::OUTWARD, _HKI( "Outward" ) );
  1283. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1284. REGISTER_TYPE( PCB_DIMENSION_BASE );
  1285. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIMENSION_BASE, PCB_TEXT> );
  1286. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIMENSION_BASE, BOARD_ITEM> );
  1287. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIMENSION_BASE, EDA_TEXT> );
  1288. propMgr.InheritsAfter( TYPE_HASH( PCB_DIMENSION_BASE ), TYPE_HASH( PCB_TEXT ) );
  1289. propMgr.InheritsAfter( TYPE_HASH( PCB_DIMENSION_BASE ), TYPE_HASH( BOARD_ITEM ) );
  1290. propMgr.InheritsAfter( TYPE_HASH( PCB_DIMENSION_BASE ), TYPE_HASH( EDA_TEXT ) );
  1291. propMgr.Mask( TYPE_HASH( PCB_DIMENSION_BASE ), TYPE_HASH( EDA_TEXT ), _HKI( "Orientation" ) );
  1292. const wxString groupDimension = _HKI( "Dimension Properties" );
  1293. auto isLeader =
  1294. []( INSPECTABLE* aItem ) -> bool
  1295. {
  1296. return dynamic_cast<PCB_DIM_LEADER*>( aItem ) != nullptr;
  1297. };
  1298. auto isNotLeader =
  1299. []( INSPECTABLE* aItem ) -> bool
  1300. {
  1301. return dynamic_cast<PCB_DIM_LEADER*>( aItem ) == nullptr;
  1302. };
  1303. auto isMultiArrowDirection =
  1304. []( INSPECTABLE* aItem ) -> bool
  1305. {
  1306. return dynamic_cast<PCB_DIM_ALIGNED*>( aItem ) != nullptr;
  1307. };
  1308. propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, wxString>( _HKI( "Prefix" ),
  1309. &PCB_DIMENSION_BASE::ChangePrefix, &PCB_DIMENSION_BASE::GetPrefix ),
  1310. groupDimension )
  1311. .SetAvailableFunc( isNotLeader );
  1312. propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, wxString>( _HKI( "Suffix" ),
  1313. &PCB_DIMENSION_BASE::ChangeSuffix, &PCB_DIMENSION_BASE::GetSuffix ),
  1314. groupDimension )
  1315. .SetAvailableFunc( isNotLeader );
  1316. propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, wxString>( _HKI( "Override Text" ),
  1317. &PCB_DIMENSION_BASE::ChangeOverrideText, &PCB_DIMENSION_BASE::GetOverrideText ),
  1318. groupDimension )
  1319. .SetAvailableFunc( isNotLeader );
  1320. propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, wxString>( _HKI( "Text" ),
  1321. &PCB_DIMENSION_BASE::ChangeOverrideText, &PCB_DIMENSION_BASE::GetOverrideText ),
  1322. groupDimension )
  1323. .SetAvailableFunc( isLeader );
  1324. propMgr.AddProperty( new PROPERTY_ENUM<PCB_DIMENSION_BASE, DIM_UNITS_MODE>( _HKI( "Units" ),
  1325. &PCB_DIMENSION_BASE::ChangeUnitsMode, &PCB_DIMENSION_BASE::GetUnitsMode ),
  1326. groupDimension )
  1327. .SetAvailableFunc( isNotLeader );
  1328. propMgr.AddProperty( new PROPERTY_ENUM<PCB_DIMENSION_BASE, DIM_UNITS_FORMAT>( _HKI( "Units Format" ),
  1329. &PCB_DIMENSION_BASE::ChangeUnitsFormat, &PCB_DIMENSION_BASE::GetUnitsFormat ),
  1330. groupDimension )
  1331. .SetAvailableFunc( isNotLeader );
  1332. propMgr.AddProperty( new PROPERTY_ENUM<PCB_DIMENSION_BASE, DIM_PRECISION>( _HKI( "Precision" ),
  1333. &PCB_DIMENSION_BASE::ChangePrecision, &PCB_DIMENSION_BASE::GetPrecision ),
  1334. groupDimension )
  1335. .SetAvailableFunc( isNotLeader );
  1336. propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, bool>( _HKI( "Suppress Trailing Zeroes" ),
  1337. &PCB_DIMENSION_BASE::ChangeSuppressZeroes, &PCB_DIMENSION_BASE::GetSuppressZeroes ),
  1338. groupDimension )
  1339. .SetAvailableFunc( isNotLeader );
  1340. propMgr.AddProperty( new PROPERTY_ENUM<PCB_DIMENSION_BASE, DIM_ARROW_DIRECTION>( _HKI( "Arrow Direction"),
  1341. &PCB_DIMENSION_BASE::ChangeArrowDirection, &PCB_DIMENSION_BASE::GetArrowDirection ),
  1342. groupDimension )
  1343. .SetAvailableFunc( isMultiArrowDirection );
  1344. const wxString groupText = _HKI( "Text Properties" );
  1345. const auto isTextOrientationWriteable =
  1346. []( INSPECTABLE* aItem ) -> bool
  1347. {
  1348. return !static_cast<PCB_DIMENSION_BASE*>( aItem )->GetKeepTextAligned();
  1349. };
  1350. propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, bool>( _HKI( "Keep Aligned with Dimension" ),
  1351. &PCB_DIMENSION_BASE::ChangeKeepTextAligned,
  1352. &PCB_DIMENSION_BASE::GetKeepTextAligned ),
  1353. groupText );
  1354. propMgr.AddProperty( new PROPERTY<PCB_DIMENSION_BASE, double>( _HKI( "Orientation" ),
  1355. &PCB_DIMENSION_BASE::ChangeTextAngleDegrees,
  1356. &PCB_DIMENSION_BASE::GetTextAngleDegreesProp,
  1357. PROPERTY_DISPLAY::PT_DEGREE ),
  1358. groupText )
  1359. .SetWriteableFunc( isTextOrientationWriteable );
  1360. }
  1361. } _DIMENSION_DESC;
  1362. ENUM_TO_WXANY( DIM_PRECISION )
  1363. ENUM_TO_WXANY( DIM_UNITS_FORMAT )
  1364. ENUM_TO_WXANY( DIM_UNITS_MODE )
  1365. ENUM_TO_WXANY( DIM_ARROW_DIRECTION )
  1366. static struct ALIGNED_DIMENSION_DESC
  1367. {
  1368. ALIGNED_DIMENSION_DESC()
  1369. {
  1370. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1371. REGISTER_TYPE( PCB_DIM_ALIGNED );
  1372. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ALIGNED, BOARD_ITEM> );
  1373. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ALIGNED, EDA_TEXT> );
  1374. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ALIGNED, PCB_TEXT> );
  1375. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ALIGNED, PCB_DIMENSION_BASE> );
  1376. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( BOARD_ITEM ) );
  1377. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( EDA_TEXT ) );
  1378. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( PCB_TEXT ) );
  1379. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( PCB_DIMENSION_BASE ) );
  1380. const wxString groupDimension = _HKI( "Dimension Properties" );
  1381. propMgr.AddProperty( new PROPERTY<PCB_DIM_ALIGNED, int>( _HKI( "Crossbar Height" ),
  1382. &PCB_DIM_ALIGNED::ChangeHeight, &PCB_DIM_ALIGNED::GetHeight,
  1383. PROPERTY_DISPLAY::PT_SIZE ),
  1384. groupDimension );
  1385. propMgr.AddProperty( new PROPERTY<PCB_DIM_ALIGNED, int>( _HKI( "Extension Line Overshoot" ),
  1386. &PCB_DIM_ALIGNED::ChangeExtensionHeight, &PCB_DIM_ALIGNED::GetExtensionHeight,
  1387. PROPERTY_DISPLAY::PT_SIZE ),
  1388. groupDimension );
  1389. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( EDA_TEXT ),
  1390. _HKI( "Visible" ),
  1391. []( INSPECTABLE* aItem ) { return false; } );
  1392. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( EDA_TEXT ),
  1393. _HKI( "Text" ),
  1394. []( INSPECTABLE* aItem ) { return false; } );
  1395. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( EDA_TEXT ),
  1396. _HKI( "Vertical Justification" ),
  1397. []( INSPECTABLE* aItem ) { return false; } );
  1398. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( EDA_TEXT ),
  1399. _HKI( "Hyperlink" ),
  1400. []( INSPECTABLE* aItem ) { return false; } );
  1401. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ALIGNED ), TYPE_HASH( BOARD_ITEM ),
  1402. _HKI( "Knockout" ),
  1403. []( INSPECTABLE* aItem ) { return false; } );
  1404. }
  1405. } ALIGNED_DIMENSION_DESC;
  1406. static struct ORTHOGONAL_DIMENSION_DESC
  1407. {
  1408. ORTHOGONAL_DIMENSION_DESC()
  1409. {
  1410. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1411. REGISTER_TYPE( PCB_DIM_ORTHOGONAL );
  1412. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ORTHOGONAL, BOARD_ITEM> );
  1413. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ORTHOGONAL, EDA_TEXT> );
  1414. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ORTHOGONAL, PCB_TEXT> );
  1415. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ORTHOGONAL, PCB_DIMENSION_BASE> );
  1416. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_ORTHOGONAL, PCB_DIM_ALIGNED> );
  1417. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( BOARD_ITEM ) );
  1418. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( EDA_TEXT ) );
  1419. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( PCB_TEXT ) );
  1420. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( PCB_DIMENSION_BASE ) );
  1421. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( PCB_DIM_ALIGNED ) );
  1422. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( EDA_TEXT ),
  1423. _HKI( "Visible" ),
  1424. []( INSPECTABLE* aItem ) { return false; } );
  1425. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( EDA_TEXT ),
  1426. _HKI( "Text" ),
  1427. []( INSPECTABLE* aItem ) { return false; } );
  1428. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( EDA_TEXT ),
  1429. _HKI( "Vertical Justification" ),
  1430. []( INSPECTABLE* aItem ) { return false; } );
  1431. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( EDA_TEXT ),
  1432. _HKI( "Hyperlink" ),
  1433. []( INSPECTABLE* aItem ) { return false; } );
  1434. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_ORTHOGONAL ), TYPE_HASH( BOARD_ITEM ),
  1435. _HKI( "Knockout" ),
  1436. []( INSPECTABLE* aItem ) { return false; } );
  1437. }
  1438. } ORTHOGONAL_DIMENSION_DESC;
  1439. static struct RADIAL_DIMENSION_DESC
  1440. {
  1441. RADIAL_DIMENSION_DESC()
  1442. {
  1443. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1444. REGISTER_TYPE( PCB_DIM_RADIAL );
  1445. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_RADIAL, BOARD_ITEM> );
  1446. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_RADIAL, EDA_TEXT> );
  1447. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_RADIAL, PCB_TEXT> );
  1448. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_RADIAL, PCB_DIMENSION_BASE> );
  1449. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( BOARD_ITEM ) );
  1450. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( EDA_TEXT ) );
  1451. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( PCB_TEXT ) );
  1452. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( PCB_DIMENSION_BASE ) );
  1453. const wxString groupDimension = _HKI( "Dimension Properties" );
  1454. propMgr.AddProperty( new PROPERTY<PCB_DIM_RADIAL, int>( _HKI( "Leader Length" ),
  1455. &PCB_DIM_RADIAL::ChangeLeaderLength, &PCB_DIM_RADIAL::GetLeaderLength,
  1456. PROPERTY_DISPLAY::PT_SIZE ),
  1457. groupDimension );
  1458. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( EDA_TEXT ),
  1459. _HKI( "Visible" ),
  1460. []( INSPECTABLE* aItem ) { return false; } );
  1461. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( EDA_TEXT ),
  1462. _HKI( "Text" ),
  1463. []( INSPECTABLE* aItem ) { return false; } );
  1464. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( EDA_TEXT ),
  1465. _HKI( "Vertical Justification" ),
  1466. []( INSPECTABLE* aItem ) { return false; } );
  1467. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( EDA_TEXT ),
  1468. _HKI( "Hyperlink" ),
  1469. []( INSPECTABLE* aItem ) { return false; } );
  1470. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_RADIAL ), TYPE_HASH( BOARD_ITEM ),
  1471. _HKI( "Knockout" ),
  1472. []( INSPECTABLE* aItem ) { return false; } );
  1473. }
  1474. } RADIAL_DIMENSION_DESC;
  1475. static struct LEADER_DIMENSION_DESC
  1476. {
  1477. LEADER_DIMENSION_DESC()
  1478. {
  1479. ENUM_MAP<DIM_TEXT_BORDER>::Instance()
  1480. .Map( DIM_TEXT_BORDER::NONE, _HKI( "None" ) )
  1481. .Map( DIM_TEXT_BORDER::RECTANGLE, _HKI( "Rectangle" ) )
  1482. .Map( DIM_TEXT_BORDER::CIRCLE, _HKI( "Circle" ) );
  1483. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1484. REGISTER_TYPE( PCB_DIM_LEADER );
  1485. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_LEADER, BOARD_ITEM> );
  1486. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_LEADER, EDA_TEXT> );
  1487. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_LEADER, PCB_TEXT> );
  1488. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_LEADER, PCB_DIMENSION_BASE> );
  1489. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( BOARD_ITEM ) );
  1490. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( EDA_TEXT ) );
  1491. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( PCB_TEXT ) );
  1492. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( PCB_DIMENSION_BASE ) );
  1493. const wxString groupDimension = _HKI( "Dimension Properties" );
  1494. propMgr.AddProperty( new PROPERTY_ENUM<PCB_DIM_LEADER, DIM_TEXT_BORDER>( _HKI( "Text Frame" ),
  1495. &PCB_DIM_LEADER::ChangeTextBorder, &PCB_DIM_LEADER::GetTextBorder ),
  1496. groupDimension );
  1497. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( EDA_TEXT ),
  1498. _HKI( "Visible" ),
  1499. []( INSPECTABLE* aItem ) { return false; } );
  1500. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( EDA_TEXT ),
  1501. _HKI( "Text" ),
  1502. []( INSPECTABLE* aItem ) { return false; } );
  1503. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( EDA_TEXT ),
  1504. _HKI( "Vertical Justification" ),
  1505. []( INSPECTABLE* aItem ) { return false; } );
  1506. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( EDA_TEXT ),
  1507. _HKI( "Hyperlink" ),
  1508. []( INSPECTABLE* aItem ) { return false; } );
  1509. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_LEADER ), TYPE_HASH( BOARD_ITEM ),
  1510. _HKI( "Knockout" ),
  1511. []( INSPECTABLE* aItem ) { return false; } );
  1512. }
  1513. } LEADER_DIMENSION_DESC;
  1514. ENUM_TO_WXANY( DIM_TEXT_BORDER )
  1515. static struct CENTER_DIMENSION_DESC
  1516. {
  1517. CENTER_DIMENSION_DESC()
  1518. {
  1519. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1520. REGISTER_TYPE( PCB_DIM_CENTER );
  1521. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_CENTER, BOARD_ITEM> );
  1522. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_CENTER, EDA_TEXT> );
  1523. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_CENTER, PCB_TEXT> );
  1524. propMgr.AddTypeCast( new TYPE_CAST<PCB_DIM_CENTER, PCB_DIMENSION_BASE> );
  1525. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( BOARD_ITEM ) );
  1526. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( EDA_TEXT ) );
  1527. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( PCB_TEXT ) );
  1528. propMgr.InheritsAfter( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( PCB_DIMENSION_BASE ) );
  1529. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( EDA_TEXT ),
  1530. _HKI( "Visible" ),
  1531. []( INSPECTABLE* aItem ) { return false; } );
  1532. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( EDA_TEXT ),
  1533. _HKI( "Text" ),
  1534. []( INSPECTABLE* aItem ) { return false; } );
  1535. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( EDA_TEXT ),
  1536. _HKI( "Vertical Justification" ),
  1537. []( INSPECTABLE* aItem ) { return false; } );
  1538. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( EDA_TEXT ),
  1539. _HKI( "Hyperlink" ),
  1540. []( INSPECTABLE* aItem ) { return false; } );
  1541. propMgr.OverrideAvailability( TYPE_HASH( PCB_DIM_CENTER ), TYPE_HASH( BOARD_ITEM ),
  1542. _HKI( "Knockout" ),
  1543. []( INSPECTABLE* aItem ) { return false; } );
  1544. }
  1545. } CENTER_DIMENSION_DESC;