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.

735 lines
24 KiB

2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 Chris Pavlina <pavlina.chris@gmail.com>
  5. * Copyright (C) 2015, 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /******************************************************************************
  25. * Field autoplacer: Tries to find an optimal place for symbol fields, and places them there.
  26. * There are two modes: "auto"-autoplace, and "manual" autoplace.
  27. * Auto mode is for when the process is run automatically, like when rotating parts, and it
  28. * avoids doing things that would be helpful for the final positioning but annoying if they
  29. * happened without permission.
  30. * Short description of the process:
  31. *
  32. * 1. Compute the dimensions of the fields' bounding box ::computeFBoxSize
  33. * 2. Determine which side the fields will go on. ::chooseSideForFields
  34. * 1. Sort the four sides in preference order,
  35. * depending on the symbol's shape and
  36. * orientation ::getPreferredSides
  37. * 2. If in manual mode, sift out the sides that would
  38. * cause fields to overlap other items ::getCollidingSides
  39. * 3. If any remaining sides have zero pins there,
  40. * choose the highest zero-pin side according to
  41. * preference order.
  42. * 4. If all sides have pins, choose the side with the
  43. * fewest pins.
  44. * 3. Compute the position of the fields' bounding box ::fieldBoxPlacement
  45. * 4. In manual mode, shift the box vertically if possible
  46. * to fit fields between adjacent wires ::fitFieldsBetweenWires
  47. * 5. Move all fields to their final positions
  48. * 1. Re-justify fields if options allow that ::justifyField
  49. * 2. Round to a 50-mil grid coordinate if desired
  50. */
  51. #include <boost/range/adaptor/reversed.hpp>
  52. #include <sch_edit_frame.h>
  53. #include <sch_symbol.h>
  54. #include <sch_line.h>
  55. #include <sch_pin.h>
  56. #include <kiface_base.h>
  57. #include <algorithm>
  58. #include <tool/tool_manager.h>
  59. #include <tools/ee_selection_tool.h>
  60. #include <eeschema_settings.h>
  61. #include <core/arraydim.h>
  62. #define FIELD_PADDING schIUScale.MilsToIU( 15 ) // arbitrarily chosen for aesthetics
  63. #define WIRE_V_SPACING schIUScale.MilsToIU( 100 )
  64. #define HPADDING schIUScale.MilsToIU( 25 ) // arbitrarily chosen for aesthetics
  65. #define VPADDING schIUScale.MilsToIU( 15 ) // arbitrarily chosen for aesthetics
  66. /**
  67. * Round up/down to the nearest multiple of n
  68. */
  69. template<typename T> T round_n( const T& value, const T& n, bool aRoundUp )
  70. {
  71. if( value % n )
  72. return n * (value / n + (aRoundUp ? 1 : 0));
  73. else
  74. return value;
  75. }
  76. class AUTOPLACER
  77. {
  78. public:
  79. typedef VECTOR2I SIDE;
  80. static const SIDE SIDE_TOP, SIDE_BOTTOM, SIDE_LEFT, SIDE_RIGHT;
  81. enum COLLISION { COLLIDE_NONE, COLLIDE_OBJECTS, COLLIDE_H_WIRES };
  82. struct SIDE_AND_NPINS
  83. {
  84. SIDE side;
  85. unsigned pins;
  86. };
  87. struct SIDE_AND_COLL
  88. {
  89. SIDE side;
  90. COLLISION collision;
  91. };
  92. AUTOPLACER( SCH_SYMBOL* aSymbol, SCH_SCREEN* aScreen ) :
  93. m_screen( aScreen ),
  94. m_symbol( aSymbol )
  95. {
  96. m_symbol->GetFields( m_fields, /* aVisibleOnly */ true );
  97. auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
  98. wxASSERT( cfg );
  99. m_allow_rejustify = false;
  100. m_align_to_grid = true;
  101. if( cfg )
  102. {
  103. m_allow_rejustify = cfg->m_AutoplaceFields.allow_rejustify;
  104. m_align_to_grid = cfg->m_AutoplaceFields.align_to_grid;
  105. }
  106. m_symbol_bbox = m_symbol->GetBodyBoundingBox();
  107. m_fbox_size = computeFBoxSize( /* aDynamic */ true );
  108. m_is_power_symbol = !m_symbol->IsInNetlist();
  109. if( aScreen )
  110. getPossibleCollisions( m_colliders );
  111. }
  112. /**
  113. * Do the actual autoplacement.
  114. * @param aManual - if true, use extra heuristics for smarter placement when manually
  115. * called up.
  116. */
  117. void DoAutoplace( bool aManual )
  118. {
  119. bool forceWireSpacing = false;
  120. SIDE_AND_NPINS sideandpins = chooseSideForFields( aManual );
  121. SIDE field_side = sideandpins.side;
  122. VECTOR2I fbox_pos = fieldBoxPlacement( sideandpins );
  123. BOX2I field_box( fbox_pos, m_fbox_size );
  124. if( aManual )
  125. forceWireSpacing = fitFieldsBetweenWires( &field_box, field_side );
  126. // Move the fields
  127. int last_y_coord = field_box.GetTop();
  128. for( unsigned field_idx = 0; field_idx < m_fields.size(); ++field_idx )
  129. {
  130. SCH_FIELD* field = m_fields[field_idx];
  131. if( !field->IsVisible() || !field->CanAutoplace() )
  132. continue;
  133. if( m_allow_rejustify )
  134. {
  135. if( sideandpins.pins > 0 )
  136. {
  137. if( field_side == SIDE_TOP || field_side == SIDE_BOTTOM )
  138. justifyField( field, SIDE_RIGHT );
  139. else
  140. justifyField( field, SIDE_TOP );
  141. }
  142. else
  143. {
  144. justifyField( field, field_side );
  145. }
  146. }
  147. VECTOR2I pos( fieldHPlacement( field, field_box ),
  148. fieldVPlacement( field, field_box, &last_y_coord, !forceWireSpacing ) );
  149. if( m_align_to_grid )
  150. {
  151. if( abs( field_side.x ) > 0 )
  152. pos.x = round_n( pos.x, schIUScale.MilsToIU( 50 ), field_side.x >= 0 );
  153. if( abs( field_side.y ) > 0 )
  154. pos.y = round_n( pos.y, schIUScale.MilsToIU( 50 ), field_side.y >= 0 );
  155. }
  156. field->SetPosition( pos );
  157. }
  158. }
  159. protected:
  160. /**
  161. * Compute and return the size of the fields' bounding box.
  162. * @param aDynamic - if true, use dynamic spacing
  163. */
  164. VECTOR2I computeFBoxSize( bool aDynamic )
  165. {
  166. int max_field_width = 0;
  167. int total_height = 0;
  168. for( SCH_FIELD* field : m_fields )
  169. {
  170. if( !field->IsVisible() || !field->CanAutoplace() )
  171. {
  172. continue;
  173. }
  174. if( m_symbol->GetTransform().y1 )
  175. field->SetTextAngle( ANGLE_VERTICAL );
  176. else
  177. field->SetTextAngle( ANGLE_HORIZONTAL );
  178. BOX2I bbox = field->GetBoundingBox();
  179. int field_width = bbox.GetWidth();
  180. int field_height = bbox.GetHeight();
  181. max_field_width = std::max( max_field_width, field_width );
  182. if( !aDynamic )
  183. total_height += WIRE_V_SPACING;
  184. else if( m_align_to_grid )
  185. total_height += round_n( field_height, schIUScale.MilsToIU( 50 ), true );
  186. else
  187. total_height += field_height + FIELD_PADDING;
  188. }
  189. return VECTOR2I( max_field_width, total_height );
  190. }
  191. /**
  192. * Return the side that a pin is on.
  193. */
  194. SIDE getPinSide( SCH_PIN* aPin )
  195. {
  196. PIN_ORIENTATION pin_orient = aPin->PinDrawOrient( m_symbol->GetTransform() );
  197. switch( pin_orient )
  198. {
  199. case PIN_ORIENTATION::PIN_RIGHT: return SIDE_LEFT;
  200. case PIN_ORIENTATION::PIN_LEFT: return SIDE_RIGHT;
  201. case PIN_ORIENTATION::PIN_UP: return SIDE_BOTTOM;
  202. case PIN_ORIENTATION::PIN_DOWN: return SIDE_TOP;
  203. default:
  204. wxFAIL_MSG( wxS( "Invalid pin orientation" ) );
  205. return SIDE_LEFT;
  206. }
  207. }
  208. /**
  209. * Count the number of pins on a side of the symbol.
  210. */
  211. unsigned pinsOnSide( SIDE aSide )
  212. {
  213. unsigned pin_count = 0;
  214. for( SCH_PIN* each_pin : m_symbol->GetPins() )
  215. {
  216. if( !each_pin->IsVisible() && !m_is_power_symbol )
  217. continue;
  218. if( getPinSide( each_pin ) == aSide )
  219. ++pin_count;
  220. }
  221. return pin_count;
  222. }
  223. /**
  224. * Populate a list of all drawing items that *may* collide with the fields. That is, all
  225. * drawing items, including other fields, that are not the current symbol or its own fields.
  226. */
  227. void getPossibleCollisions( std::vector<SCH_ITEM*>& aItems )
  228. {
  229. wxCHECK_RET( m_screen, wxS( "getPossibleCollisions() with null m_screen" ) );
  230. BOX2I symbolBox = m_symbol->GetBodyAndPinsBoundingBox();
  231. std::vector<SIDE_AND_NPINS> sides = getPreferredSides();
  232. for( SIDE_AND_NPINS& side : sides )
  233. {
  234. BOX2I box( fieldBoxPlacement( side ), m_fbox_size );
  235. box.Merge( symbolBox );
  236. for( SCH_ITEM* item : m_screen->Items().Overlapping( box ) )
  237. {
  238. if( SCH_SYMBOL* candidate = dynamic_cast<SCH_SYMBOL*>( item ) )
  239. {
  240. if( candidate == m_symbol )
  241. continue;
  242. std::vector<SCH_FIELD*> fields;
  243. candidate->GetFields( fields, /* aVisibleOnly */ true );
  244. for( SCH_FIELD* field : fields )
  245. aItems.push_back( field );
  246. }
  247. aItems.push_back( item );
  248. }
  249. }
  250. }
  251. /**
  252. * Filter a list of possible colliders to include only those that actually collide
  253. * with a given rectangle. Returns the new vector.
  254. */
  255. std::vector<SCH_ITEM*> filterCollisions( const BOX2I& aRect )
  256. {
  257. std::vector<SCH_ITEM*> filtered;
  258. for( SCH_ITEM* item : m_colliders )
  259. {
  260. BOX2I item_box;
  261. if( SCH_SYMBOL* item_comp = dynamic_cast<SCH_SYMBOL*>( item ) )
  262. item_box = item_comp->GetBodyAndPinsBoundingBox();
  263. else
  264. item_box = item->GetBoundingBox();
  265. if( item_box.Intersects( aRect ) )
  266. filtered.push_back( item );
  267. }
  268. return filtered;
  269. }
  270. /**
  271. * Return a list with the preferred field sides for the symbol, in decreasing order of
  272. * preference.
  273. */
  274. std::vector<SIDE_AND_NPINS> getPreferredSides()
  275. {
  276. SIDE_AND_NPINS sides_init[] = {
  277. { SIDE_RIGHT, pinsOnSide( SIDE_RIGHT ) },
  278. { SIDE_TOP, pinsOnSide( SIDE_TOP ) },
  279. { SIDE_LEFT, pinsOnSide( SIDE_LEFT ) },
  280. { SIDE_BOTTOM, pinsOnSide( SIDE_BOTTOM ) },
  281. };
  282. std::vector<SIDE_AND_NPINS> sides( sides_init, sides_init + arrayDim( sides_init ) );
  283. int orient = m_symbol->GetOrientation();
  284. int orient_angle = orient & 0xff; // enum is a bitmask
  285. bool h_mirrored = ( ( orient & SYM_MIRROR_X )
  286. && ( orient_angle == SYM_ORIENT_0 || orient_angle == SYM_ORIENT_180 ) );
  287. double w = double( m_symbol_bbox.GetWidth() );
  288. double h = double( m_symbol_bbox.GetHeight() );
  289. // The preferred-sides heuristics are a bit magical. These were determined mostly
  290. // by trial and error.
  291. if( m_is_power_symbol )
  292. {
  293. // For power symbols, we generally want the label at the top first.
  294. switch( orient_angle )
  295. {
  296. case SYM_ORIENT_0:
  297. std::swap( sides[0], sides[1] );
  298. std::swap( sides[1], sides[3] );
  299. // TOP, BOTTOM, RIGHT, LEFT
  300. break;
  301. case SYM_ORIENT_90:
  302. std::swap( sides[0], sides[2] );
  303. std::swap( sides[1], sides[2] );
  304. // LEFT, RIGHT, TOP, BOTTOM
  305. break;
  306. case SYM_ORIENT_180:
  307. std::swap( sides[0], sides[3] );
  308. // BOTTOM, TOP, LEFT, RIGHT
  309. break;
  310. case SYM_ORIENT_270:
  311. std::swap( sides[1], sides[2] );
  312. // RIGHT, LEFT, TOP, BOTTOM
  313. break;
  314. }
  315. }
  316. else
  317. {
  318. // If the symbol is horizontally mirrored, swap left and right
  319. if( h_mirrored )
  320. {
  321. std::swap( sides[0], sides[2] );
  322. }
  323. // If the symbol is very long or is a power symbol, swap H and V
  324. if( w/h > 3.0 )
  325. {
  326. std::swap( sides[0], sides[1] );
  327. std::swap( sides[1], sides[3] );
  328. }
  329. }
  330. return sides;
  331. }
  332. /**
  333. * Return a list of the sides where a field set would collide with another item.
  334. */
  335. std::vector<SIDE_AND_COLL> getCollidingSides()
  336. {
  337. SIDE sides_init[] = { SIDE_RIGHT, SIDE_TOP, SIDE_LEFT, SIDE_BOTTOM };
  338. std::vector<SIDE> sides( sides_init, sides_init + arrayDim( sides_init ) );
  339. std::vector<SIDE_AND_COLL> colliding;
  340. // Iterate over all sides and find the ones that collide
  341. for( SIDE side : sides )
  342. {
  343. SIDE_AND_NPINS sideandpins;
  344. sideandpins.side = side;
  345. sideandpins.pins = pinsOnSide( side );
  346. BOX2I box( fieldBoxPlacement( sideandpins ), m_fbox_size );
  347. COLLISION collision = COLLIDE_NONE;
  348. for( SCH_ITEM* collider : filterCollisions( box ) )
  349. {
  350. SCH_LINE* line = dynamic_cast<SCH_LINE*>( collider );
  351. if( line && !side.x )
  352. {
  353. VECTOR2I start = line->GetStartPoint(), end = line->GetEndPoint();
  354. if( start.y == end.y && collision != COLLIDE_OBJECTS )
  355. collision = COLLIDE_H_WIRES;
  356. else
  357. collision = COLLIDE_OBJECTS;
  358. }
  359. else
  360. {
  361. collision = COLLIDE_OBJECTS;
  362. }
  363. }
  364. if( collision != COLLIDE_NONE )
  365. colliding.push_back( { side, collision } );
  366. }
  367. return colliding;
  368. }
  369. /**
  370. * Choose a side for the fields, filtered on only one side collision type.
  371. * Removes the sides matching the filter from the list.
  372. */
  373. SIDE_AND_NPINS chooseSideFiltered( std::vector<SIDE_AND_NPINS>& aSides,
  374. const std::vector<SIDE_AND_COLL>& aCollidingSides,
  375. COLLISION aCollision,
  376. SIDE_AND_NPINS aLastSelection)
  377. {
  378. SIDE_AND_NPINS sel = aLastSelection;
  379. std::vector<SIDE_AND_NPINS>::iterator it = aSides.begin();
  380. while( it != aSides.end() )
  381. {
  382. bool collide = false;
  383. for( SIDE_AND_COLL collision : aCollidingSides )
  384. {
  385. if( collision.side == it->side && collision.collision == aCollision )
  386. collide = true;
  387. }
  388. if( !collide )
  389. {
  390. ++it;
  391. }
  392. else
  393. {
  394. if( it->pins <= sel.pins )
  395. {
  396. sel.pins = it->pins;
  397. sel.side = it->side;
  398. }
  399. it = aSides.erase( it );
  400. }
  401. }
  402. return sel;
  403. }
  404. /**
  405. * Look where a symbol's pins are to pick a side to put the fields on
  406. * @param aAvoidCollisions - if true, pick last the sides where the label will collide
  407. * with other items.
  408. */
  409. SIDE_AND_NPINS chooseSideForFields( bool aAvoidCollisions )
  410. {
  411. std::vector<SIDE_AND_NPINS> sides = getPreferredSides();
  412. std::reverse( sides.begin(), sides.end() );
  413. SIDE_AND_NPINS side = { VECTOR2I( 1, 0 ), UINT_MAX };
  414. if( aAvoidCollisions )
  415. {
  416. std::vector<SIDE_AND_COLL> colliding_sides = getCollidingSides();
  417. side = chooseSideFiltered( sides, colliding_sides, COLLIDE_OBJECTS, side );
  418. side = chooseSideFiltered( sides, colliding_sides, COLLIDE_H_WIRES, side );
  419. }
  420. for( SIDE_AND_NPINS& each_side : sides | boost::adaptors::reversed )
  421. {
  422. if( !each_side.pins ) return each_side;
  423. }
  424. for( SIDE_AND_NPINS& each_side : sides )
  425. {
  426. if( each_side.pins <= side.pins )
  427. {
  428. side.pins = each_side.pins;
  429. side.side = each_side.side;
  430. }
  431. }
  432. return side;
  433. }
  434. /**
  435. * Set the justification of a field based on the side it's supposed to be on, taking
  436. * into account whether the field will be displayed with flipped justification due to
  437. * mirroring.
  438. */
  439. void justifyField( SCH_FIELD* aField, SIDE aFieldSide )
  440. {
  441. // Justification is set twice to allow IsHorizJustifyFlipped() to work correctly.
  442. aField->SetHorizJustify( TO_HJUSTIFY( -aFieldSide.x ) );
  443. aField->SetHorizJustify( TO_HJUSTIFY( -aFieldSide.x
  444. * ( aField->IsHorizJustifyFlipped() ? -1 : 1 ) ) );
  445. aField->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  446. }
  447. /**
  448. * Return the position of the field bounding box.
  449. */
  450. VECTOR2I fieldBoxPlacement( SIDE_AND_NPINS aFieldSideAndPins )
  451. {
  452. VECTOR2I fbox_center = m_symbol_bbox.Centre();
  453. int offs_x = ( m_symbol_bbox.GetWidth() + m_fbox_size.x ) / 2;
  454. int offs_y = ( m_symbol_bbox.GetHeight() + m_fbox_size.y ) / 2;
  455. if( aFieldSideAndPins.side.x != 0 )
  456. offs_x += HPADDING;
  457. else if( aFieldSideAndPins.side.y != 0 )
  458. offs_y += VPADDING;
  459. fbox_center.x += aFieldSideAndPins.side.x * offs_x;
  460. fbox_center.y += aFieldSideAndPins.side.y * offs_y;
  461. int x = fbox_center.x - ( m_fbox_size.x / 2 );
  462. int y = fbox_center.y - ( m_fbox_size.y / 2 );
  463. auto getPinsBox =
  464. [&]( const VECTOR2I& aSide )
  465. {
  466. BOX2I pinsBox;
  467. for( SCH_PIN* each_pin : m_symbol->GetPins() )
  468. {
  469. if( !each_pin->IsVisible() && !m_is_power_symbol )
  470. continue;
  471. if( getPinSide( each_pin ) == aSide )
  472. pinsBox.Merge( each_pin->GetBoundingBox() );
  473. }
  474. return pinsBox;
  475. };
  476. if( aFieldSideAndPins.pins > 0 )
  477. {
  478. BOX2I pinsBox = getPinsBox( aFieldSideAndPins.side );
  479. if( aFieldSideAndPins.side == SIDE_TOP || aFieldSideAndPins.side == SIDE_BOTTOM )
  480. {
  481. x = pinsBox.GetRight() + ( HPADDING * 2 );
  482. }
  483. else if( aFieldSideAndPins.side == SIDE_RIGHT || aFieldSideAndPins.side == SIDE_LEFT )
  484. {
  485. y = pinsBox.GetTop() - ( m_fbox_size.y + ( VPADDING * 2 ) );
  486. }
  487. }
  488. return VECTOR2I( x, y );
  489. }
  490. /**
  491. * Shift a field box up or down a bit to make the fields fit between some wires.
  492. * Returns true if a shift was made.
  493. */
  494. bool fitFieldsBetweenWires( BOX2I* aBox, SIDE aSide )
  495. {
  496. if( aSide != SIDE_TOP && aSide != SIDE_BOTTOM )
  497. return false;
  498. std::vector<SCH_ITEM*> colliders = filterCollisions( *aBox );
  499. if( colliders.empty() )
  500. return false;
  501. // Find the offset of the wires for proper positioning
  502. int offset = 0;
  503. for( SCH_ITEM* item : colliders )
  504. {
  505. SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
  506. if( !line )
  507. return false;
  508. VECTOR2I start = line->GetStartPoint(), end = line->GetEndPoint();
  509. if( start.y != end.y )
  510. return false;
  511. int this_offset = (3 * WIRE_V_SPACING / 2) - ( start.y % WIRE_V_SPACING );
  512. if( offset == 0 )
  513. offset = this_offset;
  514. else if( offset != this_offset )
  515. return false;
  516. }
  517. // At this point we are recomputing the field box size. Do not
  518. // return false after this point.
  519. m_fbox_size = computeFBoxSize( /* aDynamic */ false );
  520. VECTOR2I pos = aBox->GetPosition();
  521. pos.y = round_n( pos.y, WIRE_V_SPACING, aSide == SIDE_BOTTOM );
  522. aBox->SetOrigin( pos );
  523. return true;
  524. }
  525. /**
  526. * Place a field horizontally, taking into account the field width and justification.
  527. *
  528. * @param aField - the field to place.
  529. * @param aFieldBox - box in which fields will be placed
  530. *
  531. * @return Correct field horizontal position
  532. */
  533. int fieldHPlacement( SCH_FIELD* aField, const BOX2I& aFieldBox )
  534. {
  535. int field_hjust;
  536. int field_xcoord;
  537. if( aField->IsHorizJustifyFlipped() )
  538. field_hjust = -aField->GetHorizJustify();
  539. else
  540. field_hjust = aField->GetHorizJustify();
  541. switch( field_hjust )
  542. {
  543. case GR_TEXT_H_ALIGN_LEFT:
  544. field_xcoord = aFieldBox.GetLeft();
  545. break;
  546. case GR_TEXT_H_ALIGN_CENTER:
  547. field_xcoord = aFieldBox.Centre().x;
  548. break;
  549. case GR_TEXT_H_ALIGN_RIGHT:
  550. field_xcoord = aFieldBox.GetRight();
  551. break;
  552. default:
  553. wxFAIL_MSG( wxS( "Unexpected value for SCH_FIELD::GetHorizJustify()" ) );
  554. field_xcoord = aFieldBox.Centre().x; // Most are centered
  555. }
  556. return field_xcoord;
  557. }
  558. /**
  559. * Place a field vertically. Because field vertical placements accumulate,
  560. * this takes a pointer to a vertical position accumulator.
  561. *
  562. * @param aField - the field to place.
  563. * @param aFieldBox - box in which fields will be placed.
  564. * @param aAccumulatedPosition - pointer to a position accumulator
  565. * @param aDynamic - use dynamic spacing
  566. *
  567. * @return Correct field vertical position
  568. */
  569. int fieldVPlacement( SCH_FIELD* aField, const BOX2I& aFieldBox, int* aAccumulatedPosition,
  570. bool aDynamic )
  571. {
  572. int field_height;
  573. int padding;
  574. if( !aDynamic )
  575. {
  576. field_height = WIRE_V_SPACING / 2;
  577. padding = WIRE_V_SPACING / 2;
  578. }
  579. else if( m_align_to_grid )
  580. {
  581. field_height = aField->GetBoundingBox().GetHeight();
  582. padding = round_n( field_height, schIUScale.MilsToIU( 50 ), true ) - field_height;
  583. }
  584. else
  585. {
  586. field_height = aField->GetBoundingBox().GetHeight();
  587. padding = FIELD_PADDING;
  588. }
  589. int placement = *aAccumulatedPosition + padding / 2 + field_height / 2;
  590. *aAccumulatedPosition += padding + field_height;
  591. return placement;
  592. }
  593. private:
  594. SCH_SCREEN* m_screen;
  595. SCH_SYMBOL* m_symbol;
  596. std::vector<SCH_FIELD*> m_fields;
  597. std::vector<SCH_ITEM*> m_colliders;
  598. BOX2I m_symbol_bbox;
  599. VECTOR2I m_fbox_size;
  600. bool m_allow_rejustify;
  601. bool m_align_to_grid;
  602. bool m_is_power_symbol;
  603. };
  604. const AUTOPLACER::SIDE AUTOPLACER::SIDE_TOP( 0, -1 );
  605. const AUTOPLACER::SIDE AUTOPLACER::SIDE_BOTTOM( 0, 1 );
  606. const AUTOPLACER::SIDE AUTOPLACER::SIDE_LEFT( -1, 0 );
  607. const AUTOPLACER::SIDE AUTOPLACER::SIDE_RIGHT( 1, 0 );
  608. void SCH_SYMBOL::AutoplaceFields( SCH_SCREEN* aScreen, bool aManual )
  609. {
  610. if( aManual )
  611. wxASSERT_MSG( aScreen, wxS( "A SCH_SCREEN pointer must be given for manual autoplacement" ) );
  612. AUTOPLACER autoplacer( this, aScreen );
  613. autoplacer.DoAutoplace( aManual );
  614. m_fieldsAutoplaced = ( aManual ? FIELDS_AUTOPLACED_MANUAL : FIELDS_AUTOPLACED_AUTO );
  615. }