KiCad PCB EDA Suite
autoplace_fields.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015 Chris Pavlina <[email protected]>
5  * Copyright (C) 2015, 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 /******************************************************************************
26  * Field autoplacer: Tries to find an optimal place for symbol fields, and places them there.
27  * There are two modes: "auto"-autoplace, and "manual" autoplace.
28  * Auto mode is for when the process is run automatically, like when rotating parts, and it
29  * avoids doing things that would be helpful for the final positioning but annoying if they
30  * happened without permission.
31  * Short description of the process:
32  *
33  * 1. Compute the dimensions of the fields' bounding box ::computeFBoxSize
34  * 2. Determine which side the fields will go on. ::chooseSideForFields
35  * 1. Sort the four sides in preference order,
36  * depending on the symbol's shape and
37  * orientation ::getPreferredSides
38  * 2. If in manual mode, sift out the sides that would
39  * cause fields to overlap other items ::getCollidingSides
40  * 3. If any remaining sides have zero pins there,
41  * choose the highest zero-pin side according to
42  * preference order.
43  * 4. If all sides have pins, choose the side with the
44  * fewest pins.
45  * 3. Compute the position of the fields' bounding box ::fieldBoxPlacement
46  * 4. In manual mode, shift the box vertically if possible
47  * to fit fields between adjacent wires ::fitFieldsBetweenWires
48  * 5. Move all fields to their final positions
49  * 1. Re-justify fields if options allow that ::justifyField
50  * 2. Round to a 50-mil grid coordinate if desired
51  */
52 
53 #include <boost/range/adaptor/reversed.hpp>
54 
55 #include <sch_edit_frame.h>
56 #include <hotkeys_basic.h>
57 #include <sch_symbol.h>
58 #include <sch_line.h>
59 #include <lib_pin.h>
60 #include <kiface_base.h>
61 #include <vector>
62 #include <algorithm>
63 #include <tool/tool_manager.h>
65 #include <eeschema_settings.h>
66 #include <core/arraydim.h>
67 
68 #define FIELD_PADDING Mils2iu( 10 ) // arbitrarily chosen for aesthetics
69 #define WIRE_V_SPACING Mils2iu( 100 )
70 #define HPADDING Mils2iu( 25 )
71 #define VPADDING Mils2iu( 25 )
72 
76 template<typename T> T round_n( const T& value, const T& n, bool aRoundUp )
77 {
78  if( value % n )
79  return n * (value / n + (aRoundUp ? 1 : 0));
80  else
81  return value;
82 }
83 
84 
89 {
90  return static_cast<EDA_TEXT_HJUSTIFY_T>( x );
91 }
92 
93 
95 {
96 public:
97  typedef wxPoint SIDE;
100 
102  {
104  unsigned pins;
105  };
106 
108  {
111  };
112 
113  AUTOPLACER( SCH_SYMBOL* aSymbol, SCH_SCREEN* aScreen ) :
114  m_screen( aScreen ),
115  m_symbol( aSymbol )
116  {
117  m_symbol->GetFields( m_fields, /* aVisibleOnly */ true );
118 
119  auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
120  wxASSERT( cfg );
121 
122  m_allow_rejustify = false;
123  m_align_to_grid = true;
124 
125  if( cfg )
126  {
127  m_allow_rejustify = cfg->m_AutoplaceFields.allow_rejustify;
128  m_align_to_grid = cfg->m_AutoplaceFields.align_to_grid;
129  }
130 
132  m_fbox_size = computeFBoxSize( /* aDynamic */ true );
133 
135 
136  if( aScreen )
138  }
139 
145  void DoAutoplace( bool aManual )
146  {
147  bool forceWireSpacing = false;
148  SIDE_AND_NPINS sideandpins = chooseSideForFields( aManual );
149  SIDE field_side = sideandpins.side;
150  wxPoint fbox_pos = fieldBoxPlacement( sideandpins );
151  EDA_RECT field_box( fbox_pos, m_fbox_size );
152 
153  if( aManual )
154  forceWireSpacing = fitFieldsBetweenWires( &field_box, field_side );
155 
156  // Move the fields
157  int last_y_coord = field_box.GetTop();
158 
159  for( unsigned field_idx = 0; field_idx < m_fields.size(); ++field_idx )
160  {
161  SCH_FIELD* field = m_fields[field_idx];
162 
163  if( !field->IsVisible() )
164  continue;
165 
166  if( m_allow_rejustify )
167  {
168  if( sideandpins.pins > 0 )
169  {
170  if( field_side == SIDE_TOP || field_side == SIDE_BOTTOM )
171  justifyField( field, SIDE_RIGHT );
172  else
173  justifyField( field, SIDE_TOP );
174  }
175  else
176  {
177  justifyField( field, field_side );
178  }
179  }
180 
181  wxPoint pos( fieldHPlacement( field, field_box ),
182  fieldVPlacement( field, field_box, &last_y_coord, !forceWireSpacing ) );
183 
184  if( m_align_to_grid )
185  {
186  if( abs( field_side.x ) > 0 )
187  pos.x = round_n( pos.x, Mils2iu( 50 ), field_side.x >= 0 );
188 
189  if( abs( field_side.y ) > 0 )
190  pos.y = round_n( pos.y, Mils2iu( 50 ), field_side.y >= 0 );
191  }
192 
193  field->SetPosition( pos );
194  }
195  }
196 
197 protected:
202  wxSize computeFBoxSize( bool aDynamic )
203  {
204  int max_field_width = 0;
205  int total_height = 0;
206 
207  std::vector<SCH_FIELD*> visibleFields;
208 
209  for( SCH_FIELD* field : m_fields )
210  {
211  if( field->IsVisible() )
212  visibleFields.push_back( field );
213  }
214 
215  for( SCH_FIELD* field : visibleFields )
216  {
217  if( m_symbol->GetTransform().y1 )
218  field->SetTextAngle( TEXT_ANGLE_VERT );
219  else
220  field->SetTextAngle( TEXT_ANGLE_HORIZ );
221 
222  EDA_RECT bbox = field->GetBoundingBox();
223  int field_width = bbox.GetWidth();
224  int field_height = bbox.GetHeight();
225 
226  max_field_width = std::max( max_field_width, field_width );
227 
228  // Remove interline spacing from field_height for last line.
229  if( field == visibleFields.back() )
230  field_height *= 0.62;
231 
232  if( !aDynamic )
233  total_height += WIRE_V_SPACING;
234  else if( m_align_to_grid )
235  total_height += round_n( field_height, Mils2iu( 50 ), true );
236  else
237  total_height += field_height + FIELD_PADDING;
238  }
239 
240  return wxSize( max_field_width, total_height );
241  }
242 
247  {
248  int pin_orient = aPin->GetLibPin()->PinDrawOrient( m_symbol->GetTransform() );
249 
250  switch( pin_orient )
251  {
252  case PIN_RIGHT: return SIDE_LEFT;
253  case PIN_LEFT: return SIDE_RIGHT;
254  case PIN_UP: return SIDE_BOTTOM;
255  case PIN_DOWN: return SIDE_TOP;
256  default:
257  wxFAIL_MSG( wxT( "Invalid pin orientation" ) );
258  return SIDE_LEFT;
259  }
260  }
261 
265  unsigned pinsOnSide( SIDE aSide )
266  {
267  unsigned pin_count = 0;
268 
269  for( SCH_PIN* each_pin : m_symbol->GetPins() )
270  {
271  if( !each_pin->IsVisible() && !m_is_power_symbol )
272  continue;
273 
274  if( getPinSide( each_pin ) == aSide )
275  ++pin_count;
276  }
277 
278  return pin_count;
279  }
280 
285  void getPossibleCollisions( std::vector<SCH_ITEM*>& aItems )
286  {
287  wxCHECK_RET( m_screen, wxT( "getPossibleCollisions() with null m_screen" ) );
288 
290  std::vector<SIDE_AND_NPINS> sides = getPreferredSides();
291 
292  for( SIDE_AND_NPINS& side : sides )
293  {
294  EDA_RECT box( fieldBoxPlacement( side ), m_fbox_size );
295  box.Merge( symbolBox );
296 
297  for( SCH_ITEM* item : m_screen->Items().Overlapping( box ) )
298  {
299  if( SCH_SYMBOL* candidate = dynamic_cast<SCH_SYMBOL*>( item ) )
300  {
301  if( candidate == m_symbol )
302  continue;
303 
304  std::vector<SCH_FIELD*> fields;
305  candidate->GetFields( fields, /* aVisibleOnly */ true );
306 
307  for( SCH_FIELD* field : fields )
308  aItems.push_back( field );
309  }
310 
311  aItems.push_back( item );
312  }
313  }
314  }
315 
320  std::vector<SCH_ITEM*> filterCollisions( const EDA_RECT& aRect )
321  {
322  std::vector<SCH_ITEM*> filtered;
323 
324  for( SCH_ITEM* item : m_colliders )
325  {
326  EDA_RECT item_box;
327 
328  if( SCH_SYMBOL* item_comp = dynamic_cast<SCH_SYMBOL*>( item ) )
329  item_box = item_comp->GetBodyAndPinsBoundingBox();
330  else
331  item_box = item->GetBoundingBox();
332 
333  if( item_box.Intersects( aRect ) )
334  filtered.push_back( item );
335  }
336 
337  return filtered;
338  }
339 
344  std::vector<SIDE_AND_NPINS> getPreferredSides()
345  {
346  SIDE_AND_NPINS sides_init[] = {
348  { SIDE_TOP, pinsOnSide( SIDE_TOP ) },
351  };
352  std::vector<SIDE_AND_NPINS> sides( sides_init, sides_init + arrayDim( sides_init ) );
353 
354  int orient = m_symbol->GetOrientation();
355  int orient_angle = orient & 0xff; // enum is a bitmask
356  bool h_mirrored = ( ( orient & SYM_MIRROR_X )
357  && ( orient_angle == SYM_ORIENT_0 || orient_angle == SYM_ORIENT_180 ) );
358  double w = double( m_symbol_bbox.GetWidth() );
359  double h = double( m_symbol_bbox.GetHeight() );
360 
361  // The preferred-sides heuristics are a bit magical. These were determined mostly
362  // by trial and error.
363 
364  if( m_is_power_symbol )
365  {
366  // For power symbols, we generally want the label at the top first.
367  switch( orient_angle )
368  {
369  case SYM_ORIENT_0:
370  std::swap( sides[0], sides[1] );
371  std::swap( sides[1], sides[3] );
372  // TOP, BOTTOM, RIGHT, LEFT
373  break;
374  case SYM_ORIENT_90:
375  std::swap( sides[0], sides[2] );
376  std::swap( sides[1], sides[2] );
377  // LEFT, RIGHT, TOP, BOTTOM
378  break;
379  case SYM_ORIENT_180:
380  std::swap( sides[0], sides[3] );
381  // BOTTOM, TOP, LEFT, RIGHT
382  break;
383  case SYM_ORIENT_270:
384  std::swap( sides[1], sides[2] );
385  // RIGHT, LEFT, TOP, BOTTOM
386  break;
387  }
388  }
389  else
390  {
391  // If the symbol is horizontally mirrored, swap left and right
392  if( h_mirrored )
393  {
394  std::swap( sides[0], sides[2] );
395  }
396 
397  // If the symbol is very long or is a power symbol, swap H and V
398  if( w/h > 3.0 )
399  {
400  std::swap( sides[0], sides[1] );
401  std::swap( sides[1], sides[3] );
402  }
403  }
404 
405  return sides;
406  }
407 
411  std::vector<SIDE_AND_COLL> getCollidingSides()
412  {
413  SIDE sides_init[] = { SIDE_RIGHT, SIDE_TOP, SIDE_LEFT, SIDE_BOTTOM };
414  std::vector<SIDE> sides( sides_init, sides_init + arrayDim( sides_init ) );
415  std::vector<SIDE_AND_COLL> colliding;
416 
417  // Iterate over all sides and find the ones that collide
418  for( SIDE side : sides )
419  {
420  SIDE_AND_NPINS sideandpins;
421  sideandpins.side = side;
422  sideandpins.pins = pinsOnSide( side );
423 
424  EDA_RECT box( fieldBoxPlacement( sideandpins ), m_fbox_size );
425 
426  COLLISION collision = COLLIDE_NONE;
427 
428  for( SCH_ITEM* collider : filterCollisions( box ) )
429  {
430  SCH_LINE* line = dynamic_cast<SCH_LINE*>( collider );
431 
432  if( line && !side.x )
433  {
434  wxPoint start = line->GetStartPoint(), end = line->GetEndPoint();
435 
436  if( start.y == end.y && collision != COLLIDE_OBJECTS )
437  collision = COLLIDE_H_WIRES;
438  else
439  collision = COLLIDE_OBJECTS;
440  }
441  else
442  {
443  collision = COLLIDE_OBJECTS;
444  }
445  }
446 
447  if( collision != COLLIDE_NONE )
448  colliding.push_back( { side, collision } );
449  }
450 
451  return colliding;
452  }
453 
458  SIDE_AND_NPINS chooseSideFiltered( std::vector<SIDE_AND_NPINS>& aSides,
459  const std::vector<SIDE_AND_COLL>& aCollidingSides,
460  COLLISION aCollision,
461  SIDE_AND_NPINS aLastSelection)
462  {
463  SIDE_AND_NPINS sel = aLastSelection;
464 
465  std::vector<SIDE_AND_NPINS>::iterator it = aSides.begin();
466 
467  while( it != aSides.end() )
468  {
469  bool collide = false;
470 
471  for( SIDE_AND_COLL collision : aCollidingSides )
472  {
473  if( collision.side == it->side && collision.collision == aCollision )
474  collide = true;
475  }
476 
477  if( !collide )
478  {
479  ++it;
480  }
481  else
482  {
483  if( it->pins <= sel.pins )
484  {
485  sel.pins = it->pins;
486  sel.side = it->side;
487  }
488 
489  it = aSides.erase( it );
490  }
491  }
492 
493  return sel;
494  }
495 
501  SIDE_AND_NPINS chooseSideForFields( bool aAvoidCollisions )
502  {
503  std::vector<SIDE_AND_NPINS> sides = getPreferredSides();
504 
505  std::reverse( sides.begin(), sides.end() );
506  SIDE_AND_NPINS side = { wxPoint( 1, 0 ), UINT_MAX };
507 
508  if( aAvoidCollisions )
509  {
510  std::vector<SIDE_AND_COLL> colliding_sides = getCollidingSides();
511  side = chooseSideFiltered( sides, colliding_sides, COLLIDE_OBJECTS, side );
512  side = chooseSideFiltered( sides, colliding_sides, COLLIDE_H_WIRES, side );
513  }
514 
515  for( SIDE_AND_NPINS& each_side : sides | boost::adaptors::reversed )
516  {
517  if( !each_side.pins ) return each_side;
518  }
519 
520  for( SIDE_AND_NPINS& each_side : sides )
521  {
522  if( each_side.pins <= side.pins )
523  {
524  side.pins = each_side.pins;
525  side.side = each_side.side;
526  }
527  }
528 
529  return side;
530  }
531 
537  void justifyField( SCH_FIELD* aField, SIDE aFieldSide )
538  {
539  // Justification is set twice to allow IsHorizJustifyFlipped() to work correctly.
540  aField->SetHorizJustify( TO_HJUSTIFY( -aFieldSide.x ) );
541  aField->SetHorizJustify( TO_HJUSTIFY( -aFieldSide.x
542  * ( aField->IsHorizJustifyFlipped() ? -1 : 1 ) ) );
544  }
545 
549  wxPoint fieldBoxPlacement( SIDE_AND_NPINS aFieldSideAndPins )
550  {
551  wxPoint fbox_center = m_symbol_bbox.Centre();
552  int offs_x = ( m_symbol_bbox.GetWidth() + m_fbox_size.GetWidth() ) / 2;
553  int offs_y = ( m_symbol_bbox.GetHeight() + m_fbox_size.GetHeight() ) / 2;
554 
555  if( aFieldSideAndPins.side.x != 0 )
556  offs_x += HPADDING;
557  else if( aFieldSideAndPins.side.y != 0 )
558  offs_y += VPADDING;
559 
560  fbox_center.x += aFieldSideAndPins.side.x * offs_x;
561  fbox_center.y += aFieldSideAndPins.side.y * offs_y;
562 
563  int x = fbox_center.x - ( m_fbox_size.GetWidth() / 2 );
564  int y = fbox_center.y - ( m_fbox_size.GetHeight() / 2 );
565 
566  auto getPinsBox =
567  [&]( const wxPoint& aSide )
568  {
569  EDA_RECT pinsBox;
570 
571  for( SCH_PIN* each_pin : m_symbol->GetPins() )
572  {
573  if( !each_pin->IsVisible() && !m_is_power_symbol )
574  continue;
575 
576  if( getPinSide( each_pin ) == aSide )
577  pinsBox.Merge( each_pin->GetBoundingBox() );
578  }
579 
580  return pinsBox;
581  };
582 
583  if( aFieldSideAndPins.pins > 0 )
584  {
585  EDA_RECT pinsBox = getPinsBox( aFieldSideAndPins.side );
586 
587  if( aFieldSideAndPins.side == SIDE_TOP || aFieldSideAndPins.side == SIDE_BOTTOM )
588  {
589  x = pinsBox.GetRight() + ( HPADDING * 2 );
590  }
591  else if( aFieldSideAndPins.side == SIDE_RIGHT || aFieldSideAndPins.side == SIDE_LEFT )
592  {
593  y = pinsBox.GetTop() - ( m_fbox_size.GetHeight() + ( VPADDING * 2 ) );
594  }
595  }
596 
597  return wxPoint( x, y );
598  }
599 
604  bool fitFieldsBetweenWires( EDA_RECT* aBox, SIDE aSide )
605  {
606  if( aSide != SIDE_TOP && aSide != SIDE_BOTTOM )
607  return false;
608 
609  std::vector<SCH_ITEM*> colliders = filterCollisions( *aBox );
610 
611  if( colliders.empty() )
612  return false;
613 
614  // Find the offset of the wires for proper positioning
615  int offset = 0;
616 
617  for( SCH_ITEM* item : colliders )
618  {
619  SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
620 
621  if( !line )
622  return false;
623 
624  wxPoint start = line->GetStartPoint(), end = line->GetEndPoint();
625 
626  if( start.y != end.y )
627  return false;
628 
629  int this_offset = (3 * WIRE_V_SPACING / 2) - ( start.y % WIRE_V_SPACING );
630 
631  if( offset == 0 )
632  offset = this_offset;
633  else if( offset != this_offset )
634  return false;
635  }
636 
637  // At this point we are recomputing the field box size. Do not
638  // return false after this point.
639  m_fbox_size = computeFBoxSize( /* aDynamic */ false );
640 
641  wxPoint pos = aBox->GetPosition();
642 
643  pos.y = round_n( pos.y, WIRE_V_SPACING, aSide == SIDE_BOTTOM );
644 
645  aBox->SetOrigin( pos );
646  return true;
647  }
648 
657  int fieldHPlacement( SCH_FIELD *aField, const EDA_RECT &aFieldBox )
658  {
659  int field_hjust;
660  int field_xcoord;
661 
662  if( aField->IsHorizJustifyFlipped() )
663  field_hjust = -aField->GetHorizJustify();
664  else
665  field_hjust = aField->GetHorizJustify();
666 
667  switch( field_hjust )
668  {
670  field_xcoord = aFieldBox.GetLeft();
671  break;
673  field_xcoord = aFieldBox.Centre().x;
674  break;
676  field_xcoord = aFieldBox.GetRight();
677  break;
678  default:
679  wxFAIL_MSG( wxT( "Unexpected value for SCH_FIELD::GetHorizJustify()" ) );
680  field_xcoord = aFieldBox.Centre().x; // Most are centered
681  }
682 
683  return field_xcoord;
684  }
685 
697  int fieldVPlacement( SCH_FIELD *aField, const EDA_RECT &aFieldBox, int *aAccumulatedPosition,
698  bool aDynamic )
699  {
700  int field_height;
701  int padding;
702 
703  if( !aDynamic )
704  {
705  field_height = WIRE_V_SPACING / 2;
706  padding = WIRE_V_SPACING / 2;
707  }
708  else if( m_align_to_grid )
709  {
710  field_height = aField->GetBoundingBox().GetHeight();
711  padding = round_n( field_height, Mils2iu( 50 ), true ) - field_height;
712  }
713  else
714  {
715  field_height = aField->GetBoundingBox().GetHeight();
716  padding = FIELD_PADDING;
717  }
718 
719  int placement = *aAccumulatedPosition + padding / 2 + field_height / 2;
720 
721  *aAccumulatedPosition += padding + field_height;
722 
723  return placement;
724  }
725 
726 private:
729  std::vector<SCH_FIELD*> m_fields;
730  std::vector<SCH_ITEM*> m_colliders;
732  wxSize m_fbox_size;
736 };
737 
738 
743 
744 
745 void SCH_SYMBOL::AutoplaceFields( SCH_SCREEN* aScreen, bool aManual )
746 {
747  if( aManual )
748  wxASSERT_MSG( aScreen, wxT( "A SCH_SCREEN pointer must be given for manual autoplacement" ) );
749 
750  AUTOPLACER autoplacer( this, aScreen );
751  autoplacer.DoAutoplace( aManual );
753 }
#define TEXT_ANGLE_HORIZ
Frequent text rotations, used with {Set,Get}TextAngle(), in 0.1 degrees for now, hoping to migrate to...
Definition: eda_text.h:71
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:49
LIB_PIN * GetLibPin() const
Definition: sch_pin.h:59
std::vector< SCH_FIELD * > m_fields
EDA_TEXT_HJUSTIFY_T
Definition: eda_text.h:82
#define VPADDING
std::vector< SIDE_AND_NPINS > getPreferredSides()
Return a list with the preferred field sides for the symbol, in decreasing order of preference.
void Merge(const EDA_RECT &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: eda_rect.cpp:432
SIDE_AND_NPINS chooseSideForFields(bool aAvoidCollisions)
Look where a symbol's pins are to pick a side to put the fields on.
wxPoint GetStartPoint() const
Definition: sch_line.h:90
bool collide(T aObject, U aAnotherObject, int aMinDistance)
Used by SHAPE_INDEX to implement Query().
Definition: shape_index.h:114
EDA_RECT GetBodyAndPinsBoundingBox() const
Return a bounding box for the symbol body and pins but not the fields.
int GetTop() const
Definition: eda_rect.h:122
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
bool fitFieldsBetweenWires(EDA_RECT *aBox, SIDE aSide)
Shift a field box up or down a bit to make the fields fit between some wires.
int GetLeft() const
Definition: eda_rect.h:121
#define HPADDING
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:741
int GetWidth() const
Definition: eda_rect.h:118
void SetOrigin(const wxPoint &pos)
Definition: eda_rect.h:130
EDA_RECT m_symbol_bbox
Definition: lib_pin.h:48
int PinDrawOrient(const TRANSFORM &aTransform) const
Return the pin real orientation (PIN_UP, PIN_DOWN, PIN_RIGHT, PIN_LEFT), according to its orientation...
Definition: lib_pin.cpp:833
TRANSFORM & GetTransform()
Definition: sch_symbol.h:232
SIDE getPinSide(SCH_PIN *aPin)
Return the side that a pin is on.
#define WIRE_V_SPACING
FIELDS_AUTOPLACED m_fieldsAutoplaced
Definition: sch_item.h:489
T round_n(const T &value, const T &n, bool aRoundUp)
Round up/down to the nearest multiple of n.
std::vector< SCH_ITEM * > filterCollisions(const EDA_RECT &aRect)
Filter a list of possible colliders to include only those that actually collide with a given rectangl...
AUTOPLACER(SCH_SYMBOL *aSymbol, SCH_SCREEN *aScreen)
int y1
Definition: transform.h:49
static const SIDE SIDE_LEFT
bool IsInNetlist() const
SCH_SCREEN * m_screen
EDA_TEXT_HJUSTIFY_T GetHorizJustify() const
Definition: eda_text.h:219
std::vector< SCH_ITEM * > m_colliders
virtual bool IsVisible() const
Definition: eda_text.h:207
void AutoplaceFields(SCH_SCREEN *aScreen, bool aManual) override
Automatically orient all the fields in the symbol.
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:223
const wxPoint GetPosition() const
Definition: eda_rect.h:111
wxSize computeFBoxSize(bool aDynamic)
Compute and return the size of the fields' bounding box.
int GetRight() const
Definition: eda_rect.h:120
EDA_RECT GetBodyBoundingBox() const
Return a bounding box for the symbol body but not the pins or fields.
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
int fieldVPlacement(SCH_FIELD *aField, const EDA_RECT &aFieldBox, int *aAccumulatedPosition, bool aDynamic)
Place a field vertically.
unsigned pinsOnSide(SIDE aSide)
Count the number of pins on a side of the symbol.
#define FIELD_PADDING
EDA_TEXT_HJUSTIFY_T TO_HJUSTIFY(int x)
Convert an integer to a horizontal justification; neg=L zero=C pos=R.
SCH_SYMBOL * m_symbol
int GetHeight() const
Definition: eda_rect.h:119
EE_TYPE Overlapping(const EDA_RECT &aRect) const
Definition: sch_rtree.h:235
static const SIDE SIDE_RIGHT
Schematic symbol object.
Definition: sch_symbol.h:78
Segment description base class to describe items which have 2 end points (track, wire,...
Definition: sch_line.h:37
const EDA_RECT GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: sch_field.cpp:305
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:222
static const SIDE SIDE_BOTTOM
wxPoint fieldBoxPlacement(SIDE_AND_NPINS aFieldSideAndPins)
Return the position of the field bounding box.
EE_RTREE & Items()
Gets the full RTree, usually for iterating.
Definition: sch_screen.h:110
Handle the component boundary box.
Definition: eda_rect.h:42
std::vector< SIDE_AND_COLL > getCollidingSides()
Return a list of the sides where a field set would collide with another item.
wxPoint Centre() const
Definition: eda_rect.h:64
bool IsHorizJustifyFlipped() const
Return whether the field will be rendered with the horizontal justification inverted due to rotation ...
Definition: sch_field.cpp:347
bool Intersects(const EDA_RECT &aRect) const
Test for a common area between rectangles.
Definition: eda_rect.cpp:150
int GetOrientation()
Get the display symbol orientation.
void DoAutoplace(bool aManual)
Do the actual autoplacement.
#define TEXT_ANGLE_VERT
Definition: eda_text.h:72
static const SIDE SIDE_TOP
void SetPosition(const wxPoint &aPosition) override
Definition: sch_field.cpp:822
int fieldHPlacement(SCH_FIELD *aField, const EDA_RECT &aFieldBox)
Place a field horizontally, taking into account the field width and justification.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:182
SIDE_AND_NPINS chooseSideFiltered(std::vector< SIDE_AND_NPINS > &aSides, const std::vector< SIDE_AND_COLL > &aCollidingSides, COLLISION aCollision, SIDE_AND_NPINS aLastSelection)
Choose a side for the fields, filtered on only one side collision type.
std::vector< SCH_PIN * > GetPins(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve a list of the SCH_PINs for the given sheet path.
Definition: sch_symbol.cpp:896
void justifyField(SCH_FIELD *aField, SIDE aFieldSide)
Set the justification of a field based on the side it's supposed to be on, taking into account whethe...
void getPossibleCollisions(std::vector< SCH_ITEM * > &aItems)
Populate a list of all drawing items that may collide with the fields.
wxPoint GetEndPoint() const
Definition: sch_line.h:93