KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 The 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, see <https://www.gnu.org/licenses/>.
19 */
20
21/******************************************************************************
22 * Field autoplacer: Tries to find an optimal place for symbol fields, and places them there.
23 * There are two modes: "auto"-autoplace, and "manual" autoplace.
24 * Auto mode is for when the process is run automatically, like when rotating parts, and it
25 * avoids doing things that would be helpful for the final positioning but annoying if they
26 * happened without permission.
27 * Short description of the process:
28 *
29 * 1. Compute the dimensions of the fields' bounding box ::computeFBoxSize
30 * 2. Determine which side the fields will go on. ::chooseSideForFields
31 * 1. Sort the four sides in preference order,
32 * depending on the symbol's shape and
33 * orientation ::getPreferredSides
34 * 2. If in manual mode, sift out the sides that would
35 * cause fields to overlap other items ::getCollidingSides
36 * 3. If any remaining sides have zero pins there,
37 * choose the highest zero-pin side according to
38 * preference order.
39 * 4. If all sides have pins, choose the side with the
40 * fewest pins.
41 * 3. Compute the position of the fields' bounding box ::fieldBoxPlacement
42 * 4. In manual mode, shift the box vertically if possible
43 * to fit fields between adjacent wires ::fitFieldsBetweenWires
44 * 5. Move all fields to their final positions
45 * 1. Re-justify fields if options allow that ::justifyField
46 * 2. Round to a 50-mil grid coordinate if desired
47 */
48
49#include <boost/range/adaptor/reversed.hpp>
50
52#include <sch_edit_frame.h>
53#include <sch_line.h>
54#include <kiface_base.h>
55#include <algorithm>
56#include <tool/tool_manager.h>
57#include <core/arraydim.h>
58
59#define FIELD_PADDING schIUScale.MilsToIU( 15 ) // arbitrarily chosen for aesthetics
60#define WIRE_V_SPACING schIUScale.MilsToIU( 100 )
61#define HPADDING schIUScale.MilsToIU( 25 ) // arbitrarily chosen for aesthetics
62#define VPADDING schIUScale.MilsToIU( 15 ) // arbitrarily chosen for aesthetics
63
67template<typename T> T round_n( const T& value, const T& n, bool aRoundUp )
68{
69 if( value % n )
70 return n * (value / n + (aRoundUp ? 1 : 0));
71 else
72 return value;
73}
74
75
77{
78public:
79 typedef VECTOR2I SIDE;
82
84 {
86 unsigned pins;
87 };
88
94
95 AUTOPLACER( SYMBOL* aSymbol, SCH_SCREEN* aScreen ) :
96 m_screen( aScreen ),
97 m_symbol( aSymbol ),
98 m_is_power_symbol( false )
99 {
100 m_symbol->GetFields( m_fields, /* aVisibleOnly */ true );
101
102 auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
103 wxASSERT( cfg );
104
105 m_allow_rejustify = false;
106 m_align_to_grid = true;
107
108 if( cfg )
109 {
110 m_allow_rejustify = cfg->m_AutoplaceFields.allow_rejustify;
111 m_align_to_grid = cfg->m_AutoplaceFields.align_to_grid;
112 }
113
114 // Fields always display horizontally after autoplace. For 90/270 rotated
115 // symbols, GetDrawRotation() flips the stored angle, so we store VERTICAL
116 // to counteract the transform and produce horizontal display.
117 m_field_angle = m_symbol->GetTransform().y1 ? ANGLE_VERTICAL : ANGLE_HORIZONTAL;
118
119 m_symbol_bbox = m_symbol->GetBodyBoundingBox();
120 m_fbox_size = computeFBoxSize( /* aDynamic */ true );
121
122 if( SCH_SYMBOL* schSymbol = dynamic_cast<SCH_SYMBOL*>( m_symbol ) )
123 m_is_power_symbol = !schSymbol->DoNetList();
124
125 if( aScreen )
127 }
128
134 {
135 bool forceWireSpacing = false;
136 SIDE_AND_NPINS sideandpins = chooseSideForFields( aAlgo == AUTOPLACE_MANUAL );
137 SIDE field_side = sideandpins.side;
138 VECTOR2I fbox_pos = fieldBoxPlacement( sideandpins );
139 BOX2I field_box( fbox_pos, m_fbox_size );
140
141 if( aAlgo == AUTOPLACE_MANUAL )
142 forceWireSpacing = fitFieldsBetweenWires( &field_box, field_side );
143
144 // Move the fields
145 int last_y_coord = field_box.GetTop();
146
147 for( SCH_FIELD* field : m_fields )
148 {
149 if( !field->IsVisible() || !field->CanAutoplace() )
150 continue;
151
152 field->SetTextAngle( m_field_angle );
153
155 {
156 if( sideandpins.pins > 0 )
157 {
158 if( field_side == SIDE_TOP || field_side == SIDE_BOTTOM )
159 justifyField( field, SIDE_RIGHT );
160 else
161 justifyField( field, SIDE_TOP );
162 }
163 else
164 {
165 justifyField( field, field_side );
166 }
167 }
168
169 VECTOR2I pos( fieldHPlacement( field, field_box ),
170 fieldVPlacement( field, field_box, &last_y_coord, !forceWireSpacing ) );
171
172 if( m_align_to_grid )
173 {
174 if( abs( field_side.x ) > 0 )
175 pos.x = round_n( pos.x, schIUScale.MilsToIU( 50 ), field_side.x >= 0 );
176
177 if( abs( field_side.y ) > 0 )
178 pos.y = round_n( pos.y, schIUScale.MilsToIU( 50 ), field_side.y >= 0 );
179 }
180
181 field->SetPosition( pos );
182 }
183 }
184
185protected:
190 VECTOR2I computeFBoxSize( bool aDynamic )
191 {
192 int max_field_width = 0;
193 int total_height = 0;
194
195 for( SCH_FIELD* field : m_fields )
196 {
197 if( !field->IsVisible() || !field->CanAutoplace() )
198 {
199 continue;
200 }
201
202 // GetBoundingBox() applies both the field's text angle and the symbol
203 // transform. Set the display angle so the combined rotation produces
204 // bounding box dimensions matching the final horizontal display, then
205 // restore the original angle.
206 EDA_ANGLE savedAngle = field->GetTextAngle();
207 field->SetTextAngle( m_field_angle );
208 BOX2I bbox = field->GetBoundingBox();
209 field->SetTextAngle( savedAngle );
210 int field_width = bbox.GetWidth();
211 int field_height = bbox.GetHeight();
212
213 max_field_width = std::max( max_field_width, field_width );
214
215 if( !aDynamic )
216 total_height += WIRE_V_SPACING;
217 else if( m_align_to_grid )
218 total_height += round_n( field_height, schIUScale.MilsToIU( 50 ), true );
219 else
220 total_height += field_height + FIELD_PADDING;
221 }
222
223 return VECTOR2I( max_field_width, total_height );
224 }
225
230 {
231 PIN_ORIENTATION pin_orient = aPin->PinDrawOrient( m_symbol->GetTransform() );
232
233 switch( pin_orient )
234 {
239 default:
240 wxFAIL_MSG( wxS( "Invalid pin orientation" ) );
241 return SIDE_LEFT;
242 }
243 }
244
248 unsigned pinsOnSide( SIDE aSide )
249 {
250 unsigned pin_count = 0;
251
252 for( SCH_PIN* each_pin : m_symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
253 {
254 if( !each_pin->IsVisible() && !m_is_power_symbol )
255 continue;
256
257 if( getPinSide( each_pin ) == aSide )
258 ++pin_count;
259 }
260
261 return pin_count;
262 }
263
268 void getPossibleCollisions( std::vector<SCH_ITEM*>& aItems )
269 {
270 wxCHECK_RET( m_screen, wxS( "getPossibleCollisions() with null m_screen" ) );
271
272 BOX2I symbolBox = m_symbol->GetBodyAndPinsBoundingBox();
273 std::vector<SIDE_AND_NPINS> sides = getPreferredSides();
274
275 for( SIDE_AND_NPINS& side : sides )
276 {
277 BOX2I box( fieldBoxPlacement( side ), m_fbox_size );
278 box.Merge( symbolBox );
279
280 for( SCH_ITEM* item : m_screen->Items().Overlapping( box ) )
281 {
282 if( SCH_SYMBOL* candidate = dynamic_cast<SCH_SYMBOL*>( item ) )
283 {
284 if( candidate == m_symbol )
285 continue;
286
287 std::vector<SCH_FIELD*> fields;
288 candidate->GetFields( fields, /* aVisibleOnly */ true );
289
290 for( SCH_FIELD* field : fields )
291 aItems.push_back( field );
292 }
293
294 aItems.push_back( item );
295 }
296 }
297 }
298
303 std::vector<SCH_ITEM*> filterCollisions( const BOX2I& aRect )
304 {
305 std::vector<SCH_ITEM*> filtered;
306
307 for( SCH_ITEM* item : m_colliders )
308 {
309 BOX2I item_box;
310
311 if( SCH_SYMBOL* item_comp = dynamic_cast<SCH_SYMBOL*>( item ) )
312 item_box = item_comp->GetBodyAndPinsBoundingBox();
313 else
314 item_box = item->GetBoundingBox();
315
316 if( item_box.Intersects( aRect ) )
317 filtered.push_back( item );
318 }
319
320 return filtered;
321 }
322
327 std::vector<SIDE_AND_NPINS> getPreferredSides()
328 {
329 SIDE_AND_NPINS sides_init[] = {
334 };
335 std::vector<SIDE_AND_NPINS> sides( sides_init, sides_init + arrayDim( sides_init ) );
336
337 int orient = m_symbol->GetOrientation();
338 int orient_angle = orient & 0xff; // enum is a bitmask
339 bool h_mirrored = ( ( orient & SYM_MIRROR_X )
340 && ( orient_angle == SYM_ORIENT_0 || orient_angle == SYM_ORIENT_180 ) );
341 double w = double( m_symbol_bbox.GetWidth() );
342 double h = double( m_symbol_bbox.GetHeight() );
343
344 // The preferred-sides heuristics are a bit magical. These were determined mostly
345 // by trial and error.
346
348 {
349 // For power symbols, we generally want the label at the top first.
350 switch( orient_angle )
351 {
352 case SYM_ORIENT_0:
353 std::swap( sides[0], sides[1] );
354 std::swap( sides[1], sides[3] );
355 // TOP, BOTTOM, RIGHT, LEFT
356 break;
357 case SYM_ORIENT_90:
358 std::swap( sides[0], sides[2] );
359 std::swap( sides[1], sides[2] );
360 // LEFT, RIGHT, TOP, BOTTOM
361 break;
362 case SYM_ORIENT_180:
363 std::swap( sides[0], sides[3] );
364 // BOTTOM, TOP, LEFT, RIGHT
365 break;
366 case SYM_ORIENT_270:
367 std::swap( sides[1], sides[2] );
368 // RIGHT, LEFT, TOP, BOTTOM
369 break;
370 }
371 }
372 else
373 {
374 // If the symbol is horizontally mirrored, swap left and right
375 if( h_mirrored )
376 {
377 std::swap( sides[0], sides[2] );
378 }
379
380 // If the symbol is very long or is a power symbol, swap H and V
381 if( w/h > 3.0 )
382 {
383 std::swap( sides[0], sides[1] );
384 std::swap( sides[1], sides[3] );
385 }
386 }
387
388 return sides;
389 }
390
395 {
396 if( !m_screen )
397 return BOX2I();
398
399 const PAGE_INFO& pageInfo = m_screen->GetPageSettings();
401
402 int pageWidth = pageInfo.GetWidthIU( schIUScale.IU_PER_MILS );
403 int pageHeight = pageInfo.GetHeightIU( schIUScale.IU_PER_MILS );
404
405 int leftMargin = schIUScale.mmToIU( dsModel.GetLeftMargin() );
406 int rightMargin = schIUScale.mmToIU( dsModel.GetRightMargin() );
407 int topMargin = schIUScale.mmToIU( dsModel.GetTopMargin() );
408 int bottomMargin = schIUScale.mmToIU( dsModel.GetBottomMargin() );
409
410 BOX2I drawableArea;
411 drawableArea.SetOrigin( leftMargin, topMargin );
412 drawableArea.SetEnd( pageWidth - rightMargin, pageHeight - bottomMargin );
413
414 return drawableArea;
415 }
416
420 std::vector<SIDE_AND_COLL> getCollidingSides()
421 {
422 SIDE sides_init[] = { SIDE_RIGHT, SIDE_TOP, SIDE_LEFT, SIDE_BOTTOM };
423 std::vector<SIDE> sides( sides_init, sides_init + arrayDim( sides_init ) );
424 std::vector<SIDE_AND_COLL> colliding;
425
426 BOX2I drawableArea = getDrawableArea();
427 bool checkDrawableArea = ( drawableArea.GetWidth() > 0 && drawableArea.GetHeight() > 0 );
428
429 // Iterate over all sides and find the ones that collide
430 for( SIDE side : sides )
431 {
432 SIDE_AND_NPINS sideandpins;
433 sideandpins.side = side;
434 sideandpins.pins = pinsOnSide( side );
435
436 BOX2I box( fieldBoxPlacement( sideandpins ), m_fbox_size );
437
438 COLLISION collision = COLLIDE_NONE;
439
440 // Check collision with drawing sheet boundary
441 if( checkDrawableArea && !drawableArea.Contains( box ) )
442 collision = COLLIDE_OBJECTS;
443
444 for( SCH_ITEM* collider : filterCollisions( box ) )
445 {
446 SCH_LINE* line = dynamic_cast<SCH_LINE*>( collider );
447
448 if( line && !side.x )
449 {
450 VECTOR2I start = line->GetStartPoint(), end = line->GetEndPoint();
451
452 if( start.y == end.y && collision != COLLIDE_OBJECTS )
453 collision = COLLIDE_H_WIRES;
454 else
455 collision = COLLIDE_OBJECTS;
456 }
457 else
458 {
459 collision = COLLIDE_OBJECTS;
460 }
461 }
462
463 if( collision != COLLIDE_NONE )
464 colliding.push_back( { side, collision } );
465 }
466
467 return colliding;
468 }
469
474 SIDE_AND_NPINS chooseSideFiltered( std::vector<SIDE_AND_NPINS>& aSides,
475 const std::vector<SIDE_AND_COLL>& aCollidingSides,
476 COLLISION aCollision,
477 SIDE_AND_NPINS aLastSelection)
478 {
479 SIDE_AND_NPINS sel = aLastSelection;
480
481 std::vector<SIDE_AND_NPINS>::iterator it = aSides.begin();
482
483 while( it != aSides.end() )
484 {
485 bool collide = false;
486
487 for( SIDE_AND_COLL collision : aCollidingSides )
488 {
489 if( collision.side == it->side && collision.collision == aCollision )
490 collide = true;
491 }
492
493 if( !collide )
494 {
495 ++it;
496 }
497 else
498 {
499 if( it->pins <= sel.pins )
500 {
501 sel.pins = it->pins;
502 sel.side = it->side;
503 }
504
505 it = aSides.erase( it );
506 }
507 }
508
509 return sel;
510 }
511
517 SIDE_AND_NPINS chooseSideForFields( bool aAvoidCollisions )
518 {
519 std::vector<SIDE_AND_NPINS> sides = getPreferredSides();
520
521 std::reverse( sides.begin(), sides.end() );
522 SIDE_AND_NPINS side = { VECTOR2I( 1, 0 ), UINT_MAX };
523
524 if( aAvoidCollisions )
525 {
526 std::vector<SIDE_AND_COLL> colliding_sides = getCollidingSides();
527 side = chooseSideFiltered( sides, colliding_sides, COLLIDE_OBJECTS, side );
528 side = chooseSideFiltered( sides, colliding_sides, COLLIDE_H_WIRES, side );
529 }
530
531 for( SIDE_AND_NPINS& each_side : sides | boost::adaptors::reversed )
532 {
533 if( !each_side.pins ) return each_side;
534 }
535
536 for( SIDE_AND_NPINS& each_side : sides )
537 {
538 if( each_side.pins <= side.pins )
539 {
540 side.pins = each_side.pins;
541 side.side = each_side.side;
542 }
543 }
544
545 return side;
546 }
547
553 void justifyField( SCH_FIELD* aField, SIDE aFieldSide )
554 {
555 // Justification is set twice to allow IsHorizJustifyFlipped() to work correctly.
556 aField->SetHorizJustify( ToHAlignment( -aFieldSide.x ) );
557 if( aField->IsHorizJustifyFlipped() )
559
561 }
562
567 {
568 VECTOR2I fbox_center = m_symbol_bbox.Centre();
569 int offs_x = ( m_symbol_bbox.GetWidth() + m_fbox_size.x ) / 2;
570 int offs_y = ( m_symbol_bbox.GetHeight() + m_fbox_size.y ) / 2;
571
572 if( aFieldSideAndPins.side.x != 0 )
573 offs_x += HPADDING;
574 else if( aFieldSideAndPins.side.y != 0 )
575 offs_y += VPADDING;
576
577 fbox_center.x += aFieldSideAndPins.side.x * offs_x;
578 fbox_center.y += aFieldSideAndPins.side.y * offs_y;
579
580 int x = fbox_center.x - ( m_fbox_size.x / 2 );
581 int y = fbox_center.y - ( m_fbox_size.y / 2 );
582
583 auto getPinsBox =
584 [&]( const VECTOR2I& aSide )
585 {
586 BOX2I pinsBox;
587
588 for( SCH_PIN* each_pin : m_symbol->GetGraphicalPins( ALL_UNITS, ALL_BODY_STYLES ) )
589 {
590 if( !each_pin->IsVisible() && !m_is_power_symbol )
591 continue;
592
593 if( getPinSide( each_pin ) == aSide )
594 pinsBox.Merge( each_pin->GetBoundingBox() );
595 }
596
597 return pinsBox;
598 };
599
600 if( aFieldSideAndPins.pins > 0 )
601 {
602 BOX2I pinsBox = getPinsBox( aFieldSideAndPins.side );
603
604 if( aFieldSideAndPins.side == SIDE_TOP || aFieldSideAndPins.side == SIDE_BOTTOM )
605 {
606 x = pinsBox.GetRight() + ( HPADDING * 2 );
607 }
608 else if( aFieldSideAndPins.side == SIDE_RIGHT || aFieldSideAndPins.side == SIDE_LEFT )
609 {
610 y = pinsBox.GetTop() - ( m_fbox_size.y + ( VPADDING * 2 ) );
611 }
612 }
613
614 return VECTOR2I( x, y );
615 }
616
621 bool fitFieldsBetweenWires( BOX2I* aBox, SIDE aSide )
622 {
623 if( aSide != SIDE_TOP && aSide != SIDE_BOTTOM )
624 return false;
625
626 std::vector<SCH_ITEM*> colliders = filterCollisions( *aBox );
627
628 if( colliders.empty() )
629 return false;
630
631 // Find the offset of the wires for proper positioning
632 int offset = 0;
633
634 for( SCH_ITEM* item : colliders )
635 {
636 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
637
638 if( !line )
639 return false;
640
641 VECTOR2I start = line->GetStartPoint(), end = line->GetEndPoint();
642
643 if( start.y != end.y )
644 return false;
645
646 int this_offset = (3 * WIRE_V_SPACING / 2) - ( start.y % WIRE_V_SPACING );
647
648 if( offset == 0 )
649 offset = this_offset;
650 else if( offset != this_offset )
651 return false;
652 }
653
654 // At this point we are recomputing the field box size. Do not
655 // return false after this point.
656 m_fbox_size = computeFBoxSize( /* aDynamic */ false );
657
658 VECTOR2I pos = aBox->GetPosition();
659
660 pos.y = round_n( pos.y, WIRE_V_SPACING, aSide == SIDE_BOTTOM );
661
662 aBox->SetOrigin( pos );
663 return true;
664 }
665
674 int fieldHPlacement( SCH_FIELD* aField, const BOX2I& aFieldBox )
675 {
676 int field_hjust;
677 int field_xcoord;
678
679 if( aField->IsHorizJustifyFlipped() )
680 field_hjust = -aField->GetHorizJustify();
681 else
682 field_hjust = aField->GetHorizJustify();
683
684 switch( field_hjust )
685 {
687 field_xcoord = aFieldBox.GetLeft();
688 break;
690 field_xcoord = aFieldBox.Centre().x;
691 break;
693 field_xcoord = aFieldBox.GetRight();
694 break;
695 default:
696 wxFAIL_MSG( wxS( "Unexpected value for SCH_FIELD::GetHorizJustify()" ) );
697 field_xcoord = aFieldBox.Centre().x; // Most are centered
698 }
699
700 return field_xcoord;
701 }
702
714 int fieldVPlacement( SCH_FIELD* aField, const BOX2I& aFieldBox, int* aAccumulatedPosition,
715 bool aDynamic )
716 {
717 int field_height;
718 int padding;
719
720 if( !aDynamic )
721 {
722 field_height = WIRE_V_SPACING / 2;
723 padding = WIRE_V_SPACING / 2;
724 }
725 else if( m_align_to_grid )
726 {
727 field_height = aField->GetBoundingBox().GetHeight();
728 padding = round_n( field_height, schIUScale.MilsToIU( 50 ), true ) - field_height;
729 }
730 else
731 {
732 field_height = aField->GetBoundingBox().GetHeight();
733 padding = FIELD_PADDING;
734 }
735
736 int placement = *aAccumulatedPosition + padding / 2 + field_height / 2;
737
738 *aAccumulatedPosition += padding + field_height;
739
740 return placement;
741 }
742
743private:
746 std::vector<SCH_FIELD*> m_fields;
747 std::vector<SCH_ITEM*> m_colliders;
754};
755
756
761
762
764{
765 if( aAlgo == AUTOPLACE_MANUAL )
766 wxASSERT_MSG( aScreen, wxS( "A SCH_SCREEN ptr must be given for manual autoplacement" ) );
767
768 AUTOPLACER autoplacer( this, aScreen );
769 autoplacer.DoAutoplace( aAlgo );
770
771 switch( aAlgo )
772 {
775 default: wxFAIL_MSG( "Unknown autoplace algorithm" ); break;
776 }
777}
778
779
781{
782 if( aAlgo == AUTOPLACE_MANUAL )
783 wxFAIL_MSG( wxS( "Manual autoplacement not supported for LIB_SYMBOLs" ) );
784
785 AUTOPLACER autoplacer( this, aScreen );
786 autoplacer.DoAutoplace( aAlgo );
787
788 switch( aAlgo )
789 {
792 default: wxFAIL_MSG( "Unknown autoplace algorithm" ); break;
793 }
794}
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition arraydim.h:27
#define HPADDING
#define FIELD_PADDING
#define VPADDING
#define WIRE_V_SPACING
T round_n(const T &value, const T &n, bool aRoundUp)
Round up/down to the nearest multiple of n.
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
VECTOR2I computeFBoxSize(bool aDynamic)
Compute and return the size of the fields' bounding box.
VECTOR2I fieldBoxPlacement(SIDE_AND_NPINS aFieldSideAndPins)
Return the position of the field bounding box.
SIDE getPinSide(SCH_PIN *aPin)
Return the side that a pin is on.
int fieldVPlacement(SCH_FIELD *aField, const BOX2I &aFieldBox, int *aAccumulatedPosition, bool aDynamic)
Place a field vertically.
void getPossibleCollisions(std::vector< SCH_ITEM * > &aItems)
Populate a list of all drawing items that may collide with the fields.
static const SIDE SIDE_TOP
static const SIDE SIDE_BOTTOM
unsigned pinsOnSide(SIDE aSide)
Count the number of pins on a side of the symbol.
std::vector< SCH_FIELD * > m_fields
BOX2I getDrawableArea()
Compute the drawable area (inside the drawing sheet border) for collision detection.
SIDE_AND_NPINS chooseSideForFields(bool aAvoidCollisions)
Look where a symbol's pins are to pick a side to put the fields on.
bool fitFieldsBetweenWires(BOX2I *aBox, SIDE aSide)
Shift a field box up or down a bit to make the fields fit between some wires.
int fieldHPlacement(SCH_FIELD *aField, const BOX2I &aFieldBox)
Place a field horizontally, taking into account the field width and justification.
SCH_SCREEN * m_screen
std::vector< SCH_ITEM * > m_colliders
static const SIDE SIDE_RIGHT
std::vector< SCH_ITEM * > filterCollisions(const BOX2I &aRect)
Filter a list of possible colliders to include only those that actually collide with a given rectangl...
std::vector< SIDE_AND_NPINS > getPreferredSides()
Return a list with the preferred field sides for the symbol, in decreasing order of preference.
std::vector< SIDE_AND_COLL > getCollidingSides()
Return a list of the sides where a field set would collide with another item.
EDA_ANGLE m_field_angle
AUTOPLACER(SYMBOL *aSymbol, SCH_SCREEN *aScreen)
static const SIDE SIDE_LEFT
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 DoAutoplace(AUTOPLACE_ALGO aAlgo)
Do the actual autoplacement.
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...
constexpr const Vec & GetPosition() const
Definition box2.h:208
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:234
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr Vec Centre() const
Definition box2.h:94
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:594
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:294
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:308
Handle the graphic items list to draw/plot the frame and title block.
double GetRightMargin()
static DS_DATA_MODEL & GetTheInstance()
Return the instance of DS_DATA_MODEL used in the application.
double GetTopMargin()
double GetBottomMargin()
double GetLeftMargin()
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:378
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:239
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:92
void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo) override
Automatically orient all the fields in the symbol.
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
int GetHeightIU(double aIUScale) const
Get the page height in IU.
Definition page_info.h:163
int GetWidthIU(double aIUScale) const
Get the page width in IU.
Definition page_info.h:154
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
bool IsHorizJustifyFlipped() const
Return whether the field will be rendered with the horizontal justification inverted due to rotation ...
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:170
AUTOPLACE_ALGO m_fieldsAutoplaced
Definition sch_item.h:842
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:39
VECTOR2I GetEndPoint() const
Definition sch_line.h:145
VECTOR2I GetStartPoint() const
Definition sch_line.h:136
PIN_ORIENTATION PinDrawOrient(const TRANSFORM &aTransform) const
Return the pin real orientation (PIN_UP, PIN_DOWN, PIN_RIGHT, PIN_LEFT), according to its orientation...
Definition sch_pin.cpp:1431
Schematic symbol object.
Definition sch_symbol.h:73
void AutoplaceFields(SCH_SCREEN *aScreen, AUTOPLACE_ALGO aAlgo) override
Automatically orient all the fields in the symbol.
A base class for LIB_SYMBOL and SCH_SYMBOL.
Definition symbol.h:60
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:445
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
PIN_ORIENTATION
The symbol library pin object orientations.
Definition pin_type.h:101
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:123
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:107
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:114
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:131
AUTOPLACE_ALGO
Definition sch_item.h:73
@ AUTOPLACE_MANUAL
Definition sch_item.h:76
@ AUTOPLACE_AUTO
Definition sch_item.h:75
bool collide(T aObject, U aAnotherObject, int aLayer, int aMinDistance)
Used by SHAPE_INDEX to implement Query().
#define ALL_UNITS
Definition symbol.h:150
#define ALL_BODY_STYLES
Definition symbol.h:151
@ SYM_ORIENT_270
Definition symbol.h:39
@ SYM_ORIENT_180
Definition symbol.h:38
@ SYM_MIRROR_X
Definition symbol.h:40
@ SYM_ORIENT_90
Definition symbol.h:37
@ SYM_ORIENT_0
Definition symbol.h:36
VECTOR2I end
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_CENTER
constexpr GR_TEXT_H_ALIGN_T GetFlippedAlignment(GR_TEXT_H_ALIGN_T aAlign)
Get the reverse alignment: left-right are swapped, others are unchanged.
constexpr GR_TEXT_H_ALIGN_T ToHAlignment(int x)
Convert an integral value to horizontal alignment.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708