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 <pavlina.chris@gmail.com>
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 <sch_draw_panel.h>
61 #include <kiface_i.h>
62 #include <vector>
63 #include <algorithm>
64 #include <tool/tool_manager.h>
66 #include <eeschema_settings.h>
67 #include <core/arraydim.h>
68 
69 #define FIELD_PADDING Mils2iu( 10 ) // arbitrarily chosen for aesthetics
70 #define WIRE_V_SPACING Mils2iu( 100 )
71 #define HPADDING Mils2iu( 25 )
72 #define VPADDING Mils2iu( 25 )
73 
77 template<typename T> T round_n( const T& value, const T& n, bool aRoundUp )
78 {
79  if( value % n )
80  return n * (value / n + (aRoundUp ? 1 : 0));
81  else
82  return value;
83 }
84 
85 
90 {
91  return static_cast<EDA_TEXT_HJUSTIFY_T>( x );
92 }
93 
94 
96 {
97 public:
98  typedef wxPoint SIDE;
101 
103  {
105  unsigned pins;
106  };
107 
109  {
112  };
113 
114  AUTOPLACER( SCH_COMPONENT* aSymbol, SCH_SCREEN* aScreen ) :
115  m_screen( aScreen ),
116  m_symbol( aSymbol )
117  {
118  m_symbol->GetFields( m_fields, /* aVisibleOnly */ true );
119 
120  auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
121  wxASSERT( cfg );
122 
123  m_allow_rejustify = false;
124  m_align_to_grid = true;
125 
126  if( cfg )
127  {
128  m_allow_rejustify = cfg->m_AutoplaceFields.allow_rejustify;
129  m_align_to_grid = cfg->m_AutoplaceFields.align_to_grid;
130  }
131 
133  m_fbox_size = computeFBoxSize( /* aDynamic */ true );
134 
136 
137  if( aScreen )
139  }
140 
146  void DoAutoplace( bool aManual )
147  {
148  bool force_wire_spacing = false;
149  SIDE field_side = chooseSideForFields( aManual );
150  wxPoint fbox_pos = fieldBoxPlacement( field_side );
151  EDA_RECT field_box( fbox_pos, m_fbox_size );
152 
153  if( aManual )
154  force_wire_spacing = 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( m_allow_rejustify )
164  justifyField( field, field_side );
165 
166  wxPoint pos( fieldHorizPlacement( field, field_box ),
167  fieldVertPlacement( field, field_box, &last_y_coord, !force_wire_spacing ) );
168 
169  if( m_align_to_grid )
170  {
171  if( abs( field_side.x ) > 0 )
172  pos.x = round_n( pos.x, Mils2iu( 50 ), field_side.x >= 0 );
173 
174  if( abs( field_side.y ) > 0 )
175  pos.y = round_n( pos.y, Mils2iu( 50 ), field_side.y >= 0 );
176  }
177 
178  field->SetPosition( pos );
179  }
180  }
181 
182 protected:
187  wxSize computeFBoxSize( bool aDynamic )
188  {
189  int max_field_width = 0;
190  int total_height = 0;
191 
192  for( SCH_FIELD* field : m_fields )
193  {
194  if( m_symbol->GetTransform().y1 )
195  field->SetTextAngle( TEXT_ANGLE_VERT );
196  else
197  field->SetTextAngle( TEXT_ANGLE_HORIZ );
198 
199  EDA_RECT bbox = field->GetBoundingBox();
200  int field_width = bbox.GetWidth();
201  int field_height = bbox.GetHeight();
202 
203  max_field_width = std::max( max_field_width, field_width );
204 
205  // Remove interline spacing from field_height for last line.
206  if( field == m_fields[ m_fields.size() - 1 ] )
207  field_height *= 0.62;
208 
209  if( !aDynamic )
210  total_height += WIRE_V_SPACING;
211  else if( m_align_to_grid )
212  total_height += round_n( field_height, Mils2iu( 50 ), true );
213  else
214  total_height += field_height + FIELD_PADDING;
215  }
216 
217  return wxSize( max_field_width, total_height );
218  }
219 
224  {
225  int pin_orient = aPin->GetLibPin()->PinDrawOrient( m_symbol->GetTransform() );
226 
227  switch( pin_orient )
228  {
229  case PIN_RIGHT: return SIDE_LEFT;
230  case PIN_LEFT: return SIDE_RIGHT;
231  case PIN_UP: return SIDE_BOTTOM;
232  case PIN_DOWN: return SIDE_TOP;
233  default:
234  wxFAIL_MSG( "Invalid pin orientation" );
235  return SIDE_LEFT;
236  }
237  }
238 
242  unsigned pinsOnSide( SIDE aSide )
243  {
244  unsigned pin_count = 0;
245 
246  for( SCH_PIN* each_pin : m_symbol->GetPins() )
247  {
248  if( !each_pin->IsVisible() && !m_is_power_symbol )
249  continue;
250 
251  if( getPinSide( each_pin ) == aSide )
252  ++pin_count;
253  }
254 
255  return pin_count;
256  }
257 
262  void getPossibleCollisions( std::vector<SCH_ITEM*>& aItems )
263  {
264  wxCHECK_RET( m_screen, "getPossibleCollisions() with null m_screen" );
265 
266  for( SCH_ITEM* item : m_screen->Items().Overlapping( m_symbol->GetBoundingBox() ) )
267  {
268  if( SCH_COMPONENT* candidate = dynamic_cast<SCH_COMPONENT*>( item ) )
269  {
270  if( candidate == m_symbol )
271  continue;
272 
273  std::vector<SCH_FIELD*> fields;
274  candidate->GetFields( fields, /* aVisibleOnly */ true );
275 
276  for( SCH_FIELD* field : fields )
277  aItems.push_back( field );
278  }
279 
280  aItems.push_back( item );
281  }
282  }
283 
288  std::vector<SCH_ITEM*> filterCollisions( const EDA_RECT& aRect )
289  {
290  std::vector<SCH_ITEM*> filtered;
291 
292  for( SCH_ITEM* item : m_colliders )
293  {
294  EDA_RECT item_box;
295 
296  if( SCH_COMPONENT* item_comp = dynamic_cast<SCH_COMPONENT*>( item ) )
297  item_box = item_comp->GetBodyBoundingBox();
298  else
299  item_box = item->GetBoundingBox();
300 
301  if( item_box.Intersects( aRect ) )
302  filtered.push_back( item );
303  }
304  return filtered;
305  }
306 
311  std::vector<SIDE_AND_NPINS> getPreferredSides()
312  {
313  SIDE_AND_NPINS sides_init[] = {
315  { SIDE_TOP, pinsOnSide( SIDE_TOP ) },
318  };
319  std::vector<SIDE_AND_NPINS> sides( sides_init, sides_init + arrayDim( sides_init ) );
320 
321  int orient = m_symbol->GetOrientation();
322  int orient_angle = orient & 0xff; // enum is a bitmask
323  bool h_mirrored = ( ( orient & CMP_MIRROR_X )
324  && ( orient_angle == CMP_ORIENT_0 || orient_angle == CMP_ORIENT_180 ) );
325  double w = double( m_symbol_bbox.GetWidth() );
326  double h = double( m_symbol_bbox.GetHeight() );
327 
328  // The preferred-sides heuristics are a bit magical. These were determined mostly
329  // by trial and error.
330 
331  if( m_is_power_symbol )
332  {
333  // For power symbols, we generally want the label at the top first.
334  switch( orient_angle )
335  {
336  case CMP_ORIENT_0:
337  std::swap( sides[0], sides[1] );
338  std::swap( sides[1], sides[3] );
339  // TOP, BOTTOM, RIGHT, LEFT
340  break;
341  case CMP_ORIENT_90:
342  std::swap( sides[0], sides[2] );
343  std::swap( sides[1], sides[2] );
344  // LEFT, RIGHT, TOP, BOTTOM
345  break;
346  case CMP_ORIENT_180:
347  std::swap( sides[0], sides[3] );
348  // BOTTOM, TOP, LEFT, RIGHT
349  break;
350  case CMP_ORIENT_270:
351  std::swap( sides[1], sides[2] );
352  // RIGHT, LEFT, TOP, BOTTOM
353  break;
354  }
355  }
356  else
357  {
358  // If the symbol is horizontally mirrored, swap left and right
359  if( h_mirrored )
360  {
361  std::swap( sides[0], sides[2] );
362  }
363 
364  // If the symbol is very long or is a power symbol, swap H and V
365  if( w/h > 3.0 )
366  {
367  std::swap( sides[0], sides[1] );
368  std::swap( sides[1], sides[3] );
369  }
370  }
371 
372  return sides;
373  }
374 
378  std::vector<SIDE_AND_COLL> getCollidingSides()
379  {
380  SIDE sides_init[] = { SIDE_RIGHT, SIDE_TOP, SIDE_LEFT, SIDE_BOTTOM };
381  std::vector<SIDE> sides( sides_init, sides_init + arrayDim( sides_init ) );
382  std::vector<SIDE_AND_COLL> colliding;
383 
384  // Iterate over all sides and find the ones that collide
385  for( SIDE side : sides )
386  {
387  EDA_RECT box( fieldBoxPlacement( side ), m_fbox_size );
388 
389  COLLISION collision = COLLIDE_NONE;
390 
391  for( SCH_ITEM* collider : filterCollisions( box ) )
392  {
393  SCH_LINE* line = dynamic_cast<SCH_LINE*>( collider );
394 
395  if( line && !side.x )
396  {
397  wxPoint start = line->GetStartPoint(), end = line->GetEndPoint();
398 
399  if( start.y == end.y && collision != COLLIDE_OBJECTS )
400  collision = COLLIDE_H_WIRES;
401  else
402  collision = COLLIDE_OBJECTS;
403  }
404  else
405  {
406  collision = COLLIDE_OBJECTS;
407  }
408  }
409 
410  if( collision != COLLIDE_NONE )
411  colliding.push_back( { side, collision } );
412  }
413 
414  return colliding;
415  }
416 
421  SIDE_AND_NPINS chooseSideFiltered( std::vector<SIDE_AND_NPINS>& aSides,
422  const std::vector<SIDE_AND_COLL>& aCollidingSides,
423  COLLISION aCollision,
424  SIDE_AND_NPINS aLastSelection)
425  {
426  SIDE_AND_NPINS sel = aLastSelection;
427 
428  std::vector<SIDE_AND_NPINS>::iterator it = aSides.begin();
429 
430  while( it != aSides.end() )
431  {
432  bool collide = false;
433 
434  for( SIDE_AND_COLL collision : aCollidingSides )
435  {
436  if( collision.side == it->side && collision.collision == aCollision )
437  collide = true;
438  }
439 
440  if( !collide )
441  {
442  ++it;
443  }
444  else
445  {
446  if( it->pins <= sel.pins )
447  {
448  sel.pins = it->pins;
449  sel.side = it->side;
450  }
451 
452  it = aSides.erase( it );
453  }
454  }
455 
456  return sel;
457  }
458 
464  SIDE chooseSideForFields( bool aAvoidCollisions )
465  {
466  std::vector<SIDE_AND_NPINS> sides = getPreferredSides();
467 
468  std::reverse( sides.begin(), sides.end() );
469  SIDE_AND_NPINS side = { wxPoint( 1, 0 ), UINT_MAX };
470 
471  if( aAvoidCollisions )
472  {
473  std::vector<SIDE_AND_COLL> colliding_sides = getCollidingSides();
474  side = chooseSideFiltered( sides, colliding_sides, COLLIDE_OBJECTS, side );
475  side = chooseSideFiltered( sides, colliding_sides, COLLIDE_H_WIRES, side );
476  }
477 
478  for( SIDE_AND_NPINS& each_side : sides | boost::adaptors::reversed )
479  {
480  if( !each_side.pins ) return each_side.side;
481  }
482 
483  for( SIDE_AND_NPINS& each_side : sides )
484  {
485  if( each_side.pins <= side.pins )
486  {
487  side.pins = each_side.pins;
488  side.side = each_side.side;
489  }
490  }
491 
492  return side.side;
493  }
494 
500  void justifyField( SCH_FIELD* aField, SIDE aFieldSide )
501  {
502  // Justification is set twice to allow IsHorizJustifyFlipped() to work correctly.
503  aField->SetHorizJustify( TO_HJUSTIFY( -aFieldSide.x ) );
504  aField->SetHorizJustify( TO_HJUSTIFY( -aFieldSide.x
505  * ( aField->IsHorizJustifyFlipped() ? -1 : 1 ) ) );
507  }
508 
512  wxPoint fieldBoxPlacement( SIDE aFieldSide )
513  {
514  wxPoint fbox_center = m_symbol_bbox.Centre();
515  int offs_x = ( m_symbol_bbox.GetWidth() + m_fbox_size.GetWidth() ) / 2;
516  int offs_y = ( m_symbol_bbox.GetHeight() + m_fbox_size.GetHeight() ) / 2;
517 
518  if( aFieldSide.x != 0 )
519  offs_x += HPADDING;
520  else if( aFieldSide.y != 0 )
521  offs_y += VPADDING;
522 
523  fbox_center.x += aFieldSide.x * offs_x;
524  fbox_center.y += aFieldSide.y * offs_y;
525 
526  wxPoint fbox_pos( fbox_center.x - m_fbox_size.GetWidth() / 2,
527  fbox_center.y - m_fbox_size.GetHeight() / 2 );
528 
529  return fbox_pos;
530  }
531 
536  bool fitFieldsBetweenWires( EDA_RECT* aBox, SIDE aSide )
537  {
538  if( aSide != SIDE_TOP && aSide != SIDE_BOTTOM )
539  return false;
540 
541  std::vector<SCH_ITEM*> colliders = filterCollisions( *aBox );
542 
543  if( colliders.empty() )
544  return false;
545 
546  // Find the offset of the wires for proper positioning
547  int offset = 0;
548 
549  for( SCH_ITEM* item : colliders )
550  {
551  SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
552 
553  if( !line )
554  return false;
555 
556  wxPoint start = line->GetStartPoint(), end = line->GetEndPoint();
557 
558  if( start.y != end.y )
559  return false;
560 
561  int this_offset = (3 * WIRE_V_SPACING / 2) - ( start.y % WIRE_V_SPACING );
562 
563  if( offset == 0 )
564  offset = this_offset;
565  else if( offset != this_offset )
566  return false;
567  }
568 
569  // At this point we are recomputing the field box size. Do not
570  // return false after this point.
571  m_fbox_size = computeFBoxSize( /* aDynamic */ false );
572 
573  wxPoint pos = aBox->GetPosition();
574 
575  pos.y = round_n( pos.y, WIRE_V_SPACING, aSide == SIDE_BOTTOM );
576 
577  aBox->SetOrigin( pos );
578  return true;
579  }
580 
589  int fieldHorizPlacement( SCH_FIELD *aField, const EDA_RECT &aFieldBox )
590  {
591  int field_hjust;
592  int field_xcoord;
593 
594  if( aField->IsHorizJustifyFlipped() )
595  field_hjust = -aField->GetHorizJustify();
596  else
597  field_hjust = aField->GetHorizJustify();
598 
599  switch( field_hjust )
600  {
602  field_xcoord = aFieldBox.GetLeft();
603  break;
605  field_xcoord = aFieldBox.Centre().x;
606  break;
608  field_xcoord = aFieldBox.GetRight();
609  break;
610  default:
611  wxFAIL_MSG( "Unexpected value for SCH_FIELD::GetHorizJustify()" );
612  field_xcoord = aFieldBox.Centre().x; // Most are centered
613  }
614 
615  return field_xcoord;
616  }
617 
629  int fieldVertPlacement( SCH_FIELD *aField, const EDA_RECT &aFieldBox, int *aPosAccum,
630  bool aDynamic )
631  {
632  int field_height;
633  int padding;
634 
635  if( !aDynamic )
636  {
637  field_height = WIRE_V_SPACING / 2;
638  padding = WIRE_V_SPACING / 2;
639  }
640  else if( m_align_to_grid )
641  {
642  field_height = aField->GetBoundingBox().GetHeight();
643  padding = round_n( field_height, Mils2iu( 50 ), true ) - field_height;
644  }
645  else
646  {
647  field_height = aField->GetBoundingBox().GetHeight();
648  padding = FIELD_PADDING;
649  }
650 
651  int placement = *aPosAccum + padding / 2 + field_height / 2;
652 
653  *aPosAccum += padding + field_height;
654 
655  return placement;
656  }
657 
658 private:
661  std::vector<SCH_FIELD*> m_fields;
662  std::vector<SCH_ITEM*> m_colliders;
664  wxSize m_fbox_size;
668 };
669 
670 
675 
676 
677 void SCH_COMPONENT::AutoplaceFields( SCH_SCREEN* aScreen, bool aManual )
678 {
679  if( aManual )
680  wxASSERT_MSG( aScreen, "A SCH_SCREEN pointer must be given for manual autoplacement" );
681 
682  AUTOPLACER autoplacer( this, aScreen );
683  autoplacer.DoAutoplace( aManual );
685 }
#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:50
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:50
LIB_PIN * GetLibPin() const
Definition: sch_pin.h:58
std::vector< SCH_FIELD * > m_fields
EDA_TEXT_HJUSTIFY_T
Definition: eda_text.h:61
#define VPADDING
std::vector< SIDE_AND_NPINS > getPreferredSides()
Return a list with the preferred field sides for the symbol, in decreasing order of preference.
int GetOrientation()
Get the display symbol orientation.
wxPoint GetStartPoint() const
Definition: sch_line.h:90
bool collide(T aObject, U aAnotherObject, int aMinDistance)
collide template method
Definition: shape_index.h:92
int GetTop() const
Definition: eda_rect.h:118
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:117
#define HPADDING
int GetWidth() const
Definition: eda_rect.h:114
bool IsInNetlist() const
void SetOrigin(const wxPoint &pos)
Definition: eda_rect.h:126
EDA_RECT m_symbol_bbox
Definition: lib_pin.h:50
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:791
wxPoint fieldBoxPlacement(SIDE aFieldSide)
Return the position of the field bounding box.
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:498
int fieldHorizPlacement(SCH_FIELD *aField, const EDA_RECT &aFieldBox)
Place a field horizontally, taking into account the field width and justification.
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...
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:701
const EDA_RECT GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
int y1
Definition: transform.h:49
static const SIDE SIDE_LEFT
SCH_SCREEN * m_screen
EDA_TEXT_HJUSTIFY_T GetHorizJustify() const
Definition: eda_text.h:205
std::vector< SCH_ITEM * > m_colliders
EDA_RECT GetBodyBoundingBox() const
Return a bounding box for the symbol body but not the fields.
void SetVertJustify(EDA_TEXT_VJUSTIFY_T aType)
Definition: eda_text.h:209
const wxPoint GetPosition() const
Definition: eda_rect.h:107
KIFACE_I & Kiface()
Global KIFACE_I "get" accessor.
wxSize computeFBoxSize(bool aDynamic)
Compute and return the size of the fields' bounding box.
AUTOPLACER(SCH_COMPONENT *aSymbol, SCH_SCREEN *aScreen)
int GetRight() const
Definition: eda_rect.h:116
int fieldVertPlacement(SCH_FIELD *aField, const EDA_RECT &aFieldBox, int *aPosAccum, bool aDynamic)
Place a field vertically.
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition: arraydim.h:31
unsigned pinsOnSide(SIDE aSide)
Count the number of pins on a side of the symbol.
TRANSFORM & GetTransform()
Definition: sch_symbol.h:231
#define FIELD_PADDING
EDA_TEXT_HJUSTIFY_T TO_HJUSTIFY(int x)
Convert an integer to a horizontal justification; neg=L zero=C pos=R.
int GetHeight() const
Definition: eda_rect.h:115
SCH_COMPONENT * m_symbol
EE_TYPE Overlapping(const EDA_RECT &aRect) const
Definition: sch_rtree.h:221
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:856
static const SIDE SIDE_RIGHT
SIDE chooseSideForFields(bool aAvoidCollisions)
Look where a symbol's pins are to pick a side to put the fields on.
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:262
void SetHorizJustify(EDA_TEXT_HJUSTIFY_T aType)
Definition: eda_text.h:208
static const SIDE SIDE_BOTTOM
EE_RTREE & Items()
Definition: sch_screen.h:103
Handle the component boundary box.
Definition: eda_rect.h:42
void AutoplaceFields(SCH_SCREEN *aScreen, bool aManual) override
Automatically orient all the fields in the symbol.
Schematic symbol object.
Definition: sch_symbol.h:78
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:60
bool IsHorizJustifyFlipped() const
Return whether the field will be rendered with the horizontal justification inverted due to rotation ...
Definition: sch_field.cpp:304
bool Intersects(const EDA_RECT &aRect) const
Test for a common area between rectangles.
Definition: eda_rect.cpp:150
void DoAutoplace(bool aManual)
Do the actual autoplacement.
#define TEXT_ANGLE_VERT
Definition: eda_text.h:51
static const SIDE SIDE_TOP
void SetPosition(const wxPoint &aPosition) override
Definition: sch_field.cpp:687
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:197
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.
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