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, 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
56#include <sch_edit_frame.h>
57#include <sch_line.h>
58#include <kiface_base.h>
59#include <algorithm>
60#include <tool/tool_manager.h>
61#include <core/arraydim.h>
62
63#define FIELD_PADDING schIUScale.MilsToIU( 15 ) // arbitrarily chosen for aesthetics
64#define WIRE_V_SPACING schIUScale.MilsToIU( 100 )
65#define HPADDING schIUScale.MilsToIU( 25 ) // arbitrarily chosen for aesthetics
66#define VPADDING schIUScale.MilsToIU( 15 ) // arbitrarily chosen for aesthetics
67
71template<typename T> T round_n( const T& value, const T& n, bool aRoundUp )
72{
73 if( value % n )
74 return n * (value / n + (aRoundUp ? 1 : 0));
75 else
76 return value;
77}
78
79
81{
82public:
83 typedef VECTOR2I SIDE;
86
88 {
90 unsigned pins;
91 };
92
98
99 AUTOPLACER( SYMBOL* aSymbol, SCH_SCREEN* aScreen ) :
100 m_screen( aScreen ),
101 m_symbol( aSymbol ),
102 m_is_power_symbol( false )
103 {
104 m_symbol->GetFields( m_fields, /* aVisibleOnly */ true );
105
106 auto cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
107 wxASSERT( cfg );
108
109 m_allow_rejustify = false;
110 m_align_to_grid = true;
111
112 if( cfg )
113 {
114 m_allow_rejustify = cfg->m_AutoplaceFields.allow_rejustify;
115 m_align_to_grid = cfg->m_AutoplaceFields.align_to_grid;
116 }
117
118 m_symbol_bbox = m_symbol->GetBodyBoundingBox();
119 m_fbox_size = computeFBoxSize( /* aDynamic */ true );
120
121 if( SCH_SYMBOL* schSymbol = dynamic_cast<SCH_SYMBOL*>( m_symbol ) )
122 m_is_power_symbol = !schSymbol->IsInNetlist();
123
124 if( aScreen )
126 }
127
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
153 {
154 if( sideandpins.pins > 0 )
155 {
156 if( field_side == SIDE_TOP || field_side == SIDE_BOTTOM )
157 justifyField( field, SIDE_RIGHT );
158 else
159 justifyField( field, SIDE_TOP );
160 }
161 else
162 {
163 justifyField( field, field_side );
164 }
165 }
166
167 VECTOR2I pos( fieldHPlacement( field, field_box ),
168 fieldVPlacement( field, field_box, &last_y_coord, !forceWireSpacing ) );
169
170 if( m_align_to_grid )
171 {
172 if( abs( field_side.x ) > 0 )
173 pos.x = round_n( pos.x, schIUScale.MilsToIU( 50 ), field_side.x >= 0 );
174
175 if( abs( field_side.y ) > 0 )
176 pos.y = round_n( pos.y, schIUScale.MilsToIU( 50 ), field_side.y >= 0 );
177 }
178
179 field->SetPosition( pos );
180 }
181 }
182
183protected:
188 VECTOR2I computeFBoxSize( bool aDynamic )
189 {
190 int max_field_width = 0;
191 int total_height = 0;
192
193 for( SCH_FIELD* field : m_fields )
194 {
195 if( !field->IsVisible() || !field->CanAutoplace() )
196 {
197 continue;
198 }
199
200 // Set field angle to HORIZONTAL. GetBoundingBox() applies both the field's text
201 // angle and the symbol transform. For 90/270 degree rotated symbols, this results
202 // in vertical text display. Previously, setting VERTICAL for rotated symbols
203 // caused 180-degree effective rotation (field angle + symbol transform), which
204 // resulted in incorrect bounding box dimensions and field overlap issues.
205 field->SetTextAngle( ANGLE_HORIZONTAL );
206
207 BOX2I bbox = field->GetBoundingBox();
208 int field_width = bbox.GetWidth();
209 int field_height = bbox.GetHeight();
210
211 max_field_width = std::max( max_field_width, field_width );
212
213 if( !aDynamic )
214 total_height += WIRE_V_SPACING;
215 else if( m_align_to_grid )
216 total_height += round_n( field_height, schIUScale.MilsToIU( 50 ), true );
217 else
218 total_height += field_height + FIELD_PADDING;
219 }
220
221 return VECTOR2I( max_field_width, total_height );
222 }
223
228 {
229 PIN_ORIENTATION pin_orient = aPin->PinDrawOrient( m_symbol->GetTransform() );
230
231 switch( pin_orient )
232 {
237 default:
238 wxFAIL_MSG( wxS( "Invalid pin orientation" ) );
239 return SIDE_LEFT;
240 }
241 }
242
246 unsigned pinsOnSide( SIDE aSide )
247 {
248 unsigned pin_count = 0;
249
250 for( SCH_PIN* each_pin : m_symbol->GetPins() )
251 {
252 if( !each_pin->IsVisible() && !m_is_power_symbol )
253 continue;
254
255 if( getPinSide( each_pin ) == aSide )
256 ++pin_count;
257 }
258
259 return pin_count;
260 }
261
266 void getPossibleCollisions( std::vector<SCH_ITEM*>& aItems )
267 {
268 wxCHECK_RET( m_screen, wxS( "getPossibleCollisions() with null m_screen" ) );
269
270 BOX2I symbolBox = m_symbol->GetBodyAndPinsBoundingBox();
271 std::vector<SIDE_AND_NPINS> sides = getPreferredSides();
272
273 for( SIDE_AND_NPINS& side : sides )
274 {
275 BOX2I box( fieldBoxPlacement( side ), m_fbox_size );
276 box.Merge( symbolBox );
277
278 for( SCH_ITEM* item : m_screen->Items().Overlapping( box ) )
279 {
280 if( SCH_SYMBOL* candidate = dynamic_cast<SCH_SYMBOL*>( item ) )
281 {
282 if( candidate == m_symbol )
283 continue;
284
285 std::vector<SCH_FIELD*> fields;
286 candidate->GetFields( fields, /* aVisibleOnly */ true );
287
288 for( SCH_FIELD* field : fields )
289 aItems.push_back( field );
290 }
291
292 aItems.push_back( item );
293 }
294 }
295 }
296
301 std::vector<SCH_ITEM*> filterCollisions( const BOX2I& aRect )
302 {
303 std::vector<SCH_ITEM*> filtered;
304
305 for( SCH_ITEM* item : m_colliders )
306 {
307 BOX2I item_box;
308
309 if( SCH_SYMBOL* item_comp = dynamic_cast<SCH_SYMBOL*>( item ) )
310 item_box = item_comp->GetBodyAndPinsBoundingBox();
311 else
312 item_box = item->GetBoundingBox();
313
314 if( item_box.Intersects( aRect ) )
315 filtered.push_back( item );
316 }
317
318 return filtered;
319 }
320
325 std::vector<SIDE_AND_NPINS> getPreferredSides()
326 {
327 SIDE_AND_NPINS sides_init[] = {
332 };
333 std::vector<SIDE_AND_NPINS> sides( sides_init, sides_init + arrayDim( sides_init ) );
334
335 int orient = m_symbol->GetOrientation();
336 int orient_angle = orient & 0xff; // enum is a bitmask
337 bool h_mirrored = ( ( orient & SYM_MIRROR_X )
338 && ( orient_angle == SYM_ORIENT_0 || orient_angle == SYM_ORIENT_180 ) );
339 double w = double( m_symbol_bbox.GetWidth() );
340 double h = double( m_symbol_bbox.GetHeight() );
341
342 // The preferred-sides heuristics are a bit magical. These were determined mostly
343 // by trial and error.
344
346 {
347 // For power symbols, we generally want the label at the top first.
348 switch( orient_angle )
349 {
350 case SYM_ORIENT_0:
351 std::swap( sides[0], sides[1] );
352 std::swap( sides[1], sides[3] );
353 // TOP, BOTTOM, RIGHT, LEFT
354 break;
355 case SYM_ORIENT_90:
356 std::swap( sides[0], sides[2] );
357 std::swap( sides[1], sides[2] );
358 // LEFT, RIGHT, TOP, BOTTOM
359 break;
360 case SYM_ORIENT_180:
361 std::swap( sides[0], sides[3] );
362 // BOTTOM, TOP, LEFT, RIGHT
363 break;
364 case SYM_ORIENT_270:
365 std::swap( sides[1], sides[2] );
366 // RIGHT, LEFT, TOP, BOTTOM
367 break;
368 }
369 }
370 else
371 {
372 // If the symbol is horizontally mirrored, swap left and right
373 if( h_mirrored )
374 {
375 std::swap( sides[0], sides[2] );
376 }
377
378 // If the symbol is very long or is a power symbol, swap H and V
379 if( w/h > 3.0 )
380 {
381 std::swap( sides[0], sides[1] );
382 std::swap( sides[1], sides[3] );
383 }
384 }
385
386 return sides;
387 }
388
393 {
394 if( !m_screen )
395 return BOX2I();
396
397 const PAGE_INFO& pageInfo = m_screen->GetPageSettings();
399
400 int pageWidth = pageInfo.GetWidthIU( schIUScale.IU_PER_MILS );
401 int pageHeight = pageInfo.GetHeightIU( schIUScale.IU_PER_MILS );
402
403 int leftMargin = schIUScale.mmToIU( dsModel.GetLeftMargin() );
404 int rightMargin = schIUScale.mmToIU( dsModel.GetRightMargin() );
405 int topMargin = schIUScale.mmToIU( dsModel.GetTopMargin() );
406 int bottomMargin = schIUScale.mmToIU( dsModel.GetBottomMargin() );
407
408 BOX2I drawableArea;
409 drawableArea.SetOrigin( leftMargin, topMargin );
410 drawableArea.SetEnd( pageWidth - rightMargin, pageHeight - bottomMargin );
411
412 return drawableArea;
413 }
414
418 std::vector<SIDE_AND_COLL> getCollidingSides()
419 {
420 SIDE sides_init[] = { SIDE_RIGHT, SIDE_TOP, SIDE_LEFT, SIDE_BOTTOM };
421 std::vector<SIDE> sides( sides_init, sides_init + arrayDim( sides_init ) );
422 std::vector<SIDE_AND_COLL> colliding;
423
424 BOX2I drawableArea = getDrawableArea();
425 bool checkDrawableArea = ( drawableArea.GetWidth() > 0 && drawableArea.GetHeight() > 0 );
426
427 // Iterate over all sides and find the ones that collide
428 for( SIDE side : sides )
429 {
430 SIDE_AND_NPINS sideandpins;
431 sideandpins.side = side;
432 sideandpins.pins = pinsOnSide( side );
433
434 BOX2I box( fieldBoxPlacement( sideandpins ), m_fbox_size );
435
436 COLLISION collision = COLLIDE_NONE;
437
438 // Check collision with drawing sheet boundary
439 if( checkDrawableArea && !drawableArea.Contains( box ) )
440 collision = COLLIDE_OBJECTS;
441
442 for( SCH_ITEM* collider : filterCollisions( box ) )
443 {
444 SCH_LINE* line = dynamic_cast<SCH_LINE*>( collider );
445
446 if( line && !side.x )
447 {
448 VECTOR2I start = line->GetStartPoint(), end = line->GetEndPoint();
449
450 if( start.y == end.y && collision != COLLIDE_OBJECTS )
451 collision = COLLIDE_H_WIRES;
452 else
453 collision = COLLIDE_OBJECTS;
454 }
455 else
456 {
457 collision = COLLIDE_OBJECTS;
458 }
459 }
460
461 if( collision != COLLIDE_NONE )
462 colliding.push_back( { side, collision } );
463 }
464
465 return colliding;
466 }
467
472 SIDE_AND_NPINS chooseSideFiltered( std::vector<SIDE_AND_NPINS>& aSides,
473 const std::vector<SIDE_AND_COLL>& aCollidingSides,
474 COLLISION aCollision,
475 SIDE_AND_NPINS aLastSelection)
476 {
477 SIDE_AND_NPINS sel = aLastSelection;
478
479 std::vector<SIDE_AND_NPINS>::iterator it = aSides.begin();
480
481 while( it != aSides.end() )
482 {
483 bool collide = false;
484
485 for( SIDE_AND_COLL collision : aCollidingSides )
486 {
487 if( collision.side == it->side && collision.collision == aCollision )
488 collide = true;
489 }
490
491 if( !collide )
492 {
493 ++it;
494 }
495 else
496 {
497 if( it->pins <= sel.pins )
498 {
499 sel.pins = it->pins;
500 sel.side = it->side;
501 }
502
503 it = aSides.erase( it );
504 }
505 }
506
507 return sel;
508 }
509
515 SIDE_AND_NPINS chooseSideForFields( bool aAvoidCollisions )
516 {
517 std::vector<SIDE_AND_NPINS> sides = getPreferredSides();
518
519 std::reverse( sides.begin(), sides.end() );
520 SIDE_AND_NPINS side = { VECTOR2I( 1, 0 ), UINT_MAX };
521
522 if( aAvoidCollisions )
523 {
524 std::vector<SIDE_AND_COLL> colliding_sides = getCollidingSides();
525 side = chooseSideFiltered( sides, colliding_sides, COLLIDE_OBJECTS, side );
526 side = chooseSideFiltered( sides, colliding_sides, COLLIDE_H_WIRES, side );
527 }
528
529 for( SIDE_AND_NPINS& each_side : sides | boost::adaptors::reversed )
530 {
531 if( !each_side.pins ) return each_side;
532 }
533
534 for( SIDE_AND_NPINS& each_side : sides )
535 {
536 if( each_side.pins <= side.pins )
537 {
538 side.pins = each_side.pins;
539 side.side = each_side.side;
540 }
541 }
542
543 return side;
544 }
545
551 void justifyField( SCH_FIELD* aField, SIDE aFieldSide )
552 {
553 // Justification is set twice to allow IsHorizJustifyFlipped() to work correctly.
554 aField->SetHorizJustify( ToHAlignment( -aFieldSide.x ) );
555 if( aField->IsHorizJustifyFlipped() )
557
559 }
560
565 {
566 VECTOR2I fbox_center = m_symbol_bbox.Centre();
567 int offs_x = ( m_symbol_bbox.GetWidth() + m_fbox_size.x ) / 2;
568 int offs_y = ( m_symbol_bbox.GetHeight() + m_fbox_size.y ) / 2;
569
570 if( aFieldSideAndPins.side.x != 0 )
571 offs_x += HPADDING;
572 else if( aFieldSideAndPins.side.y != 0 )
573 offs_y += VPADDING;
574
575 fbox_center.x += aFieldSideAndPins.side.x * offs_x;
576 fbox_center.y += aFieldSideAndPins.side.y * offs_y;
577
578 int x = fbox_center.x - ( m_fbox_size.x / 2 );
579 int y = fbox_center.y - ( m_fbox_size.y / 2 );
580
581 auto getPinsBox =
582 [&]( const VECTOR2I& aSide )
583 {
584 BOX2I pinsBox;
585
586 for( SCH_PIN* each_pin : m_symbol->GetPins() )
587 {
588 if( !each_pin->IsVisible() && !m_is_power_symbol )
589 continue;
590
591 if( getPinSide( each_pin ) == aSide )
592 pinsBox.Merge( each_pin->GetBoundingBox() );
593 }
594
595 return pinsBox;
596 };
597
598 if( aFieldSideAndPins.pins > 0 )
599 {
600 BOX2I pinsBox = getPinsBox( aFieldSideAndPins.side );
601
602 if( aFieldSideAndPins.side == SIDE_TOP || aFieldSideAndPins.side == SIDE_BOTTOM )
603 {
604 x = pinsBox.GetRight() + ( HPADDING * 2 );
605 }
606 else if( aFieldSideAndPins.side == SIDE_RIGHT || aFieldSideAndPins.side == SIDE_LEFT )
607 {
608 y = pinsBox.GetTop() - ( m_fbox_size.y + ( VPADDING * 2 ) );
609 }
610 }
611
612 return VECTOR2I( x, y );
613 }
614
619 bool fitFieldsBetweenWires( BOX2I* aBox, SIDE aSide )
620 {
621 if( aSide != SIDE_TOP && aSide != SIDE_BOTTOM )
622 return false;
623
624 std::vector<SCH_ITEM*> colliders = filterCollisions( *aBox );
625
626 if( colliders.empty() )
627 return false;
628
629 // Find the offset of the wires for proper positioning
630 int offset = 0;
631
632 for( SCH_ITEM* item : colliders )
633 {
634 SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
635
636 if( !line )
637 return false;
638
639 VECTOR2I start = line->GetStartPoint(), end = line->GetEndPoint();
640
641 if( start.y != end.y )
642 return false;
643
644 int this_offset = (3 * WIRE_V_SPACING / 2) - ( start.y % WIRE_V_SPACING );
645
646 if( offset == 0 )
647 offset = this_offset;
648 else if( offset != this_offset )
649 return false;
650 }
651
652 // At this point we are recomputing the field box size. Do not
653 // return false after this point.
654 m_fbox_size = computeFBoxSize( /* aDynamic */ false );
655
656 VECTOR2I pos = aBox->GetPosition();
657
658 pos.y = round_n( pos.y, WIRE_V_SPACING, aSide == SIDE_BOTTOM );
659
660 aBox->SetOrigin( pos );
661 return true;
662 }
663
672 int fieldHPlacement( SCH_FIELD* aField, const BOX2I& aFieldBox )
673 {
674 int field_hjust;
675 int field_xcoord;
676
677 if( aField->IsHorizJustifyFlipped() )
678 field_hjust = -aField->GetHorizJustify();
679 else
680 field_hjust = aField->GetHorizJustify();
681
682 switch( field_hjust )
683 {
685 field_xcoord = aFieldBox.GetLeft();
686 break;
688 field_xcoord = aFieldBox.Centre().x;
689 break;
691 field_xcoord = aFieldBox.GetRight();
692 break;
693 default:
694 wxFAIL_MSG( wxS( "Unexpected value for SCH_FIELD::GetHorizJustify()" ) );
695 field_xcoord = aFieldBox.Centre().x; // Most are centered
696 }
697
698 return field_xcoord;
699 }
700
712 int fieldVPlacement( SCH_FIELD* aField, const BOX2I& aFieldBox, int* aAccumulatedPosition,
713 bool aDynamic )
714 {
715 int field_height;
716 int padding;
717
718 if( !aDynamic )
719 {
720 field_height = WIRE_V_SPACING / 2;
721 padding = WIRE_V_SPACING / 2;
722 }
723 else if( m_align_to_grid )
724 {
725 field_height = aField->GetBoundingBox().GetHeight();
726 padding = round_n( field_height, schIUScale.MilsToIU( 50 ), true ) - field_height;
727 }
728 else
729 {
730 field_height = aField->GetBoundingBox().GetHeight();
731 padding = FIELD_PADDING;
732 }
733
734 int placement = *aAccumulatedPosition + padding / 2 + field_height / 2;
735
736 *aAccumulatedPosition += padding + field_height;
737
738 return placement;
739 }
740
741private:
744 std::vector<SCH_FIELD*> m_fields;
745 std::vector<SCH_ITEM*> m_colliders;
751};
752
753
758
759
761{
762 if( aAlgo == AUTOPLACE_MANUAL )
763 wxASSERT_MSG( aScreen, wxS( "A SCH_SCREEN ptr must be given for manual autoplacement" ) );
764
765 AUTOPLACER autoplacer( this, aScreen );
766 autoplacer.DoAutoplace( aAlgo );
767
768 switch( aAlgo )
769 {
772 default: wxFAIL_MSG( "Unknown autoplace algorithm" ); break;
773 }
774}
775
776
778{
779 if( aAlgo == AUTOPLACE_MANUAL )
780 wxFAIL_MSG( wxS( "Manual autoplacement not supported for LIB_SYMBOLs" ) );
781
782 AUTOPLACER autoplacer( this, aScreen );
783 autoplacer.DoAutoplace( aAlgo );
784
785 switch( aAlgo )
786 {
789 default: wxFAIL_MSG( "Unknown autoplace algorithm" ); break;
790 }
791}
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
Definition arraydim.h:31
#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:114
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
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.
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:211
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:237
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr Vec Centre() const
Definition box2.h:97
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:658
constexpr size_type GetHeight() const
Definition box2.h:215
constexpr coord_type GetLeft() const
Definition box2.h:228
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:168
constexpr coord_type GetRight() const
Definition box2.h:217
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:297
constexpr coord_type GetTop() const
Definition box2.h:229
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:311
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:429
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:200
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:421
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:95
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:79
int GetHeightIU(double aIUScale) const
Gets the page height in IU.
Definition page_info.h:168
int GetWidthIU(double aIUScale) const
Gets the page width in IU.
Definition page_info.h:159
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:167
AUTOPLACE_ALGO m_fieldsAutoplaced
Definition sch_item.h:776
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
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:1182
Schematic symbol object.
Definition sch_symbol.h:76
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:63
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:407
PIN_ORIENTATION
The symbol library pin object orientations.
Definition pin_type.h:105
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:127
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:111
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:118
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:135
AUTOPLACE_ALGO
Definition sch_item.h:68
@ AUTOPLACE_MANUAL
Definition sch_item.h:71
@ AUTOPLACE_AUTO
Definition sch_item.h:70
bool collide(T aObject, U aAnotherObject, int aLayer, int aMinDistance)
Used by SHAPE_INDEX to implement Query().
Definition shape_index.h:97
@ SYM_ORIENT_270
Definition symbol.h:42
@ SYM_ORIENT_180
Definition symbol.h:41
@ SYM_MIRROR_X
Definition symbol.h:43
@ SYM_ORIENT_90
Definition symbol.h:40
@ SYM_ORIENT_0
Definition symbol.h:39
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:695