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->IsInNetlist();
124
125 if( aScreen )
127 }
128
135 {
136 bool forceWireSpacing = false;
137 SIDE_AND_NPINS sideandpins = chooseSideForFields( aAlgo == AUTOPLACE_MANUAL );
138 SIDE field_side = sideandpins.side;
139 VECTOR2I fbox_pos = fieldBoxPlacement( sideandpins );
140 BOX2I field_box( fbox_pos, m_fbox_size );
141
142 if( aAlgo == AUTOPLACE_MANUAL )
143 forceWireSpacing = fitFieldsBetweenWires( &field_box, field_side );
144
145 // Move the fields
146 int last_y_coord = field_box.GetTop();
147
148 for( SCH_FIELD* field : m_fields )
149 {
150 if( !field->IsVisible() || !field->CanAutoplace() )
151 continue;
152
153 field->SetTextAngle( m_field_angle );
154
156 {
157 if( sideandpins.pins > 0 )
158 {
159 if( field_side == SIDE_TOP || field_side == SIDE_BOTTOM )
160 justifyField( field, SIDE_RIGHT );
161 else
162 justifyField( field, SIDE_TOP );
163 }
164 else
165 {
166 justifyField( field, field_side );
167 }
168 }
169
170 VECTOR2I pos( fieldHPlacement( field, field_box ),
171 fieldVPlacement( field, field_box, &last_y_coord, !forceWireSpacing ) );
172
173 if( m_align_to_grid )
174 {
175 if( abs( field_side.x ) > 0 )
176 pos.x = round_n( pos.x, schIUScale.MilsToIU( 50 ), field_side.x >= 0 );
177
178 if( abs( field_side.y ) > 0 )
179 pos.y = round_n( pos.y, schIUScale.MilsToIU( 50 ), field_side.y >= 0 );
180 }
181
182 field->SetPosition( pos );
183 }
184 }
185
186protected:
191 VECTOR2I computeFBoxSize( bool aDynamic )
192 {
193 int max_field_width = 0;
194 int total_height = 0;
195
196 for( SCH_FIELD* field : m_fields )
197 {
198 if( !field->IsVisible() || !field->CanAutoplace() )
199 {
200 continue;
201 }
202
203 // GetBoundingBox() applies both the field's text angle and the symbol
204 // transform. Set the display angle so the combined rotation produces
205 // bounding box dimensions matching the final horizontal display, then
206 // restore the original angle.
207 EDA_ANGLE savedAngle = field->GetTextAngle();
208 field->SetTextAngle( m_field_angle );
209 BOX2I bbox = field->GetBoundingBox();
210 field->SetTextAngle( savedAngle );
211 int field_width = bbox.GetWidth();
212 int field_height = bbox.GetHeight();
213
214 max_field_width = std::max( max_field_width, field_width );
215
216 if( !aDynamic )
217 total_height += WIRE_V_SPACING;
218 else if( m_align_to_grid )
219 total_height += round_n( field_height, schIUScale.MilsToIU( 50 ), true );
220 else
221 total_height += field_height + FIELD_PADDING;
222 }
223
224 return VECTOR2I( max_field_width, total_height );
225 }
226
231 {
232 PIN_ORIENTATION pin_orient = aPin->PinDrawOrient( m_symbol->GetTransform() );
233
234 switch( pin_orient )
235 {
240 default:
241 wxFAIL_MSG( wxS( "Invalid pin orientation" ) );
242 return SIDE_LEFT;
243 }
244 }
245
249 unsigned pinsOnSide( SIDE aSide )
250 {
251 unsigned pin_count = 0;
252
253 for( SCH_PIN* each_pin : m_symbol->GetPins() )
254 {
255 if( !each_pin->IsVisible() && !m_is_power_symbol )
256 continue;
257
258 if( getPinSide( each_pin ) == aSide )
259 ++pin_count;
260 }
261
262 return pin_count;
263 }
264
269 void getPossibleCollisions( std::vector<SCH_ITEM*>& aItems )
270 {
271 wxCHECK_RET( m_screen, wxS( "getPossibleCollisions() with null m_screen" ) );
272
273 BOX2I symbolBox = m_symbol->GetBodyAndPinsBoundingBox();
274 std::vector<SIDE_AND_NPINS> sides = getPreferredSides();
275
276 for( SIDE_AND_NPINS& side : sides )
277 {
278 BOX2I box( fieldBoxPlacement( side ), m_fbox_size );
279 box.Merge( symbolBox );
280
281 for( SCH_ITEM* item : m_screen->Items().Overlapping( box ) )
282 {
283 if( SCH_SYMBOL* candidate = dynamic_cast<SCH_SYMBOL*>( item ) )
284 {
285 if( candidate == m_symbol )
286 continue;
287
288 std::vector<SCH_FIELD*> fields;
289 candidate->GetFields( fields, /* aVisibleOnly */ true );
290
291 for( SCH_FIELD* field : fields )
292 aItems.push_back( field );
293 }
294
295 aItems.push_back( item );
296 }
297 }
298 }
299
304 std::vector<SCH_ITEM*> filterCollisions( const BOX2I& aRect )
305 {
306 std::vector<SCH_ITEM*> filtered;
307
308 for( SCH_ITEM* item : m_colliders )
309 {
310 BOX2I item_box;
311
312 if( SCH_SYMBOL* item_comp = dynamic_cast<SCH_SYMBOL*>( item ) )
313 item_box = item_comp->GetBodyAndPinsBoundingBox();
314 else
315 item_box = item->GetBoundingBox();
316
317 if( item_box.Intersects( aRect ) )
318 filtered.push_back( item );
319 }
320
321 return filtered;
322 }
323
328 std::vector<SIDE_AND_NPINS> getPreferredSides()
329 {
330 SIDE_AND_NPINS sides_init[] = {
335 };
336 std::vector<SIDE_AND_NPINS> sides( sides_init, sides_init + arrayDim( sides_init ) );
337
338 int orient = m_symbol->GetOrientation();
339 int orient_angle = orient & 0xff; // enum is a bitmask
340 bool h_mirrored = ( ( orient & SYM_MIRROR_X )
341 && ( orient_angle == SYM_ORIENT_0 || orient_angle == SYM_ORIENT_180 ) );
342 double w = double( m_symbol_bbox.GetWidth() );
343 double h = double( m_symbol_bbox.GetHeight() );
344
345 // The preferred-sides heuristics are a bit magical. These were determined mostly
346 // by trial and error.
347
349 {
350 // For power symbols, we generally want the label at the top first.
351 switch( orient_angle )
352 {
353 case SYM_ORIENT_0:
354 std::swap( sides[0], sides[1] );
355 std::swap( sides[1], sides[3] );
356 // TOP, BOTTOM, RIGHT, LEFT
357 break;
358 case SYM_ORIENT_90:
359 std::swap( sides[0], sides[2] );
360 std::swap( sides[1], sides[2] );
361 // LEFT, RIGHT, TOP, BOTTOM
362 break;
363 case SYM_ORIENT_180:
364 std::swap( sides[0], sides[3] );
365 // BOTTOM, TOP, LEFT, RIGHT
366 break;
367 case SYM_ORIENT_270:
368 std::swap( sides[1], sides[2] );
369 // RIGHT, LEFT, TOP, BOTTOM
370 break;
371 }
372 }
373 else
374 {
375 // If the symbol is horizontally mirrored, swap left and right
376 if( h_mirrored )
377 {
378 std::swap( sides[0], sides[2] );
379 }
380
381 // If the symbol is very long or is a power symbol, swap H and V
382 if( w/h > 3.0 )
383 {
384 std::swap( sides[0], sides[1] );
385 std::swap( sides[1], sides[3] );
386 }
387 }
388
389 return sides;
390 }
391
396 {
397 if( !m_screen )
398 return BOX2I();
399
400 const PAGE_INFO& pageInfo = m_screen->GetPageSettings();
402
403 int pageWidth = pageInfo.GetWidthIU( schIUScale.IU_PER_MILS );
404 int pageHeight = pageInfo.GetHeightIU( schIUScale.IU_PER_MILS );
405
406 int leftMargin = schIUScale.mmToIU( dsModel.GetLeftMargin() );
407 int rightMargin = schIUScale.mmToIU( dsModel.GetRightMargin() );
408 int topMargin = schIUScale.mmToIU( dsModel.GetTopMargin() );
409 int bottomMargin = schIUScale.mmToIU( dsModel.GetBottomMargin() );
410
411 BOX2I drawableArea;
412 drawableArea.SetOrigin( leftMargin, topMargin );
413 drawableArea.SetEnd( pageWidth - rightMargin, pageHeight - bottomMargin );
414
415 return drawableArea;
416 }
417
421 std::vector<SIDE_AND_COLL> getCollidingSides()
422 {
423 SIDE sides_init[] = { SIDE_RIGHT, SIDE_TOP, SIDE_LEFT, SIDE_BOTTOM };
424 std::vector<SIDE> sides( sides_init, sides_init + arrayDim( sides_init ) );
425 std::vector<SIDE_AND_COLL> colliding;
426
427 BOX2I drawableArea = getDrawableArea();
428 bool checkDrawableArea = ( drawableArea.GetWidth() > 0 && drawableArea.GetHeight() > 0 );
429
430 // Iterate over all sides and find the ones that collide
431 for( SIDE side : sides )
432 {
433 SIDE_AND_NPINS sideandpins;
434 sideandpins.side = side;
435 sideandpins.pins = pinsOnSide( side );
436
437 BOX2I box( fieldBoxPlacement( sideandpins ), m_fbox_size );
438
439 COLLISION collision = COLLIDE_NONE;
440
441 // Check collision with drawing sheet boundary
442 if( checkDrawableArea && !drawableArea.Contains( box ) )
443 collision = COLLIDE_OBJECTS;
444
445 for( SCH_ITEM* collider : filterCollisions( box ) )
446 {
447 SCH_LINE* line = dynamic_cast<SCH_LINE*>( collider );
448
449 if( line && !side.x )
450 {
451 VECTOR2I start = line->GetStartPoint(), end = line->GetEndPoint();
452
453 if( start.y == end.y && collision != COLLIDE_OBJECTS )
454 collision = COLLIDE_H_WIRES;
455 else
456 collision = COLLIDE_OBJECTS;
457 }
458 else
459 {
460 collision = COLLIDE_OBJECTS;
461 }
462 }
463
464 if( collision != COLLIDE_NONE )
465 colliding.push_back( { side, collision } );
466 }
467
468 return colliding;
469 }
470
475 SIDE_AND_NPINS chooseSideFiltered( std::vector<SIDE_AND_NPINS>& aSides,
476 const std::vector<SIDE_AND_COLL>& aCollidingSides,
477 COLLISION aCollision,
478 SIDE_AND_NPINS aLastSelection)
479 {
480 SIDE_AND_NPINS sel = aLastSelection;
481
482 std::vector<SIDE_AND_NPINS>::iterator it = aSides.begin();
483
484 while( it != aSides.end() )
485 {
486 bool collide = false;
487
488 for( SIDE_AND_COLL collision : aCollidingSides )
489 {
490 if( collision.side == it->side && collision.collision == aCollision )
491 collide = true;
492 }
493
494 if( !collide )
495 {
496 ++it;
497 }
498 else
499 {
500 if( it->pins <= sel.pins )
501 {
502 sel.pins = it->pins;
503 sel.side = it->side;
504 }
505
506 it = aSides.erase( it );
507 }
508 }
509
510 return sel;
511 }
512
518 SIDE_AND_NPINS chooseSideForFields( bool aAvoidCollisions )
519 {
520 std::vector<SIDE_AND_NPINS> sides = getPreferredSides();
521
522 std::reverse( sides.begin(), sides.end() );
523 SIDE_AND_NPINS side = { VECTOR2I( 1, 0 ), UINT_MAX };
524
525 if( aAvoidCollisions )
526 {
527 std::vector<SIDE_AND_COLL> colliding_sides = getCollidingSides();
528 side = chooseSideFiltered( sides, colliding_sides, COLLIDE_OBJECTS, side );
529 side = chooseSideFiltered( sides, colliding_sides, COLLIDE_H_WIRES, side );
530 }
531
532 for( SIDE_AND_NPINS& each_side : sides | boost::adaptors::reversed )
533 {
534 if( !each_side.pins ) return each_side;
535 }
536
537 for( SIDE_AND_NPINS& each_side : sides )
538 {
539 if( each_side.pins <= side.pins )
540 {
541 side.pins = each_side.pins;
542 side.side = each_side.side;
543 }
544 }
545
546 return side;
547 }
548
554 void justifyField( SCH_FIELD* aField, SIDE aFieldSide )
555 {
556 // Justification is set twice to allow IsHorizJustifyFlipped() to work correctly.
557 aField->SetHorizJustify( ToHAlignment( -aFieldSide.x ) );
558 if( aField->IsHorizJustifyFlipped() )
560
562 }
563
568 {
569 VECTOR2I fbox_center = m_symbol_bbox.Centre();
570 int offs_x = ( m_symbol_bbox.GetWidth() + m_fbox_size.x ) / 2;
571 int offs_y = ( m_symbol_bbox.GetHeight() + m_fbox_size.y ) / 2;
572
573 if( aFieldSideAndPins.side.x != 0 )
574 offs_x += HPADDING;
575 else if( aFieldSideAndPins.side.y != 0 )
576 offs_y += VPADDING;
577
578 fbox_center.x += aFieldSideAndPins.side.x * offs_x;
579 fbox_center.y += aFieldSideAndPins.side.y * offs_y;
580
581 int x = fbox_center.x - ( m_fbox_size.x / 2 );
582 int y = fbox_center.y - ( m_fbox_size.y / 2 );
583
584 auto getPinsBox =
585 [&]( const VECTOR2I& aSide )
586 {
587 BOX2I pinsBox;
588
589 for( SCH_PIN* each_pin : m_symbol->GetPins() )
590 {
591 if( !each_pin->IsVisible() && !m_is_power_symbol )
592 continue;
593
594 if( getPinSide( each_pin ) == aSide )
595 pinsBox.Merge( each_pin->GetBoundingBox() );
596 }
597
598 return pinsBox;
599 };
600
601 if( aFieldSideAndPins.pins > 0 )
602 {
603 BOX2I pinsBox = getPinsBox( aFieldSideAndPins.side );
604
605 if( aFieldSideAndPins.side == SIDE_TOP || aFieldSideAndPins.side == SIDE_BOTTOM )
606 {
607 x = pinsBox.GetRight() + ( HPADDING * 2 );
608 }
609 else if( aFieldSideAndPins.side == SIDE_RIGHT || aFieldSideAndPins.side == SIDE_LEFT )
610 {
611 y = pinsBox.GetTop() - ( m_fbox_size.y + ( VPADDING * 2 ) );
612 }
613 }
614
615 return VECTOR2I( x, y );
616 }
617
622 bool fitFieldsBetweenWires( BOX2I* aBox, SIDE aSide )
623 {
624 if( aSide != SIDE_TOP && aSide != SIDE_BOTTOM )
625 return false;
626
627 std::vector<SCH_ITEM*> colliders = filterCollisions( *aBox );
628
629 if( colliders.empty() )
630 return false;
631
632 // Find the offset of the wires for proper positioning
633 int offset = 0;
634
635 for( SCH_ITEM* item : colliders )
636 {
637 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
638
639 if( !line )
640 return false;
641
642 VECTOR2I start = line->GetStartPoint(), end = line->GetEndPoint();
643
644 if( start.y != end.y )
645 return false;
646
647 int this_offset = (3 * WIRE_V_SPACING / 2) - ( start.y % WIRE_V_SPACING );
648
649 if( offset == 0 )
650 offset = this_offset;
651 else if( offset != this_offset )
652 return false;
653 }
654
655 // At this point we are recomputing the field box size. Do not
656 // return false after this point.
657 m_fbox_size = computeFBoxSize( /* aDynamic */ false );
658
659 VECTOR2I pos = aBox->GetPosition();
660
661 pos.y = round_n( pos.y, WIRE_V_SPACING, aSide == SIDE_BOTTOM );
662
663 aBox->SetOrigin( pos );
664 return true;
665 }
666
675 int fieldHPlacement( SCH_FIELD* aField, const BOX2I& aFieldBox )
676 {
677 int field_hjust;
678 int field_xcoord;
679
680 if( aField->IsHorizJustifyFlipped() )
681 field_hjust = -aField->GetHorizJustify();
682 else
683 field_hjust = aField->GetHorizJustify();
684
685 switch( field_hjust )
686 {
688 field_xcoord = aFieldBox.GetLeft();
689 break;
691 field_xcoord = aFieldBox.Centre().x;
692 break;
694 field_xcoord = aFieldBox.GetRight();
695 break;
696 default:
697 wxFAIL_MSG( wxS( "Unexpected value for SCH_FIELD::GetHorizJustify()" ) );
698 field_xcoord = aFieldBox.Centre().x; // Most are centered
699 }
700
701 return field_xcoord;
702 }
703
715 int fieldVPlacement( SCH_FIELD* aField, const BOX2I& aFieldBox, int* aAccumulatedPosition,
716 bool aDynamic )
717 {
718 int field_height;
719 int padding;
720
721 if( !aDynamic )
722 {
723 field_height = WIRE_V_SPACING / 2;
724 padding = WIRE_V_SPACING / 2;
725 }
726 else if( m_align_to_grid )
727 {
728 field_height = aField->GetBoundingBox().GetHeight();
729 padding = round_n( field_height, schIUScale.MilsToIU( 50 ), true ) - field_height;
730 }
731 else
732 {
733 field_height = aField->GetBoundingBox().GetHeight();
734 padding = FIELD_PADDING;
735 }
736
737 int placement = *aAccumulatedPosition + padding / 2 + field_height / 2;
738
739 *aAccumulatedPosition += padding + field_height;
740
741 return placement;
742 }
743
744private:
747 std::vector<SCH_FIELD*> m_fields;
748 std::vector<SCH_ITEM*> m_colliders;
755};
756
757
762
763
765{
766 if( aAlgo == AUTOPLACE_MANUAL )
767 wxASSERT_MSG( aScreen, wxS( "A SCH_SCREEN ptr must be given for manual autoplacement" ) );
768
769 AUTOPLACER autoplacer( this, aScreen );
770 autoplacer.DoAutoplace( aAlgo );
771
772 switch( aAlgo )
773 {
776 default: wxFAIL_MSG( "Unknown autoplace algorithm" ); break;
777 }
778}
779
780
782{
783 if( aAlgo == AUTOPLACE_MANUAL )
784 wxFAIL_MSG( wxS( "Manual autoplacement not supported for LIB_SYMBOLs" ) );
785
786 AUTOPLACER autoplacer( this, aScreen );
787 autoplacer.DoAutoplace( aAlgo );
788
789 switch( aAlgo )
790 {
793 default: wxFAIL_MSG( "Unknown autoplace algorithm" ); break;
794 }
795}
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:123
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
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:207
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:233
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr Vec Centre() const
Definition box2.h:93
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:654
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr coord_type GetLeft() const
Definition box2.h:224
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:164
constexpr coord_type GetRight() const
Definition box2.h:213
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:293
constexpr coord_type GetTop() const
Definition box2.h:225
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:307
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:412
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:221
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:404
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:91
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
Gets the page height in IU.
Definition page_info.h:164
int GetWidthIU(double aIUScale) const
Gets the page width in IU.
Definition page_info.h:155
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:162
AUTOPLACE_ALGO m_fieldsAutoplaced
Definition sch_item.h:779
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:38
VECTOR2I GetEndPoint() const
Definition sch_line.h:144
VECTOR2I GetStartPoint() const
Definition sch_line.h:135
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:1291
Schematic symbol object.
Definition sch_symbol.h:69
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:59
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:408
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:407
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:65
@ AUTOPLACE_MANUAL
Definition sch_item.h:68
@ AUTOPLACE_AUTO
Definition sch_item.h:67
bool collide(T aObject, U aAnotherObject, int aLayer, int aMinDistance)
Used by SHAPE_INDEX to implement Query().
Definition shape_index.h:93
@ SYM_ORIENT_270
Definition symbol.h:38
@ SYM_ORIENT_180
Definition symbol.h:37
@ SYM_MIRROR_X
Definition symbol.h:39
@ SYM_ORIENT_90
Definition symbol.h:36
@ SYM_ORIENT_0
Definition symbol.h:35
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:683