KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_painter.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) 2014 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Tomasz Wlostowski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27
28#include <trigo.h>
29#include <chrono>
30#include <bitmap_base.h>
31#include <connection_graph.h>
33#include <sch_netchain.h>
34#include <callback_gal.h>
36#include <geometry/shape_rect.h>
37#include <geometry/roundrect.h>
41#include <gr_text.h>
42#include <sch_pin.h>
43#include <math/util.h>
44#include <pin_layout_cache.h>
45#include <pgm_base.h>
46#include <sch_bitmap.h>
47#include <sch_bus_entry.h>
48#include <sch_symbol.h>
49#include <sch_edit_frame.h>
50#include <sch_field.h>
51#include <sch_group.h>
52#include <sch_junction.h>
53#include <sch_line.h>
54#include <sch_shape.h>
55#include <sch_marker.h>
56#include <sch_no_connect.h>
57#include <sch_sheet.h>
58#include <sch_sheet_pin.h>
59#include <sch_text.h>
60#include <sch_label.h>
61#include <sch_textbox.h>
62#include <sch_table.h>
63#include <schematic.h>
65#include <trace_helpers.h>
66#include <view/view.h>
67#include <kiface_base.h>
68#include <default_values.h>
69#include <advanced_config.h>
71#include <stroke_params.h>
72#include <string_utils.h>
73#include "sch_painter.h"
74#include "common.h"
75
77
78namespace KIGFX
79{
80
82{
83 return dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() );
84}
85
86
87std::vector<KICAD_T> SCH_PAINTER::g_ScaledSelectionTypes = {
106};
107
108
110 KIGFX::PAINTER( aGal ),
111 m_schematic( nullptr )
112{ }
113
114
115bool SCH_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
116{
117 const EDA_ITEM* item = dynamic_cast<const EDA_ITEM*>( aItem );
118
119 if( !item )
120 return false;
121
122 draw( item, aLayer, false );
123
124 return false;
125}
126
127
128void SCH_PAINTER::draw( const EDA_ITEM* aItem, int aLayer, bool aDimmed )
129{
130
131#ifdef CONNECTIVITY_DEBUG
132
133 auto sch_item = dynamic_cast<const SCH_ITEM*>( aItem );
134 auto conn = sch_item ? sch_item->Connection( *g_CurrentSheet ) : nullptr;
135
136 if( conn )
137 {
138 auto pos = aItem->GetBoundingBox().Centre();
139 auto label = conn->Name( true );
140
141 m_canvas->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER );
142 m_canvas->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER );
143 m_canvas->SetStrokeColor( COLOR4D( LIGHTRED ) );
144 m_canvas->SetLineWidth( Mils2ui( 2 ) );
145 m_canvas->SetGlyphSize( VECTOR2D( Mils2ui( 20 ), Mils2ui( 20 ) ) );
146 m_canvas->StrokeText( *m_canvas, conn->Name( true ), pos, 0.0, 0 );
147 }
148
149#endif
150
151 // Enable draw bounding box on request. Some bboxes are handled locally.
152 bool drawBoundingBox = m_schSettings.GetDrawBoundingBoxes();
153
154 switch( aItem->Type() )
155 {
156 case LIB_SYMBOL_T:
157 draw( static_cast<const LIB_SYMBOL*>( aItem ), aLayer );
158 break;
159 case SCH_PIN_T:
160 drawBoundingBox = false;
161 draw( static_cast<const SCH_PIN*>( aItem ), aLayer, aDimmed );
162 break;
163 case SCH_SYMBOL_T:
164 draw( static_cast<const SCH_SYMBOL*>( aItem ), aLayer );
165 break;
166 case SCH_JUNCTION_T:
167 draw( static_cast<const SCH_JUNCTION*>( aItem ), aLayer );
168 break;
169 case SCH_LINE_T:
170 draw( static_cast<const SCH_LINE*>( aItem ), aLayer );
171 break;
172 case SCH_SHAPE_T:
173 draw( static_cast<const SCH_SHAPE*>( aItem ), aLayer, aDimmed );
174 break;
175 case SCH_RULE_AREA_T:
176 draw( static_cast<const SCH_SHAPE*>( aItem ), aLayer, aDimmed );
177 break;
178 case SCH_TEXT_T:
179 draw( static_cast<const SCH_TEXT*>( aItem ), aLayer, aDimmed );
180 break;
181 case SCH_TEXTBOX_T:
182 draw( static_cast<const SCH_TEXTBOX*>( aItem ), aLayer, aDimmed );
183 break;
184 case SCH_TABLE_T:
185 draw( static_cast<const SCH_TABLE*>( aItem ), aLayer, aDimmed );
186 break;
187 case SCH_LABEL_T:
188 draw( static_cast<const SCH_LABEL*>( aItem ), aLayer, aDimmed );
189 break;
191 draw( static_cast<const SCH_DIRECTIVE_LABEL*>( aItem ), aLayer, aDimmed );
192 break;
193 case SCH_FIELD_T:
194 draw( static_cast<const SCH_FIELD*>( aItem ), aLayer, aDimmed );
195 break;
196 case SCH_HIER_LABEL_T:
197 draw( static_cast<const SCH_HIERLABEL*>( aItem ), aLayer, aDimmed );
198 break;
200 draw( static_cast<const SCH_GLOBALLABEL*>( aItem ), aLayer, aDimmed );
201 break;
202 case SCH_SHEET_T:
203 draw( static_cast<const SCH_SHEET*>( aItem ), aLayer );
204 break;
205 case SCH_SHEET_PIN_T:
206 draw( static_cast<const SCH_HIERLABEL*>( aItem ), aLayer, aDimmed );
207 break;
208 case SCH_NO_CONNECT_T:
209 draw( static_cast<const SCH_NO_CONNECT*>( aItem ), aLayer );
210 break;
212 draw( static_cast<const SCH_BUS_ENTRY_BASE*>( aItem ), aLayer );
213 break;
215 draw( static_cast<const SCH_BUS_ENTRY_BASE*>( aItem ), aLayer );
216 break;
217 case SCH_BITMAP_T:
218 draw( static_cast<const SCH_BITMAP*>( aItem ), aLayer );
219 break;
220 case SCH_MARKER_T:
221 draw( static_cast<const SCH_MARKER*>( aItem ), aLayer );
222 break;
223 case SCH_GROUP_T:
224 draw( static_cast<const SCH_GROUP*>( aItem ), aLayer );
225 break;
226 default:
227 return;
228 }
229
230 if( drawBoundingBox )
231 drawItemBoundingBox( aItem );
232}
233
234
236{
237 if( const SCH_ITEM* item = dynamic_cast<const SCH_ITEM*>( aItem ) )
238 {
239 if( item->IsPrivate() && !m_schSettings.m_IsSymbolEditor )
240 return;
241 }
242
243 BOX2I box = aItem->GetBoundingBox();
244
245 if( aItem->Type() == SCH_SYMBOL_T )
246 box = static_cast<const SCH_SYMBOL*>( aItem )->GetBodyBoundingBox();
247
248 m_gal->SetIsFill( false );
249 m_gal->SetIsStroke( true );
250 m_gal->SetStrokeColor( aItem->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 )
251 : COLOR4D( 0.2, 0.2, 0.2, 1 ) );
252 m_gal->SetLineWidth( schIUScale.MilsToIU( 3 ) );
253 m_gal->DrawRectangle( box.GetOrigin(), box.GetEnd() );
254}
255
256
258{
259 // TODO: it would be nice to have a more definitive test for this, but we've currently got
260 // no access to the VIEW_GROUP to see if it's cached or not.
261 return aItem->IsSelected();
262}
263
264
266{
267 if( m_schSettings.m_ShowUnit // showing a specific unit
268 && aItem->GetUnit() // item is unit-specific
269 && aItem->GetUnit() != m_schSettings.m_ShowUnit )
270 {
271 return false;
272 }
273
274 if( m_schSettings.m_ShowBodyStyle // showing a specific body style
275 && aItem->GetBodyStyle() // item is body-style-specific
276 && aItem->GetBodyStyle() != m_schSettings.m_ShowBodyStyle )
277 {
278 return false;
279 }
280
281 return true;
282}
283
284
286{
287 if( KIFONT::FONT* font = aItem->GetDrawFont( &m_schSettings ) )
288 return font;
289
290 return KIFONT::FONT::GetFont( m_schSettings.GetDefaultFont(), aItem->IsBold(), aItem->IsItalic() );
291}
292
293
294float SCH_PAINTER::getShadowWidth( bool aForHighlight ) const
295{
296 const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix();
297
298 int milsWidth = aForHighlight ? eeconfig()->m_Selection.highlight_thickness
300
301 // For best visuals the selection width must be a cross between the zoom level and the
302 // default line width.
303 return (float) std::fabs( matrix.GetScale().x * milsWidth ) + schIUScale.MilsToIU( milsWidth );
304}
305
306
307COLOR4D SCH_PAINTER::getRenderColor( const SCH_ITEM* aItem, int aLayer, bool aDrawingShadows,
308 bool aDimmed, bool aIgnoreNets ) const
309{
310 auto isBackgroundLayer =
311 []( int layer )
312 {
313 return layer == LAYER_DEVICE_BACKGROUND || layer == LAYER_NOTES_BACKGROUND
314 || layer == LAYER_SHAPES_BACKGROUND || layer == LAYER_SHEET_BACKGROUND;
315 };
316
317 COLOR4D color = m_schSettings.GetLayerColor( aLayer );
318
319 // Graphic items of a SYMBOL frequently use the LAYER_DEVICE layer color
320 // (i.e. when no specific color is set)
321 bool isSymbolChild = aItem->GetParentSymbol() != nullptr;
322
323 if( !m_schSettings.m_OverrideItemColors )
324 {
325 if( aItem->Type() == SCH_LINE_T )
326 {
327 color = static_cast<const SCH_LINE*>( aItem )->GetLineColor();
328 }
329 else if( aItem->Type() == SCH_BUS_WIRE_ENTRY_T )
330 {
331 color = static_cast<const SCH_BUS_WIRE_ENTRY*>( aItem )->GetBusEntryColor();
332 }
333 else if( aItem->Type() == SCH_JUNCTION_T )
334 {
335 color = static_cast<const SCH_JUNCTION*>( aItem )->GetJunctionColor();
336 }
337 else if( aItem->Type() == SCH_SHEET_T )
338 {
339 const SCH_SHEET* sheet = static_cast<const SCH_SHEET*>( aItem );
340
341 if( isBackgroundLayer( aLayer ) )
342 color = sheet->GetBackgroundColor();
343 else
344 color = sheet->GetBorderColor();
345 }
346 else if( aItem->Type() == SCH_SHAPE_T || aItem->Type() == SCH_RULE_AREA_T )
347 {
348 const SCH_SHAPE* shape = static_cast<const SCH_SHAPE*>( aItem );
349
350 if( isBackgroundLayer( aLayer ) )
351 {
352 switch( shape->GetFillMode() )
353 {
354 case FILL_T::NO_FILL:
355 break;
356
358 color = shape->GetStroke().GetColor();
359 break;
360
361 case FILL_T::HATCH:
365 color = shape->GetFillColor();
366 break;
367
369 color = m_schSettings.GetLayerColor( LAYER_DEVICE_BACKGROUND );
370 break;
371
372 default:
373 wxFAIL_MSG( wxT( "Unsupported fill type" ) );
374 }
375
376 // A filled shape means filled; if they didn't specify a fill colour then use
377 // the border colour.
378 if( shape->GetFillMode() != FILL_T::NO_FILL && color == COLOR4D::UNSPECIFIED )
379 {
380 if( aItem->Type() == SCH_RULE_AREA_T )
381 color = m_schSettings.GetLayerColor( LAYER_RULE_AREAS );
382 else if( isSymbolChild )
383 color = m_schSettings.GetLayerColor( LAYER_DEVICE );
384 else
385 color = m_schSettings.GetLayerColor( LAYER_NOTES );
386 }
387 }
388 else
389 {
390 color = shape->GetStroke().GetColor();
391 }
392 }
393 else if( aItem->IsType( { SCH_LABEL_LOCATE_ANY_T } ) )
394 {
395 const SCH_LABEL_BASE* label = static_cast<const SCH_LABEL_BASE*>( aItem );
396
397 if( label->GetTextColor() != COLOR4D::UNSPECIFIED )
398 color = label->GetTextColor(); // override color
399 else if( aIgnoreNets )
400 color = m_schSettings.GetLayerColor( aLayer ); // layer color
401 else
402 color = label->GetLabelColor(); // net/netclass color
403 }
404 else if( aItem->Type() == SCH_FIELD_T )
405 {
406 color = static_cast<const SCH_FIELD*>( aItem )->GetFieldColor();
407 }
408 else if( aItem->Type() == SCH_TEXTBOX_T || aItem->Type() == SCH_TABLECELL_T )
409 {
410 const SCH_TEXTBOX* textBox = static_cast<const SCH_TEXTBOX*>( aItem );
411
412 if( isBackgroundLayer( aLayer ) )
413 color = textBox->GetFillColor();
414 else if( !isSymbolChild || textBox->GetTextColor() != COLOR4D::UNSPECIFIED )
415 color = textBox->GetTextColor();
416 }
417 else if( const EDA_TEXT* otherTextItem = dynamic_cast<const EDA_TEXT*>( aItem ) )
418 {
419 if( !isSymbolChild || otherTextItem->GetTextColor() != COLOR4D::UNSPECIFIED )
420 color = otherTextItem->GetTextColor();
421 }
422
423 if( color.m_text )
424 color = COLOR4D( aItem->ResolveText( *color.m_text, &m_schematic->CurrentSheet() ) );
425 }
426 else /* overrideItemColors */
427 {
428 // If we ARE overriding the item colors, what do we do with non-item-color fills?
429 // There are two theories: we should leave them untouched, or we should drop them entirely.
430 // We currently implment the first.
431 if( isBackgroundLayer( aLayer) )
432 {
433 if( aItem->Type() == SCH_SHAPE_T || aItem->Type() == SCH_RULE_AREA_T )
434 {
435 const SCH_SHAPE* shape = static_cast<const SCH_SHAPE*>( aItem );
436
437 if( shape->GetFillMode() == FILL_T::FILLED_WITH_COLOR )
438 color = shape->GetFillColor();
439 }
440 else if( aItem->Type() == SCH_SHEET_T )
441 {
442 const SCH_SHEET* sheet = static_cast<const SCH_SHEET*>( aItem );
443
444 color = sheet->GetBackgroundColor();
445 }
446 }
447 }
448
449 if( color == COLOR4D::UNSPECIFIED )
450 color = m_schSettings.GetLayerColor( aLayer );
451
452 if( aItem->IsBrightened() ) // Selection disambiguation, net highlighting, etc.
453 {
454 color = m_schSettings.GetLayerColor( LAYER_BRIGHTENED );
455
456 if( aDrawingShadows )
457 {
458 if( aItem->IsSelected() )
459 color = m_schSettings.GetLayerColor( LAYER_SELECTION_SHADOWS );
460 else
461 color = color.WithAlpha( 0.15 );
462 }
463 else if( isBackgroundLayer( aLayer ) )
464 {
465 color = color.WithAlpha( 0.2 );
466 }
467 }
468 else if( aItem->IsSelected() && aDrawingShadows )
469 {
470 color = m_schSettings.GetLayerColor( LAYER_SELECTION_SHADOWS );
471 }
472 else if( aItem->IsSelected() && isBackgroundLayer( aLayer ) )
473 {
474 // Selected items will be painted over all other items, so make backgrounds translucent so
475 // that non-selected overlapping objects are visible
476 color = color.WithAlpha( 0.5 );
477 }
478
479 if( m_schSettings.m_ShowDisabled
480 || ( m_schSettings.m_ShowGraphicsDisabled && aItem->Type() != SCH_FIELD_T ) )
481 {
482 color = color.Darken( 0.5f );
483 }
484
485 if( aDimmed && !( aItem->IsSelected() && aDrawingShadows ) )
486 {
487 COLOR4D sheetColour = m_schSettings.GetLayerColor( LAYER_SCHEMATIC_BACKGROUND );
488 color.Desaturate();
489 color = color.Mix( sheetColour, 0.5f );
490 }
491
492 if( aItem->GetForcedTransparency() > 0.0 )
493 color = color.WithAlpha( color.a * ( 1.0 - aItem->GetForcedTransparency() ) );
494
495 return color;
496}
497
498
499float SCH_PAINTER::getLineWidth( const SCH_ITEM* aItem, bool aDrawingShadows,
500 bool aDrawingWireColorHighlights ) const
501{
502 wxCHECK( aItem, static_cast<float>( schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS ) ) );
503
504 int pen = aItem->GetEffectivePenWidth( &m_schSettings );
505 float width = pen;
506
507 if( aItem->IsBrightened() || aItem->IsSelected() )
508 {
509 if( aDrawingShadows && aItem->IsType( g_ScaledSelectionTypes ) )
510 width += getShadowWidth( aItem->IsBrightened() );
511 }
512
513 if( aDrawingWireColorHighlights )
514 {
515 float colorHighlightWidth = schIUScale.MilsToIU( 15.0 );
516 EESCHEMA_SETTINGS* eeschemaCfg = eeconfig();
517
518 if( eeschemaCfg )
519 colorHighlightWidth = schIUScale.MilsToIU( eeschemaCfg->m_Selection.highlight_netclass_colors_thickness );
520
521 width += colorHighlightWidth;
522 }
523
524 return width;
525}
526
527
528float SCH_PAINTER::getTextThickness( const SCH_ITEM* aItem ) const
529{
530 int pen = m_schSettings.GetDefaultPenWidth();
531
532 switch( aItem->Type() )
533 {
534 case SCH_FIELD_T:
535 pen = static_cast<const SCH_FIELD*>( aItem )->GetEffectiveTextPenWidth( pen );
536 break;
537
538 case SCH_TEXT_T:
539 pen = static_cast<const SCH_TEXT*>( aItem )->GetEffectiveTextPenWidth( pen );
540 break;
541
542 case SCH_LABEL_T:
545 case SCH_HIER_LABEL_T:
546 case SCH_SHEET_PIN_T:
547 pen = static_cast<const SCH_LABEL_BASE*>( aItem )->GetEffectiveTextPenWidth( pen );
548 break;
549
550 case SCH_TEXTBOX_T:
551 case SCH_TABLECELL_T:
552 pen = static_cast<const SCH_TEXTBOX*>( aItem )->GetEffectiveTextPenWidth( pen );
553 break;
554
555 default:
556 UNIMPLEMENTED_FOR( aItem->GetClass() );
557 }
558
559 return (float) pen;
560}
561
562
564{
565 int docTextSize = schIUScale.MilsToIU( 50 );
566 int screenTextSize = std::abs( (int) m_gal->GetScreenWorldMatrix().GetScale().y * 7 );
567
568 // 66% zoom-relative
569 return KiROUND( ( docTextSize + screenTextSize * 2 ) / 3.0 );
570}
571
572
573static bool isFieldsLayer( int aLayer )
574{
575 return aLayer == LAYER_REFERENCEPART
576 || aLayer == LAYER_VALUEPART
577 || aLayer == LAYER_INTERSHEET_REFS
578 || aLayer == LAYER_NETCLASS_REFS
579 || aLayer == LAYER_FIELDS
580 || aLayer == LAYER_SHEETNAME
581 || aLayer == LAYER_SHEETFILENAME
582 || aLayer == LAYER_SHEETFIELDS;
583}
584
585
586static BOX2I GetTextExtents( const wxString& aText, const VECTOR2D& aPosition, KIFONT::FONT& aFont,
587 const TEXT_ATTRIBUTES& aAttrs, const KIFONT::METRICS& aFontMetrics )
588{
589 const VECTOR2I extents = aFont.StringBoundaryLimits( aText, aAttrs.m_Size, aAttrs.m_StrokeWidth,
590 aAttrs.m_Bold, aAttrs.m_Italic, aFontMetrics );
591 BOX2I box( aPosition, VECTOR2I( extents.x, aAttrs.m_Size.y ) );
592
593 switch( aAttrs.m_Halign )
594 {
595 case GR_TEXT_H_ALIGN_LEFT: break;
596 case GR_TEXT_H_ALIGN_CENTER: box.SetX( box.GetX() - box.GetWidth() / 2 ); break;
597 case GR_TEXT_H_ALIGN_RIGHT: box.SetX( box.GetX() - box.GetWidth() ); break;
598 case GR_TEXT_H_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Legal only in dialogs" ) ); break;
599 }
600
601 switch( aAttrs.m_Valign )
602 {
603 case GR_TEXT_V_ALIGN_TOP: break;
604 case GR_TEXT_V_ALIGN_CENTER: box.SetY( box.GetY() - box.GetHeight() / 2 ); break;
605 case GR_TEXT_V_ALIGN_BOTTOM: box.SetY( box.GetY() - box.GetHeight() ); break;
606 case GR_TEXT_V_ALIGN_INDETERMINATE: wxFAIL_MSG( wxT( "Legal only in dialogs" ) ); break;
607 }
608
609 box.Normalize(); // Make h and v sizes always >= 0
610 box = box.GetBoundingBoxRotated( aPosition, aAttrs.m_Angle );
611
612 return box;
613}
614
615
616static void strokeText( KIGFX::GAL& aGal, const wxString& aText, const VECTOR2D& aPosition,
617 const TEXT_ATTRIBUTES& aAttrs, const KIFONT::METRICS& aFontMetrics,
618 std::optional<VECTOR2I> aMousePos = std::nullopt, wxString* aActiveUrl = nullptr )
619{
620 KIFONT::FONT* font = aAttrs.m_Font;
621
622 if( !font )
623 font = KIFONT::FONT::GetFont( eeconfig()->m_Appearance.default_font, aAttrs.m_Bold, aAttrs.m_Italic );
624
625 aGal.SetIsFill( font->IsOutline() );
626 aGal.SetIsStroke( font->IsStroke() );
627
628 font->Draw( &aGal, aText, aPosition, aAttrs, aFontMetrics, aMousePos, aActiveUrl );
629}
630
631
632static void bitmapText( KIGFX::GAL& aGal, const wxString& aText, const VECTOR2D& aPosition,
633 const TEXT_ATTRIBUTES& aAttrs )
634{
635 // Bitmap font has different metrics from the stroke font so we compensate a bit before
636 // stroking
637 aGal.SetGlyphSize( VECTOR2I( aAttrs.m_Size.x, KiROUND( aAttrs.m_Size.y * 1.05 ) ) );
638 aGal.SetLineWidth( (float) aAttrs.m_StrokeWidth * 1.35f );
639
640 aGal.SetHorizontalJustify( aAttrs.m_Halign );
641 aGal.SetVerticalJustify( aAttrs.m_Valign );
642
643 aGal.BitmapText( aText, aPosition, aAttrs.m_Angle );
644}
645
646
647static void knockoutText( KIGFX::GAL& aGal, const wxString& aText, const VECTOR2D& aPosition,
648 const TEXT_ATTRIBUTES& aAttrs, const KIFONT::METRICS& aFontMetrics )
649{
650 TEXT_ATTRIBUTES attrs( aAttrs );
651 KIFONT::FONT* font = aAttrs.m_Font;
652
653 if( !font )
654 {
655 font = KIFONT::FONT::GetFont( eeconfig()->m_Appearance.default_font, attrs.m_Bold,
656 attrs.m_Italic );
657 }
658
660 SHAPE_POLY_SET knockouts;
661
662 CALLBACK_GAL callback_gal( empty_opts,
663 // Polygon callback
664 [&]( const SHAPE_LINE_CHAIN& aPoly )
665 {
666 knockouts.AddOutline( aPoly );
667 } );
668
669 callback_gal.SetIsFill( false );
670 callback_gal.SetIsStroke( true );
671 callback_gal.SetLineWidth( (float) attrs.m_StrokeWidth );
672 font->Draw( &callback_gal, aText, aPosition, attrs, aFontMetrics );
673
674 BOX2I bbox = knockouts.BBox( attrs.m_StrokeWidth * 2 );
675 SHAPE_POLY_SET finalPoly;
676
677 finalPoly.NewOutline();
678 finalPoly.Append( bbox.GetLeft(), bbox.GetTop() );
679 finalPoly.Append( bbox.GetRight(), bbox.GetTop() );
680 finalPoly.Append( bbox.GetRight(), bbox.GetBottom() );
681 finalPoly.Append( bbox.GetLeft(), bbox.GetBottom() );
682
683 finalPoly.BooleanSubtract( knockouts );
684 finalPoly.Fracture();
685
686 aGal.SetIsStroke( false );
687 aGal.SetIsFill( true );
688 aGal.SetFillColor( attrs.m_Color );
689 aGal.DrawPolygon( finalPoly );
690}
691
692
693static void boxText( KIGFX::GAL& aGal, const wxString& aText, const VECTOR2D& aPosition,
694 const TEXT_ATTRIBUTES& aAttrs, const KIFONT::METRICS& aFontMetrics )
695{
696 KIFONT::FONT* font = aAttrs.m_Font;
697
698 if( !font )
699 font = KIFONT::FONT::GetFont( eeconfig()->m_Appearance.default_font, aAttrs.m_Bold, aAttrs.m_Italic );
700
701 BOX2I box = GetTextExtents( aText, aPosition, *font, aAttrs, aFontMetrics );
702
703 // Give the highlight a bit of margin.
704 box.Inflate( aAttrs.m_StrokeWidth / 2, aAttrs.m_StrokeWidth * 2 );
705
706 aGal.SetIsFill( true );
707 aGal.SetIsStroke( false );
708 aGal.DrawRectangle( box.GetOrigin(), box.GetEnd() );
709}
710
711
712void SCH_PAINTER::triLine( const VECTOR2D& a, const VECTOR2D& b, const VECTOR2D& c )
713{
714 m_gal->DrawLine( a, b );
715 m_gal->DrawLine( b, c );
716}
717
718
719void SCH_PAINTER::draw( const LIB_SYMBOL* aSymbol, int aLayer, bool aDrawFields, int aUnit,
720 int aBodyStyle, bool aDimmed )
721{
722 if( !aUnit )
723 aUnit = m_schSettings.m_ShowUnit;
724
725 if( !aBodyStyle )
726 aBodyStyle = m_schSettings.m_ShowBodyStyle;
727
728 std::unique_ptr< LIB_SYMBOL > tmpSymbol;
729 const LIB_SYMBOL* drawnSymbol = aSymbol;
730
731 if( aSymbol->IsDerived() )
732 {
733 tmpSymbol = aSymbol->Flatten();
734 drawnSymbol = tmpSymbol.get();
735 }
736
737 // The parent must exist on the union of all its children's draw layers. But that doesn't
738 // mean we want to draw each child on the union.
739 auto childOnLayer =
740 []( const SCH_ITEM& item, int layer )
741 {
742 return alg::contains( item.ViewGetLayers(), layer );
743 };
744
745 for( const SCH_ITEM& item : drawnSymbol->GetDrawItems() )
746 {
747 if( !aDrawFields && item.Type() == SCH_FIELD_T )
748 continue;
749
750 if( !childOnLayer( item, aLayer ) )
751 continue;
752
753 if( aUnit && item.GetUnit() && aUnit != item.GetUnit() )
754 continue;
755
756 if( aBodyStyle && item.GetBodyStyle() && aBodyStyle != item.GetBodyStyle() )
757 continue;
758
759 draw( &item, aLayer, aDimmed );
760 }
761}
762
763
765{
766 if( m_schSettings.m_PinSymbolSize > 0 )
767 return m_schSettings.m_PinSymbolSize;
768
769 return aPin.GetNameTextSize() != 0 ? aPin.GetNameTextSize() / 2 : aPin.GetNumberTextSize() / 2;
770}
771
772
773// Utility for getting the size of the 'external' pin decorators (as a radius)
774// i.e. the negation circle, the polarity 'slopes' and the nonlogic marker
776{
777 if( m_schSettings.m_PinSymbolSize > 0 )
778 return m_schSettings.m_PinSymbolSize;
779
780 return aPin.GetNumberTextSize() / 2;
781}
782
783
784// Draw the target (an open circle) for a pin which has no connection or is being moved.
786 bool aDrawingShadows, bool aBrightened )
787{
788 const PIN_LAYOUT_CACHE& plc = aPin.GetLayoutCache();
789 const CIRCLE c = plc.GetDanglingIndicator();
790
791 float lineWidth = aDrawingShadows ? getShadowWidth( aBrightened )
792 : m_schSettings.GetDanglingIndicatorThickness();
793
794 // Dangling symbols must be drawn in a slightly different colour so they can be seen when
795 // they overlap with a junction dot.
796 m_gal->SetStrokeColor( aColor.Brightened( 0.3 ) );
797
798 m_gal->SetIsFill( false );
799 m_gal->SetIsStroke( true );
800 m_gal->SetLineWidth( lineWidth );
801 m_gal->DrawCircle( c.Center, c.Radius );
802}
803
804
808void SCH_PAINTER::drawLocalPowerIcon( const VECTOR2D& aPos, double aSize, bool aRotate,
809 const COLOR4D& aColor, bool aDrawingShadows,
810 bool aBrightened )
811{
812 double lineWidth = aSize / 10.0;
813
814 if( aDrawingShadows )
815 lineWidth += getShadowWidth( aBrightened );
816
817 std::vector<SCH_SHAPE> shapeList;
818 SCH_SYMBOL::BuildLocalPowerIconShape( shapeList, aPos, aSize, lineWidth, aRotate );
819
820 m_gal->SetLineWidth( lineWidth );
821 m_gal->SetIsStroke( true );
822 m_gal->SetStrokeColor( aColor );
823 m_gal->SetFillColor( aColor );
824
825 for( const SCH_SHAPE& shape : shapeList )
826 {
827 // Currently there are only 2 shapes: BEZIER and CIRCLE
828 m_gal->SetIsFill( shape.GetFillMode() != FILL_T::NO_FILL );
829
830 if( shape.GetShape() == SHAPE_T::BEZIER )
831 m_gal->DrawCurve( shape.GetStart(), shape.GetBezierC1(), shape.GetBezierC2(), shape.GetEnd() );
832 else if( shape.GetShape() == SHAPE_T::CIRCLE )
833 m_gal->DrawCircle( shape.getCenter(), shape.GetRadius() );
834 }
835}
836
837
841static void drawAltPinModesIcon( GAL& aGal, const VECTOR2D& aPos, double aSize, bool aBaseSelected,
842 bool aRotate, int aExtraLineWidth, const COLOR4D& aColor )
843{
844 aGal.Save();
845
846 aGal.Translate( aPos );
847
848 if( aRotate )
849 {
850 aGal.Rotate( ANGLE_270.AsRadians() );
851 }
852
853 aGal.SetIsFill( false );
854 aGal.SetIsStroke( true );
855 aGal.SetLineWidth( aSize / 10.0 + aExtraLineWidth );
856 aGal.SetStrokeColor( aColor );
857
858 /*
859 * ----------->
860 * + <--center
861 * \------->
862 *
863 * or
864 *
865 * ----- ---->
866 * \
867 * \------>
868 */
869
870 const double lineYOffset = aSize / 4;
871 const double arrowHead = aSize / 8;
872
873 const VECTOR2D topLineREnd = VECTOR2D{ aSize / 2, -lineYOffset };
874 const VECTOR2D btmLineREnd = VECTOR2D{ aSize / 2, lineYOffset };
875
876 // Top line and arrowhead
877 if( aBaseSelected )
878 {
879 // Full top line
880 aGal.DrawLine( topLineREnd, topLineREnd - VECTOR2D{ aSize, 0 } );
881 }
882 else
883 {
884 // Line with a gap
885 aGal.DrawLine( topLineREnd, topLineREnd - VECTOR2D{ aSize / 2, 0 } );
886 aGal.DrawLine( topLineREnd - VECTOR2D{ aSize, 0 },
887 topLineREnd - VECTOR2D{ aSize * 0.7, 0 } );
888 }
889
890 aGal.DrawLine( topLineREnd, topLineREnd - VECTOR2D{ arrowHead * 1.2, arrowHead } );
891 aGal.DrawLine( topLineREnd, topLineREnd - VECTOR2D{ arrowHead * 1.2, -arrowHead } );
892
893 // Bottom line and arrowhead
894 aGal.DrawLine( btmLineREnd, btmLineREnd - VECTOR2D{ aSize / 2, 0 } );
895 aGal.DrawLine( btmLineREnd, btmLineREnd - VECTOR2D{ arrowHead * 1.2, arrowHead } );
896 aGal.DrawLine( btmLineREnd, btmLineREnd - VECTOR2D{ arrowHead * 1.2, -arrowHead } );
897
898 // Top and bottom 'S' arcs
899 if( !aBaseSelected )
900 {
901 aGal.DrawArc( topLineREnd - VECTOR2D{ aSize, -lineYOffset },
902 lineYOffset, ANGLE_0, -ANGLE_90 );
903 }
904
905 aGal.DrawArc( topLineREnd - VECTOR2D{ aSize - lineYOffset * 2, -lineYOffset },
906 lineYOffset, ANGLE_180, -ANGLE_90 );
907
908 aGal.Restore();
909};
910
911
912void SCH_PAINTER::draw( const SCH_PIN* aPin, int aLayer, bool aDimmed )
913{
914 // Don't draw pins from a selection view-group. Pins in a schematic must always be drawn
915 // from their parent symbol's m_part.
916 if( dynamic_cast<const SCH_SYMBOL*>( aPin->GetParentSymbol() ) )
917 return;
918
919 if( !isUnitAndConversionShown( aPin ) )
920 return;
921
922 const bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
923 const bool drawingDangling = aLayer == LAYER_DANGLING;
924 const bool drawingOP = aLayer == LAYER_OP_CURRENTS;
925
926 if( m_schSettings.IsPrinting() && ( drawingShadows || drawingDangling ) )
927 return;
928
929 const bool isDangling = m_schSettings.m_IsSymbolEditor || aPin->HasFlag( IS_DANGLING );
930
931 if( drawingShadows && !( aPin->IsBrightened() || aPin->IsSelected() ) )
932 return;
933
934 const VECTOR2I pos = aPin->GetPosition();
935 COLOR4D color = getRenderColor( aPin, LAYER_PIN, drawingShadows, aDimmed );
936
937 if( !aPin->IsVisible() )
938 {
939 if( m_schSettings.IsPrinting() )
940 return;
941
943 : m_schSettings.m_ShowHiddenPins;
944
945 if( force_show )
946 {
947 color = getRenderColor( aPin, LAYER_HIDDEN, drawingShadows, aDimmed );
948 }
949 else
950 {
951 if( drawingDangling && isDangling && aPin->IsGlobalPower() )
952 drawPinDanglingIndicator( *aPin, color, drawingShadows, aPin->IsBrightened() );
953
954 return;
955 }
956 }
957
958 if( drawingDangling )
959 {
960 if( isDangling )
961 drawPinDanglingIndicator( *aPin, color, drawingShadows, aPin->IsBrightened() );
962
963 return;
964 }
965
966 if( m_schSettings.GetDrawBoundingBoxes() )
967 drawItemBoundingBox( aPin );
968
969 const VECTOR2I p0 = aPin->GetPinRoot();
970 const VECTOR2I dir( sign( pos.x - p0.x ), sign( pos.y - p0.y ) );
971 const int len = aPin->GetLength();
972
973 if( drawingOP && !aPin->GetOperatingPoint().IsEmpty() )
974 {
975 int textSize = getOperatingPointTextSize();
976 VECTOR2I mid = ( p0 + pos ) / 2;
977 int textOffset = KiROUND( textSize * 0.22 );
978 TEXT_ATTRIBUTES attrs;
979
980 if( len > textSize )
981 {
982 if( dir.x == 0 )
983 {
984 mid.x += KiROUND( textOffset * 1.2 );
986 }
987 else
988 {
989 mid.y -= KiROUND( textOffset * 1.2 );
990 attrs.m_Angle = ANGLE_VERTICAL;
991 }
992
995
996 attrs.m_Font = KIFONT::FONT::GetFont(); // always use stroke font for performance
997 attrs.m_Size = VECTOR2I( textSize, textSize );
998 attrs.m_StrokeWidth = GetPenSizeForDemiBold( textSize );
999 attrs.m_Color = m_schSettings.GetLayerColor( LAYER_OP_CURRENTS );
1000
1001 knockoutText( *m_gal, aPin->GetOperatingPoint(), mid, attrs, aPin->GetFontMetrics() );
1002 }
1003 }
1004
1005 if( drawingOP )
1006 return;
1007
1008 VECTOR2D pc;
1009
1010 m_gal->SetIsStroke( true );
1011 m_gal->SetIsFill( false );
1012 m_gal->SetLineWidth( getLineWidth( aPin, drawingShadows ) );
1013 m_gal->SetStrokeColor( color );
1014 m_gal->SetFontBold( false );
1015 m_gal->SetFontUnderlined( false );
1016 m_gal->SetFontItalic( false );
1017
1018 const int radius = externalPinDecoSize( *aPin );
1019 const int diam = radius*2;
1020 const int clock_size = internalPinDecoSize( *aPin );
1021
1022 if( aPin->GetType() == ELECTRICAL_PINTYPE::PT_NC ) // Draw a N.C. symbol
1023 {
1024 m_gal->DrawLine( p0, pos );
1025
1026 m_gal->DrawLine( pos + VECTOR2D( -1, -1 ) * TARGET_PIN_RADIUS,
1027 pos + VECTOR2D( 1, 1 ) * TARGET_PIN_RADIUS );
1028 m_gal->DrawLine( pos + VECTOR2D( 1, -1 ) * TARGET_PIN_RADIUS ,
1029 pos + VECTOR2D( -1, 1 ) * TARGET_PIN_RADIUS );
1030 }
1031 else
1032 {
1033 switch( aPin->GetShape() )
1034 {
1035 default:
1037 m_gal->DrawLine( p0, pos );
1038 break;
1039
1041 m_gal->DrawCircle( p0 + dir * radius, radius );
1042 m_gal->DrawLine( p0 + dir * ( diam ), pos );
1043 break;
1044
1046 pc = p0 - dir * clock_size ;
1047
1048 triLine( p0 + VECTOR2D( dir.y, -dir.x) * clock_size,
1049 pc,
1050 p0 + VECTOR2D( -dir.y, dir.x) * clock_size );
1051
1052 m_gal->DrawCircle( p0 + dir * radius, radius );
1053 m_gal->DrawLine( p0 + dir * ( diam ), pos );
1054 break;
1055
1058 pc = p0 - dir * clock_size ;
1059
1060 triLine( p0 + VECTOR2D( dir.y, -dir.x) * clock_size,
1061 pc,
1062 p0 + VECTOR2D( -dir.y, dir.x) * clock_size );
1063
1064 if( !dir.y )
1065 {
1066 triLine( p0 + VECTOR2D(dir.x, 0) * diam,
1067 p0 + VECTOR2D(dir.x, -1) * diam,
1068 p0 );
1069 }
1070 else /* MapX1 = 0 */
1071 {
1072 triLine( p0 + VECTOR2D( 0, dir.y) * diam,
1073 p0 + VECTOR2D(-1, dir.y) * diam,
1074 p0 );
1075 }
1076
1077 m_gal->DrawLine( p0, pos );
1078 break;
1079
1081 m_gal->DrawLine( p0, pos );
1082
1083 if( !dir.y )
1084 {
1085 triLine( p0 + VECTOR2D( 0, clock_size ),
1086 p0 + VECTOR2D( -dir.x * clock_size, 0 ),
1087 p0 + VECTOR2D( 0, -clock_size ) );
1088 }
1089 else
1090 {
1091 triLine( p0 + VECTOR2D( clock_size, 0 ),
1092 p0 + VECTOR2D( 0, -dir.y * clock_size ),
1093 p0 + VECTOR2D( -clock_size, 0 ) );
1094 }
1095 break;
1096
1098 m_gal->DrawLine( p0, pos );
1099
1100 if( !dir.y )
1101 {
1102 triLine( p0 + VECTOR2D(dir.x, 0) * diam,
1103 p0 + VECTOR2D(dir.x, -1) * diam,
1104 p0 );
1105 }
1106 else /* MapX1 = 0 */
1107 {
1108 triLine( p0 + VECTOR2D( 0, dir.y) * diam,
1109 p0 + VECTOR2D(-1, dir.y) * diam,
1110 p0 );
1111 }
1112 break;
1113
1114 case GRAPHIC_PINSHAPE::OUTPUT_LOW: // IEEE symbol "Active Low Output"
1115 m_gal->DrawLine( p0, pos );
1116
1117 if( !dir.y ) // Horizontal pin
1118 m_gal->DrawLine( p0 - VECTOR2D( 0, diam ), p0 + VECTOR2D( dir.x, 0 ) * diam );
1119 else // Vertical pin
1120 m_gal->DrawLine( p0 - VECTOR2D( diam, 0 ), p0 + VECTOR2D( 0, dir.y ) * diam );
1121 break;
1122
1123 case GRAPHIC_PINSHAPE::NONLOGIC: // NonLogic pin symbol
1124 m_gal->DrawLine( p0, pos );
1125
1126 m_gal->DrawLine( p0 - VECTOR2D( dir.x + dir.y, dir.y - dir.x ) * radius,
1127 p0 + VECTOR2D( dir.x + dir.y, dir.y - dir.x ) * radius );
1128 m_gal->DrawLine( p0 - VECTOR2D( dir.x - dir.y, dir.x + dir.y ) * radius,
1129 p0 + VECTOR2D( dir.x - dir.y, dir.x + dir.y ) * radius );
1130 break;
1131 }
1132 }
1133
1134 if( drawingShadows && !eeconfig()->m_Selection.draw_selected_children )
1135 return;
1136
1137 // Draw the labels
1138 float nameStrokeWidth = getLineWidth( aPin, false );
1139 float numStrokeWidth = getLineWidth( aPin, false );
1140
1141 nameStrokeWidth = ClampTextPenSize( nameStrokeWidth, aPin->GetNameTextSize(), true );
1142 numStrokeWidth = ClampTextPenSize( numStrokeWidth, aPin->GetNumberTextSize(), true );
1143
1144 float shadowWidth = 0.0f;
1145
1146 if( drawingShadows )
1147 {
1148 shadowWidth = getShadowWidth( aPin->IsBrightened() );
1149 }
1150
1151 PIN_LAYOUT_CACHE& cache = aPin->GetLayoutCache();
1152 cache.SetRenderParameters( nameStrokeWidth, numStrokeWidth, m_schSettings.m_ShowPinsElectricalType,
1153 m_schSettings.m_ShowPinAltIcons );
1154
1155 const auto textRendersAsBitmap =
1156 [&]( KIGFX::GAL& aGal, int aTextSize )
1157 {
1158 // Rendering text is expensive (particularly when using outline fonts). At small effective
1159 // sizes (ie: zoomed out) the visual differences between outline and/or stroke fonts and the
1160 // bitmap font becomes immaterial, and there's often more to draw when zoomed out so the
1161 // performance gain becomes more significant.
1162 static const float BITMAP_FONT_SIZE_THRESHOLD = 3.5;
1163
1164 // Any text non bitmappable?
1165 return aTextSize * aGal.GetWorldScale() < BITMAP_FONT_SIZE_THRESHOLD;
1166 };
1167
1168 // Helper function for drawing braces around multi-line text
1169 const auto drawBrace =
1170 [&]( KIGFX::GAL& aGal, const VECTOR2D& aTop, const VECTOR2D& aBottom,
1171 int aBraceWidth, bool aLeftBrace, const TEXT_ATTRIBUTES& aAttrs )
1172 {
1173 // Draw a simple brace using line segments, accounting for text rotation
1174 VECTOR2D mid = ( aTop + aBottom ) / 2.0;
1175
1176 aGal.SetLineWidth( aAttrs.m_StrokeWidth );
1177 aGal.SetIsFill( false );
1178 aGal.SetIsStroke( true );
1179
1180 // Calculate brace points in text coordinate system
1181 VECTOR2D p1 = aTop;
1182 VECTOR2D p2 = aTop;
1183 VECTOR2D p3 = mid;
1184 VECTOR2D p4 = aBottom;
1185 VECTOR2D p5 = aBottom;
1186
1187 // Apply brace offset based on text orientation
1188 if( aAttrs.m_Angle == ANGLE_VERTICAL )
1189 {
1190 // For vertical text, braces extend in the Y direction
1191 // "Left" brace is actually towards negative Y, "right" towards positive Y
1192 double braceOffset = aLeftBrace ? -aBraceWidth : aBraceWidth;
1193 p2.y += braceOffset / 2;
1194 p3.y += braceOffset;
1195 p4.y += braceOffset / 2;
1196 }
1197 else
1198 {
1199 // For horizontal text, braces extend in the X direction
1200 double braceOffset = aLeftBrace ? -aBraceWidth : aBraceWidth;
1201 p2.x += braceOffset / 2;
1202 p3.x += braceOffset;
1203 p4.x += braceOffset / 2;
1204 }
1205
1206 // Draw the brace segments
1207 aGal.DrawLine( p1, p2 );
1208 aGal.DrawLine( p2, p3 );
1209 aGal.DrawLine( p3, p4 );
1210 aGal.DrawLine( p4, p5 );
1211 };
1212
1213 const auto drawBracesAroundText =
1214 [&]( KIGFX::GAL& aGal, const wxArrayString& aLines, const VECTOR2D& aStartPos,
1215 int aLineSpacing, const TEXT_ATTRIBUTES& aAttrs )
1216 {
1217 if( aLines.size() <= 1 )
1218 return;
1219
1220 // Calculate brace dimensions
1221 int braceWidth = aAttrs.m_Size.x / 3; // Make braces a bit larger
1222
1223 // Find the maximum line width to position braces
1224 int maxLineWidth = 0;
1225 KIFONT::FONT* font = aAttrs.m_Font;
1226
1227 if( !font )
1228 font = KIFONT::FONT::GetFont( eeconfig()->m_Appearance.default_font );
1229
1230 for( const wxString& line : aLines )
1231 {
1232 wxString trimmedLine = line;
1233 trimmedLine.Trim( true ).Trim( false );
1234 VECTOR2I lineExtents = font->StringBoundaryLimits( trimmedLine, aAttrs.m_Size,
1235 aAttrs.m_StrokeWidth, false, false,
1236 KIFONT::METRICS() );
1237 maxLineWidth = std::max( maxLineWidth, lineExtents.x );
1238 }
1239
1240 // Calculate brace positions based on text vertical alignment and rotation
1241 VECTOR2D braceStart = aStartPos;
1242 VECTOR2D braceEnd = aStartPos;
1243
1244 // Extend braces beyond the text bounds
1245 int textHeight = aAttrs.m_Size.y;
1246 int extraHeight = textHeight / 3; // Extend braces by 1/3 of text height beyond text
1247
1248 if( aAttrs.m_Angle == ANGLE_VERTICAL )
1249 {
1250 // For vertical text, lines are spaced horizontally and braces are horizontal
1251 braceEnd.x += ( (int) aLines.size() - 1 ) * aLineSpacing;
1252
1253 // Extend braces horizontally to encompass all lines plus extra space
1254 braceStart.x -= 2 * extraHeight;
1255
1256 // Position braces in the perpendicular direction (Y) with proper spacing
1257 int braceSpacing = maxLineWidth / 2 + braceWidth;
1258
1259 VECTOR2D topBraceStart = braceStart;
1260 topBraceStart.y -= braceSpacing;
1261
1262 VECTOR2D topBraceEnd = braceEnd;
1263 topBraceEnd.y -= braceSpacing;
1264
1265 drawBrace( aGal, topBraceStart, topBraceEnd, braceWidth, true, aAttrs );
1266
1267 VECTOR2D bottomBraceStart = braceStart;
1268 bottomBraceStart.y += braceSpacing;
1269
1270 VECTOR2D bottomBraceEnd = braceEnd;
1271 bottomBraceEnd.y += braceSpacing;
1272
1273 drawBrace( aGal, bottomBraceStart, bottomBraceEnd, braceWidth, false, aAttrs );
1274 }
1275 else
1276 {
1277 // For horizontal text, lines are spaced vertically and braces are vertical
1278 braceEnd.y += ( (int) aLines.size() - 1 ) * aLineSpacing;
1279
1280 // Extend braces vertically to encompass all lines plus extra space
1281 braceStart.y -= 2 * extraHeight;
1282
1283 // Position braces in the perpendicular direction (X) with proper spacing
1284 int braceSpacing = maxLineWidth / 2 + braceWidth;
1285
1286 // Draw left brace
1287 VECTOR2D leftTop = braceStart;
1288 leftTop.x -= braceSpacing;
1289
1290 VECTOR2D leftBottom = braceEnd;
1291 leftBottom.x -= braceSpacing;
1292
1293 drawBrace( aGal, leftTop, leftBottom, braceWidth, true, aAttrs );
1294
1295 // Draw right brace
1296 VECTOR2D rightTop = braceStart;
1297 rightTop.x += braceSpacing;
1298
1299 VECTOR2D rightBottom = braceEnd;
1300 rightBottom.x += braceSpacing;
1301
1302 drawBrace( aGal, rightTop, rightBottom, braceWidth, false, aAttrs );
1303 }
1304 };
1305
1306 const auto drawBracesAroundTextBitmap =
1307 [&]( KIGFX::GAL& aGal, const wxArrayString& aLines, const VECTOR2D& aStartPos,
1308 int aLineSpacing, const TEXT_ATTRIBUTES& aAttrs )
1309 {
1310 // Simplified brace drawing for bitmap text
1311 if( aLines.size() <= 1 )
1312 return;
1313
1314 int braceWidth = aAttrs.m_Size.x / 4;
1315
1316 // Estimate max line width (less precise for bitmap text)
1317 int maxLineWidth = aAttrs.m_Size.x * 4; // Conservative estimate
1318
1319 // Calculate brace positions based on rotation
1320 VECTOR2D braceStart = aStartPos;
1321 VECTOR2D braceEnd = aStartPos;
1322
1323 int textHalfHeight = aAttrs.m_Size.y / 2;
1324
1325 if( aAttrs.m_Angle == ANGLE_VERTICAL )
1326 {
1327 // For vertical text, lines are spaced horizontally
1328 braceEnd.x += ( (int) aLines.size() - 1 ) * aLineSpacing;
1329
1330 VECTOR2D leftStart = braceStart;
1331 leftStart.y -= maxLineWidth / 2.0 + braceWidth / 2.0;
1332
1333 VECTOR2D leftEnd = braceEnd;
1334 leftEnd.y -= maxLineWidth / 2.0 + braceWidth / 2.0;
1335
1336 drawBrace( aGal, leftStart, leftEnd, braceWidth, true, aAttrs );
1337
1338 VECTOR2D rightStart = braceStart;
1339 rightStart.y += maxLineWidth / 2.0 + braceWidth / 2.0;
1340
1341 VECTOR2D rightEnd = braceEnd;
1342 rightEnd.y += maxLineWidth / 2.0 + braceWidth / 2.0;
1343
1344 drawBrace( aGal, rightStart, rightEnd, braceWidth, false, aAttrs );
1345 }
1346 else
1347 {
1348 // For horizontal text, lines are spaced vertically
1349 braceEnd.y += ( (int) aLines.size() - 1 ) * aLineSpacing;
1350
1351 VECTOR2D braceTop = braceStart;
1352 braceTop.y -= textHalfHeight;
1353
1354 VECTOR2D braceBottom = braceEnd;
1355 braceBottom.y += textHalfHeight;
1356
1357 VECTOR2D leftTop = braceTop;
1358 leftTop.x -= maxLineWidth / 2.0 + braceWidth / 2.0;
1359
1360 VECTOR2D leftBottom = braceBottom;
1361 leftBottom.x -= maxLineWidth / 2.0 + braceWidth / 2.0;
1362
1363 drawBrace( aGal, leftTop, leftBottom, braceWidth, true, aAttrs );
1364
1365 VECTOR2D rightTop = braceTop;
1366 rightTop.x += maxLineWidth / 2.0 + braceWidth / 2.0;
1367
1368 VECTOR2D rightBottom = braceBottom;
1369 rightBottom.x += maxLineWidth / 2.0 + braceWidth / 2.0;
1370
1371 drawBrace( aGal, rightTop, rightBottom, braceWidth, false, aAttrs );
1372 }
1373 };
1374
1375 // Helper functions for drawing multi-line pin text with braces
1376 const auto drawMultiLineText =
1377 [&]( KIGFX::GAL& aGal, const wxString& aText, const VECTOR2D& aPosition,
1378 const TEXT_ATTRIBUTES& aAttrs, const KIFONT::METRICS& aFontMetrics )
1379 {
1380 // Check if this is multi-line stacked pin text with braces
1381 if( aText.StartsWith( "[" ) && aText.EndsWith( "]" ) && aText.Contains( "\n" ) )
1382 {
1383 // Extract content between braces and split into lines
1384 wxString content = aText.Mid( 1, aText.Length() - 2 );
1385 wxArrayString lines;
1386 wxStringSplit( content, lines, '\n' );
1387
1388 if( lines.size() > 1 )
1389 {
1390 // Calculate line spacing (similar to EDA_TEXT::GetInterline)
1391 int lineSpacing = KiROUND( aAttrs.m_Size.y * 1.3 ); // 130% of text height
1392
1393 // Calculate positioning based on text alignment and rotation
1394 VECTOR2D startPos = aPosition;
1395
1396 if( aAttrs.m_Angle == ANGLE_VERTICAL )
1397 {
1398 // For vertical text, lines are spaced horizontally
1399 // Adjust start position based on horizontal alignment
1400 if( aAttrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT )
1401 {
1402 int totalWidth = ( (int) lines.size() - 1 ) * lineSpacing;
1403 startPos.x -= totalWidth;
1404 }
1405 else if( aAttrs.m_Halign == GR_TEXT_H_ALIGN_CENTER )
1406 {
1407 int totalWidth = ( (int) lines.size() - 1 ) * lineSpacing;
1408 startPos.x -= totalWidth / 2.0;
1409 }
1410
1411 // Draw each line
1412 for( size_t i = 0; i < lines.size(); i++ )
1413 {
1414 VECTOR2D linePos = startPos;
1415 linePos.x += i * lineSpacing;
1416
1417 wxString line = lines[i];
1418 line.Trim( true ).Trim( false );
1419
1420 strokeText( aGal, line, linePos, aAttrs, aFontMetrics );
1421 }
1422 }
1423 else
1424 {
1425 // For horizontal text, lines are spaced vertically
1426 // Adjust start position based on vertical alignment
1427 if( aAttrs.m_Valign == GR_TEXT_V_ALIGN_BOTTOM )
1428 {
1429 int totalHeight = ( (int) lines.size() - 1 ) * lineSpacing;
1430 startPos.y -= totalHeight;
1431 }
1432 else if( aAttrs.m_Valign == GR_TEXT_V_ALIGN_CENTER )
1433 {
1434 int totalHeight = ( (int) lines.size() - 1 ) * lineSpacing;
1435 startPos.y -= totalHeight / 2.0;
1436 }
1437
1438 // Draw each line
1439 for( size_t i = 0; i < lines.size(); i++ )
1440 {
1441 VECTOR2D linePos = startPos;
1442 linePos.y += (int) i * lineSpacing;
1443
1444 wxString line = lines[i];
1445 line.Trim( true ).Trim( false );
1446
1447 strokeText( aGal, line, linePos, aAttrs, aFontMetrics );
1448 }
1449 }
1450
1451 // Draw braces around the text
1452 drawBracesAroundText( aGal, lines, startPos, lineSpacing, aAttrs );
1453 return;
1454 }
1455 }
1456
1457 // Fallback to regular single-line text
1458 strokeText( aGal, aText, aPosition, aAttrs, aFontMetrics );
1459 };
1460
1461 const auto boxMultiLineText =
1462 [&]( KIGFX::GAL& aGal, const wxString& aText, const VECTOR2D& aPosition,
1463 const TEXT_ATTRIBUTES& aAttrs, const KIFONT::METRICS& aFontMetrics )
1464 {
1465 // Similar to drawMultiLineText but uses boxText for outline fonts
1466 if( aText.StartsWith( "[" ) && aText.EndsWith( "]" ) && aText.Contains( "\n" ) )
1467 {
1468 wxString content = aText.Mid( 1, aText.Length() - 2 );
1469 wxArrayString lines;
1470 wxStringSplit( content, lines, '\n' );
1471
1472 if( lines.size() > 1 )
1473 {
1474 int lineSpacing = KiROUND( aAttrs.m_Size.y * 1.3 );
1475 VECTOR2D startPos = aPosition;
1476
1477 if( aAttrs.m_Angle == ANGLE_VERTICAL )
1478 {
1479 // For vertical text, lines are spaced horizontally
1480 if( aAttrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT )
1481 {
1482 int totalWidth = ( (int) lines.size() - 1 ) * lineSpacing;
1483 startPos.x -= totalWidth;
1484 }
1485 else if( aAttrs.m_Halign == GR_TEXT_H_ALIGN_CENTER )
1486 {
1487 int totalWidth = ( (int) lines.size() - 1 ) * lineSpacing;
1488 startPos.x -= totalWidth / 2.0;
1489 }
1490
1491 for( size_t i = 0; i < lines.size(); i++ )
1492 {
1493 VECTOR2D linePos = startPos;
1494 linePos.x += (int) i * lineSpacing;
1495
1496 wxString line = lines[i];
1497 line.Trim( true ).Trim( false );
1498
1499 boxText( aGal, line, linePos, aAttrs, aFontMetrics );
1500 }
1501 }
1502 else
1503 {
1504 // For horizontal text, lines are spaced vertically
1505 if( aAttrs.m_Valign == GR_TEXT_V_ALIGN_BOTTOM )
1506 {
1507 int totalHeight = ( (int) lines.size() - 1 ) * lineSpacing;
1508 startPos.y -= totalHeight;
1509 }
1510 else if( aAttrs.m_Valign == GR_TEXT_V_ALIGN_CENTER )
1511 {
1512 int totalHeight = ( (int) lines.size() - 1 ) * lineSpacing;
1513 startPos.y -= totalHeight / 2.0;
1514 }
1515
1516 for( size_t i = 0; i < lines.size(); i++ )
1517 {
1518 VECTOR2D linePos = startPos;
1519 linePos.y += (int) i * lineSpacing;
1520
1521 wxString line = lines[i];
1522 line.Trim( true ).Trim( false );
1523
1524 boxText( aGal, line, linePos, aAttrs, aFontMetrics );
1525 }
1526 }
1527
1528 drawBracesAroundText( aGal, lines, startPos, lineSpacing, aAttrs );
1529 return;
1530 }
1531 }
1532
1533 boxText( aGal, aText, aPosition, aAttrs, aFontMetrics );
1534 };
1535
1536 const auto drawMultiLineBitmapText =
1537 [&]( KIGFX::GAL& aGal, const wxString& aText, const VECTOR2D& aPosition,
1538 const TEXT_ATTRIBUTES& aAttrs )
1539 {
1540 // Similar to drawMultiLineText but uses bitmapText
1541 if( aText.StartsWith( "[" ) && aText.EndsWith( "]" ) && aText.Contains( "\n" ) )
1542 {
1543 wxString content = aText.Mid( 1, aText.Length() - 2 );
1544 wxArrayString lines;
1545 wxStringSplit( content, lines, '\n' );
1546
1547 if( lines.size() > 1 )
1548 {
1549 int lineSpacing = KiROUND( aAttrs.m_Size.y * 1.3 );
1550 VECTOR2D startPos = aPosition;
1551
1552 if( aAttrs.m_Angle == ANGLE_VERTICAL )
1553 {
1554 // For vertical text, lines are spaced horizontally
1555 if( aAttrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT )
1556 {
1557 int totalWidth = ( (int) lines.size() - 1 ) * lineSpacing;
1558 startPos.x -= totalWidth;
1559 }
1560 else if( aAttrs.m_Halign == GR_TEXT_H_ALIGN_CENTER )
1561 {
1562 int totalWidth = ( (int) lines.size() - 1 ) * lineSpacing;
1563 startPos.x -= totalWidth / 2.0;
1564 }
1565
1566 for( size_t i = 0; i < lines.size(); i++ )
1567 {
1568 VECTOR2D linePos = startPos;
1569 linePos.x += (int) i * lineSpacing;
1570
1571 wxString line = lines[i];
1572 line.Trim( true ).Trim( false );
1573
1574 bitmapText( aGal, line, linePos, aAttrs );
1575 }
1576 }
1577 else
1578 {
1579 // For horizontal text, lines are spaced vertically
1580 if( aAttrs.m_Valign == GR_TEXT_V_ALIGN_BOTTOM )
1581 {
1582 int totalHeight = ( (int) lines.size() - 1 ) * lineSpacing;
1583 startPos.y -= totalHeight;
1584 }
1585 else if( aAttrs.m_Valign == GR_TEXT_V_ALIGN_CENTER )
1586 {
1587 int totalHeight = ( (int) lines.size() - 1 ) * lineSpacing;
1588 startPos.y -= totalHeight / 2.0;
1589 }
1590
1591 for( size_t i = 0; i < lines.size(); i++ )
1592 {
1593 VECTOR2D linePos = startPos;
1594 linePos.y += (int) i * lineSpacing;
1595
1596 wxString line = lines[i];
1597 line.Trim( true ).Trim( false );
1598
1599 bitmapText( aGal, line, linePos, aAttrs );
1600 }
1601 }
1602
1603 // Draw braces with bitmap text (simplified version)
1604 drawBracesAroundTextBitmap( aGal, lines, startPos, lineSpacing, aAttrs );
1605 return;
1606 }
1607 }
1608
1609 bitmapText( aGal, aText, aPosition, aAttrs );
1610 };
1611
1612 const auto drawTextInfo =
1613 [&]( const PIN_LAYOUT_CACHE::TEXT_INFO& aTextInfo, const COLOR4D& aColor )
1614 {
1615 // const double iconSize = std::min( aPin->GetNameTextSize(), schIUScale.mmToIU( 1.5 ) );
1616 const bool renderTextAsBitmap = textRendersAsBitmap( *m_gal, aTextInfo.m_TextSize );
1617
1618 // Which of these gets used depends on the font technology, so set both
1619 m_gal->SetStrokeColor( aColor );
1620 m_gal->SetFillColor( aColor );
1621
1622 TEXT_ATTRIBUTES attrs;
1623 attrs.m_Font = KIFONT::FONT::GetFont( eeconfig()->m_Appearance.default_font );
1624 attrs.m_Size = VECTOR2I( aTextInfo.m_TextSize, aTextInfo.m_TextSize );
1625 attrs.m_Halign = aTextInfo.m_HAlign;
1626 attrs.m_Valign = aTextInfo.m_VAlign;
1627 attrs.m_Angle = aTextInfo.m_Angle;
1628 attrs.m_StrokeWidth = aTextInfo.m_Thickness;
1629
1630 if( drawingShadows )
1631 {
1632 attrs.m_StrokeWidth += KiROUND( shadowWidth );
1633
1634 if( !attrs.m_Font->IsOutline() )
1635 {
1636 drawMultiLineText( *m_gal, aTextInfo.m_Text, aTextInfo.m_TextPosition, attrs,
1637 aPin->GetFontMetrics() );
1638 }
1639 else
1640 {
1641 boxMultiLineText( *m_gal, aTextInfo.m_Text, aTextInfo.m_TextPosition, attrs,
1642 aPin->GetFontMetrics() );
1643 }
1644 }
1645 else if( nonCached( aPin ) && renderTextAsBitmap )
1646 {
1647 drawMultiLineBitmapText( *m_gal, aTextInfo.m_Text, aTextInfo.m_TextPosition, attrs );
1648 const_cast<SCH_PIN*>( aPin )->SetFlags( IS_SHOWN_AS_BITMAP );
1649 }
1650 else
1651 {
1652 drawMultiLineText( *m_gal, aTextInfo.m_Text, aTextInfo.m_TextPosition, attrs,
1653 aPin->GetFontMetrics() );
1654 const_cast<SCH_PIN*>( aPin )->SetFlags( IS_SHOWN_AS_BITMAP );
1655 }
1656 };
1657
1658 const auto getColorForLayer =
1659 [&]( int aDrawnLayer )
1660 {
1661 if( !aPin->IsVisible() )
1662 return getRenderColor( aPin, LAYER_HIDDEN, drawingShadows, aDimmed );
1663
1664 return getRenderColor( aPin, aDrawnLayer, drawingShadows, aDimmed );
1665 };
1666
1667 // Request text layout info and draw it
1668
1669 if( std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> numInfo = cache.GetPinNumberInfo( shadowWidth ) )
1670 drawTextInfo( *numInfo, getColorForLayer( LAYER_PINNUM ) );
1671
1672 if( std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> nameInfo = cache.GetPinNameInfo( shadowWidth ) )
1673 {
1674 drawTextInfo( *nameInfo, getColorForLayer( LAYER_PINNAM ) );
1675
1676 if( OPT_BOX2I altIconBox = cache.GetAltIconBBox() )
1677 {
1678 drawAltPinModesIcon( *m_gal, altIconBox->GetCenter(), altIconBox->GetWidth(),
1679 // Icon style doesn't work due to the tempPin having no alt
1680 // but maybe it's better with just one style anyway.
1681 true, nameInfo->m_Angle == ANGLE_VERTICAL, shadowWidth,
1682 getColorForLayer( LAYER_PINNAM ) );
1683 }
1684 }
1685
1686 if( std::optional<PIN_LAYOUT_CACHE::TEXT_INFO> elecTypeInfo = cache.GetPinElectricalTypeInfo( shadowWidth ) )
1687 drawTextInfo( *elecTypeInfo, getColorForLayer( LAYER_PRIVATE_NOTES ) );
1688
1689 if( aPin->IsBrightened() && m_schematic && !m_schematic->GetHighlightedNetChain().IsEmpty() )
1690 {
1691 if( SCH_NETCHAIN* sig = m_schematic->ConnectionGraph()->GetNetChainByName( m_schematic->GetHighlightedNetChain() ) )
1692 {
1693 if( sig->GetTerminalPinA() == aPin->m_Uuid || sig->GetTerminalPinB() == aPin->m_Uuid )
1694 {
1695 CIRCLE c = cache.GetDanglingIndicator();
1696 COLOR4D emphasis = sig->GetColor() != COLOR4D::UNSPECIFIED
1697 ? sig->GetColor()
1698 : color.Brightened( 0.5 );
1699 m_gal->SetStrokeColor( emphasis );
1700 m_gal->SetIsFill( false );
1701 m_gal->SetIsStroke( true );
1702 m_gal->SetLineWidth( getShadowWidth( true ) );
1703 m_gal->DrawCircle( c.Center, c.Radius );
1704 }
1705 }
1706 }
1707}
1708
1709
1710void SCH_PAINTER::drawAnchor( const VECTOR2I& aPos, bool aDrawingShadows )
1711{
1712 if( m_schSettings.IsPrinting() )
1713 return;
1714
1715 // In order for the anchors to be visible but unobtrusive, their size must factor in the
1716 // current zoom level.
1717 const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix();
1718 int radius = KiROUND( std::fabs( matrix.GetScale().x * TEXT_ANCHOR_SIZE ) / 25.0 )
1719 + schIUScale.MilsToIU( TEXT_ANCHOR_SIZE );
1720
1721 COLOR4D color = aDrawingShadows ? m_schSettings.GetLayerColor( LAYER_SELECTION_SHADOWS )
1722 : m_schSettings.GetLayerColor( LAYER_SCHEMATIC_ANCHOR );
1723
1724 m_gal->SetStrokeColor( color );
1725 m_gal->SetIsStroke( true );
1726 m_gal->SetLineWidth( aDrawingShadows ? getShadowWidth( false )
1727 : m_schSettings.GetDanglingIndicatorThickness() );
1728
1729 m_gal->DrawLine( aPos - VECTOR2I( radius, 0 ), aPos + VECTOR2I( radius, 0 ) );
1730 m_gal->DrawLine( aPos - VECTOR2I( 0, radius ), aPos + VECTOR2I( 0, radius ) );
1731}
1732
1733
1734void SCH_PAINTER::drawDanglingIndicator( const VECTOR2I& aPos, const COLOR4D& aColor, int aWidth,
1735 bool aDangling, bool aDrawingShadows, bool aBrightened )
1736{
1737 if( m_schSettings.IsPrinting() )
1738 return;
1739
1740 int size = aDangling ? DANGLING_SYMBOL_SIZE : UNSELECTED_END_SIZE;
1741
1742 if( !aDangling )
1743 aWidth /= 2;
1744
1745 VECTOR2I radius( aWidth + schIUScale.MilsToIU( size / 2 ),
1746 aWidth + schIUScale.MilsToIU( size / 2 ) );
1747
1748 // Dangling symbols must be drawn in a slightly different colour so they can be seen when
1749 // they overlap with a junction dot.
1750 m_gal->SetStrokeColor( aColor.Brightened( 0.3 ) );
1751 m_gal->SetIsStroke( true );
1752 m_gal->SetIsFill( false );
1753 m_gal->SetLineWidth( aDrawingShadows ? getShadowWidth( aBrightened )
1754 : m_schSettings.GetDanglingIndicatorThickness() );
1755
1756 m_gal->DrawRectangle( aPos - radius, aPos + radius );
1757}
1758
1759
1760void SCH_PAINTER::draw( const SCH_JUNCTION* aJct, int aLayer )
1761{
1762 bool highlightNetclassColors = false;
1763 EESCHEMA_SETTINGS* eeschemaCfg = eeconfig();
1764
1765 if( eeschemaCfg )
1766 {
1767 highlightNetclassColors = eeschemaCfg->m_Selection.highlight_netclass_colors;
1768 }
1769
1770 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
1771
1772 if( m_schSettings.IsPrinting() && drawingShadows )
1773 return;
1774
1775 if( drawingShadows && !( aJct->IsBrightened() || aJct->IsSelected() ) )
1776 return;
1777
1778 COLOR4D color;
1779
1780 if( highlightNetclassColors && aLayer == aJct->GetLayer() )
1781 color = m_schSettings.GetLayerColor( aJct->GetLayer() );
1782 else
1783 color = getRenderColor( aJct, aJct->GetLayer(), drawingShadows );
1784
1785 int junctionSize = aJct->GetEffectiveDiameter() / 2;
1786
1787 if( junctionSize > 1 )
1788 {
1789 m_gal->SetIsStroke( drawingShadows );
1790 m_gal->SetLineWidth( getLineWidth( aJct, drawingShadows ) );
1791 m_gal->SetStrokeColor( color );
1792 m_gal->SetIsFill( !drawingShadows );
1793 m_gal->SetFillColor( color );
1794 m_gal->DrawCircle( aJct->GetPosition(), junctionSize );
1795 }
1796}
1797
1798
1799void SCH_PAINTER::draw( const SCH_LINE* aLine, int aLayer )
1800{
1801 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
1802 bool drawingNetColorHighlights = aLayer == LAYER_NET_COLOR_HIGHLIGHT;
1803 bool drawingWires = aLayer == LAYER_WIRE;
1804 bool drawingBusses = aLayer == LAYER_BUS;
1805 bool drawingDangling = aLayer == LAYER_DANGLING;
1806 bool drawingOP = aLayer == LAYER_OP_VOLTAGES;
1807
1808 bool highlightNetclassColors = false;
1809 double highlightAlpha = 0.6;
1810 EESCHEMA_SETTINGS* eeschemaCfg = eeconfig();
1811 double hopOverScale = 0.0;
1812 int defaultLineWidth = schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
1813
1814 if( aLine->Schematic() ) // Can be nullptr when run from the color selection panel
1815 {
1816 hopOverScale = aLine->Schematic()->Settings().GetHopOverScale();
1817 defaultLineWidth = aLine->Schematic()->Settings().m_DefaultLineWidth;
1818 }
1819
1820 if( eeschemaCfg )
1821 {
1822 highlightNetclassColors = eeschemaCfg->m_Selection.highlight_netclass_colors;
1823 highlightAlpha = eeschemaCfg->m_Selection.highlight_netclass_colors_alpha;
1824 }
1825
1826 if( !highlightNetclassColors && drawingNetColorHighlights )
1827 return;
1828
1829 if( drawingNetColorHighlights && !( aLine->IsWire() || aLine->IsBus() ) )
1830 return;
1831
1832 if( m_schSettings.m_OverrideItemColors && drawingNetColorHighlights )
1833 return;
1834
1835 if( m_schSettings.IsPrinting() && drawingShadows )
1836 return;
1837
1838 if( drawingShadows && !( aLine->IsBrightened() || aLine->IsSelected() ) )
1839 return;
1840
1841 // Line end dangling status isn't updated until the line is finished drawing, so don't warn
1842 // them about ends that are probably connected
1843 if( aLine->IsNew() && drawingDangling )
1844 return;
1845
1846 COLOR4D color = getRenderColor( aLine, aLine->GetLayer(), drawingShadows );
1847 float width = getLineWidth( aLine, drawingShadows, drawingNetColorHighlights );
1848 LINE_STYLE lineStyle = aLine->GetEffectiveLineStyle();
1849
1850 if( highlightNetclassColors )
1851 {
1852 // Force default color for nets we are going to highlight
1853 if( drawingWires )
1854 color = m_schSettings.GetLayerColor( LAYER_WIRE );
1855 else if( drawingBusses )
1856 color = m_schSettings.GetLayerColor( LAYER_BUS );
1857 }
1858
1859 // If the user has highlighted a chain and this wire belongs to that chain,
1860 // and the chain has a colour override, tint the wire in that colour so the
1861 // highlighted chain is immediately visible.
1862 if( drawingWires && !drawingShadows && m_schematic
1863 && !m_schematic->GetHighlightedNetChain().IsEmpty() )
1864 {
1865 SCH_CONNECTION* conn = !aLine->IsConnectivityDirty() ? aLine->Connection() : nullptr;
1866
1867 if( conn && !conn->Name().IsEmpty() )
1868 {
1869 if( SCH_NETCHAIN* chain =
1870 m_schematic->ConnectionGraph()->GetNetChainForNet( conn->Name() ) )
1871 {
1872 if( chain->GetName() == m_schematic->GetHighlightedNetChain()
1873 && chain->GetColor() != COLOR4D::UNSPECIFIED )
1874 {
1875 color = chain->GetColor().WithAlpha( color.a );
1876 }
1877 }
1878 }
1879 }
1880
1881 if( drawingNetColorHighlights )
1882 {
1883 // Don't draw highlights for default-colored nets
1884 if( ( aLine->IsWire() && color == m_schSettings.GetLayerColor( LAYER_WIRE ) )
1885 || ( aLine->IsBus() && color == m_schSettings.GetLayerColor( LAYER_BUS ) ) )
1886 {
1887 return;
1888 }
1889
1890 color = color.WithAlpha( color.a * highlightAlpha );
1891 }
1892
1893 if( ( drawingDangling || drawingShadows ) && !aLine->IsNew() )
1894 {
1895 if( ( aLine->IsWire() && aLine->IsStartDangling() )
1896 || ( drawingShadows && aLine->IsSelected() && !aLine->HasFlag( STARTPOINT ) ) )
1897 {
1898 COLOR4D indicatorColor( color );
1899
1900 if( drawingShadows && !aLine->HasFlag( STARTPOINT ) )
1901 indicatorColor.Invert();
1902
1903 drawDanglingIndicator( aLine->GetStartPoint(), indicatorColor, KiROUND( width ),
1904 aLine->IsWire() && aLine->IsStartDangling(), drawingShadows,
1905 aLine->IsBrightened() );
1906 }
1907
1908 if( ( aLine->IsWire() && aLine->IsEndDangling() )
1909 || ( drawingShadows && aLine->IsSelected() && !aLine->HasFlag( ENDPOINT ) ) )
1910 {
1911 COLOR4D indicatorColor( color );
1912
1913 if( drawingShadows && !aLine->HasFlag( ENDPOINT ) )
1914 indicatorColor.Invert();
1915
1916 drawDanglingIndicator( aLine->GetEndPoint(), indicatorColor, KiROUND( width ),
1917 aLine->IsWire() && aLine->IsEndDangling(), drawingShadows,
1918 aLine->IsBrightened() );
1919 }
1920 }
1921
1922 if( drawingDangling )
1923 return;
1924
1925 if( drawingOP && !aLine->GetOperatingPoint().IsEmpty() )
1926 {
1927 int textSize = getOperatingPointTextSize();
1928 VECTOR2I pos = aLine->GetMidPoint();
1929 int textOffset = KiROUND( textSize * 0.22 );
1930 TEXT_ATTRIBUTES attrs;
1931
1932 if( aLine->GetStartPoint().y == aLine->GetEndPoint().y )
1933 {
1934 pos.y -= textOffset;
1937 }
1938 else
1939 {
1940 pos.x += KiROUND( textOffset * 1.2 );
1943 }
1944
1945 attrs.m_Font = KIFONT::FONT::GetFont(); // always use stroke font for performance
1946 attrs.m_Size = VECTOR2I( textSize, textSize );
1947 attrs.m_StrokeWidth = GetPenSizeForDemiBold( textSize );
1948 attrs.m_Color = m_schSettings.GetLayerColor( LAYER_OP_VOLTAGES );
1949
1950 knockoutText( *m_gal, aLine->GetOperatingPoint(), pos, attrs, aLine->GetFontMetrics() );
1951 }
1952
1953 if( drawingOP )
1954 return;
1955
1956 m_gal->SetIsStroke( true );
1957 m_gal->SetIsFill( false );
1958 m_gal->SetStrokeColor( color );
1959 m_gal->SetLineWidth( width );
1960
1961 std::vector<VECTOR3I> curr_wire_shape;
1962
1963 if( ( aLine->IsWire() || aLine->IsBus() ) && hopOverScale > 0.0 )
1964 {
1965 double arcRadius = defaultLineWidth * hopOverScale;
1966 curr_wire_shape = aLine->BuildWireWithHopShape( aLine->Schematic()->GetCurrentScreen(), arcRadius );
1967 }
1968 else
1969 {
1970 curr_wire_shape.emplace_back( aLine->GetStartPoint().x, aLine->GetStartPoint().y, 0 );
1971 curr_wire_shape.emplace_back( aLine->GetEndPoint().x, aLine->GetEndPoint().y, 0 );
1972 }
1973
1974 for( size_t ii = 1; ii < curr_wire_shape.size(); ii++ )
1975 {
1976 VECTOR2I start( curr_wire_shape[ii-1].x, curr_wire_shape[ii-1].y );
1977
1978 if( curr_wire_shape[ii-1].z == 0 ) // This is the start point of a segment
1979 // there are always 2 points in list for a segment
1980 {
1981 VECTOR2I end( curr_wire_shape[ii].x, curr_wire_shape[ii].y );
1982 drawLine( start, end, lineStyle, ( lineStyle <= LINE_STYLE::FIRST_TYPE || drawingShadows ), width );
1983 }
1984 else // This is the start point of a arc. there are always 3 points in list for an arc
1985 {
1986 // Hop are a small arc, so use a solid line style gives best results
1987 VECTOR2I arc_middle( curr_wire_shape[ii].x, curr_wire_shape[ii].y );
1988 ii++;
1989 VECTOR2I arc_end( curr_wire_shape[ii].x, curr_wire_shape[ii].y );
1990 ii++;
1991
1992 VECTOR2D dstart = start;
1993 VECTOR2D dmid = arc_middle;
1994 VECTOR2D dend = arc_end;
1995 VECTOR2D center = CalcArcCenter( dstart, dmid, dend );
1996
1997 EDA_ANGLE startAngle( dstart - center );
1998 EDA_ANGLE midAngle( dmid - center );
1999 EDA_ANGLE endAngle( dend - center );
2000
2001 EDA_ANGLE angle1 = midAngle - startAngle;
2002 EDA_ANGLE angle2 = endAngle - midAngle;
2003
2004 EDA_ANGLE angle = angle1.Normalize180() + angle2.Normalize180();
2005
2006 m_gal->DrawArc( center, ( dstart - center ).EuclideanNorm(), startAngle, angle );
2007 }
2008 }
2009}
2010
2011
2012void SCH_PAINTER::draw( const SCH_SHAPE* aShape, int aLayer, bool aDimmed )
2013{
2014 if( !isUnitAndConversionShown( aShape ) )
2015 return;
2016
2017 if( aShape->IsPrivate() && !m_schSettings.m_IsSymbolEditor )
2018 return;
2019
2020 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
2021
2022 if( m_schSettings.IsPrinting() && drawingShadows )
2023 return;
2024
2025 LINE_STYLE lineStyle = aShape->GetEffectiveLineStyle();
2026 COLOR4D color = getRenderColor( aShape, aLayer, drawingShadows, aDimmed );
2027
2028 if( drawingShadows && !( aShape->IsBrightened() || aShape->IsSelected() ) )
2029 return;
2030
2031 auto drawShape =
2032 [&]( const SCH_SHAPE* shape )
2033 {
2034 switch( shape->GetShape() )
2035 {
2036 case SHAPE_T::ARC:
2037 {
2038 VECTOR2D start = shape->GetStart();
2039 VECTOR2D mid = shape->GetArcMid();
2040 VECTOR2D end = shape->GetEnd();
2041 VECTOR2D center = CalcArcCenter( start, mid, end );
2042
2043 EDA_ANGLE startAngle( start - center );
2044 EDA_ANGLE midAngle( mid - center );
2045 EDA_ANGLE endAngle( end - center );
2046
2047 EDA_ANGLE angle1 = midAngle - startAngle;
2048 EDA_ANGLE angle2 = endAngle - midAngle;
2049
2050 EDA_ANGLE angle = angle1.Normalize180() + angle2.Normalize180();
2051
2052 m_gal->DrawArc( center, ( start - center ).EuclideanNorm(), startAngle, angle );
2053 break;
2054 }
2055
2056 case SHAPE_T::CIRCLE:
2057 m_gal->DrawCircle( shape->GetPosition(), shape->GetRadius() );
2058 break;
2059
2060 case SHAPE_T::RECTANGLE:
2061 if( shape->GetCornerRadius() > 0 )
2062 {
2063 // Creates a normalized ROUNDRECT item
2064 // (GetRectangleWidth() and GetRectangleHeight() can be < 0 with transforms
2065 ROUNDRECT rr( SHAPE_RECT( shape->GetPosition(),
2066 shape->GetRectangleWidth(),
2067 shape->GetRectangleHeight() ),
2068 shape->GetCornerRadius(), true /* normalize */ );
2069 SHAPE_POLY_SET poly;
2070 rr.TransformToPolygon( poly, shape->GetMaxError() );
2071 m_gal->DrawPolygon( poly );
2072 }
2073 else
2074 {
2075 m_gal->DrawRectangle( shape->GetPosition(), shape->GetEnd() );
2076 }
2077 break;
2078
2079 case SHAPE_T::POLY:
2080 {
2081 const std::vector<SHAPE*> polySegments = shape->MakeEffectiveShapes( true );
2082
2083 if( !polySegments.empty() )
2084 {
2085 std::deque<VECTOR2D> pts;
2086
2087 for( SHAPE* polySegment : polySegments )
2088 pts.push_back( static_cast<SHAPE_SEGMENT*>( polySegment )->GetSeg().A );
2089
2090 pts.push_back( static_cast<SHAPE_SEGMENT*>( polySegments.back() )->GetSeg().B );
2091
2092 for( SHAPE* polySegment : polySegments )
2093 delete polySegment;
2094
2095 m_gal->DrawPolygon( pts );
2096 }
2097 break;
2098 }
2099
2100 case SHAPE_T::BEZIER:
2101 {
2102 m_gal->DrawCurve( shape->GetStart(), shape->GetBezierC1(),
2103 shape->GetBezierC2(), shape->GetEnd() );
2104 break;
2105 }
2106
2107 case SHAPE_T::ELLIPSE:
2108 m_gal->DrawEllipse( shape->GetEllipseCenter(), shape->GetEllipseMajorRadius(),
2109 shape->GetEllipseMinorRadius(), shape->GetEllipseRotation() );
2110 break;
2111
2113 m_gal->DrawEllipseArc( shape->GetEllipseCenter(), shape->GetEllipseMajorRadius(),
2114 shape->GetEllipseMinorRadius(), shape->GetEllipseRotation(),
2115 shape->GetEllipseStartAngle(), shape->GetEllipseEndAngle() );
2116 break;
2117
2118 default:
2119 UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() );
2120 }
2121 };
2122
2123 if( aLayer == LAYER_SELECTION_SHADOWS )
2124 {
2125 if( eeconfig()->m_Selection.fill_shapes )
2126 {
2127 // Consider a NAND gate. We have no idea which side of the arc is "inside"
2128 // so we can't reliably fill.
2129 if( aShape->GetShape() == SHAPE_T::ARC )
2130 m_gal->SetIsFill( aShape->IsSolidFill() );
2131 else
2132 m_gal->SetIsFill( true );
2133
2134 m_gal->SetFillColor( color );
2135 }
2136 else
2137 {
2138 m_gal->SetIsFill( false );
2139 }
2140
2141 // We still always draw the stroke, as otherwise single-segment shapes
2142 // (like a line) don't get a shadow, and special-casing them looks inconsistent.
2143 m_gal->SetIsStroke( true );
2144 m_gal->SetLineWidth( getLineWidth( aShape, true ) );
2145 m_gal->SetStrokeColor( color );
2146
2147 drawShape( aShape );
2148 }
2149 else if( aLayer == LAYER_DEVICE_BACKGROUND || aLayer == LAYER_NOTES_BACKGROUND
2150 || aLayer == LAYER_SHAPES_BACKGROUND )
2151 {
2152 switch( aShape->GetFillMode() )
2153 {
2154 case FILL_T::NO_FILL:
2155 break;
2156
2158 // Fill in the foreground layer
2159 break;
2160
2161 case FILL_T::HATCH:
2164 aShape->UpdateHatching();
2165 m_gal->SetIsFill( false );
2166 m_gal->SetIsStroke( true );
2167 m_gal->SetStrokeColor( color );
2168 m_gal->SetLineWidth( aShape->GetHatchLineWidth() );
2169
2170 for( const SEG& seg : aShape->GetHatchLines() )
2171 m_gal->DrawLine( seg.A, seg.B );
2172
2173 break;
2174
2177 // Do not fill the shape in B&W print mode, to avoid to visible items inside the shape
2178 if( !m_schSettings.PrintBlackAndWhiteReq() )
2179 {
2180 m_gal->SetIsFill( true );
2181 m_gal->SetIsStroke( false );
2182 m_gal->SetFillColor( color );
2183
2184 drawShape( aShape );
2185 }
2186 break;
2187
2188 default:
2189 wxFAIL_MSG( wxT( "Unsupported fill type" ) );
2190 }
2191 }
2192 else if( aLayer == LAYER_DEVICE || aLayer == LAYER_NOTES || aLayer == LAYER_PRIVATE_NOTES
2193 || aLayer == LAYER_RULE_AREAS )
2194 {
2195 // Shapes filled with the device colour must be filled in the foreground
2196 if( aShape->GetFillMode() == FILL_T::FILLED_SHAPE )
2197 {
2198 m_gal->SetIsFill( true );
2199 m_gal->SetIsStroke( false );
2200 m_gal->SetFillColor( color );
2201
2202 drawShape( aShape );
2203 }
2204
2205 float lineWidth = getLineWidth( aShape, drawingShadows );
2206
2207 if( lineWidth > 0 )
2208 {
2209 m_gal->SetIsFill( false );
2210 m_gal->SetIsStroke( true );
2211 m_gal->SetLineWidth( lineWidth );
2212 m_gal->SetStrokeColor( color );
2213
2214 if( lineStyle <= LINE_STYLE::FIRST_TYPE || drawingShadows )
2215 {
2216 drawShape( aShape );
2217 }
2218 else
2219 {
2220 std::vector<SHAPE*> shapes;
2221
2222 // For ellipses pass the SHAPE_ELLIPSE directly so the dash pattern is
2223 // continuous around the curve. Otherwise MakeEffectiveShapes returns
2224 // many SHAPE_SEGMENTs and STROKE_PARAMS restarts the pattern on each.
2225 if( aShape->GetShape() == SHAPE_T::ELLIPSE )
2226 {
2227 shapes.push_back( new SHAPE_ELLIPSE( aShape->GetEllipseCenter(), aShape->GetEllipseMajorRadius(),
2228 aShape->GetEllipseMinorRadius(),
2229 aShape->GetEllipseRotation() ) );
2230 }
2231 else if( aShape->GetShape() == SHAPE_T::ELLIPSE_ARC )
2232 {
2233 shapes.push_back( new SHAPE_ELLIPSE( aShape->GetEllipseCenter(), aShape->GetEllipseMajorRadius(),
2234 aShape->GetEllipseMinorRadius(), aShape->GetEllipseRotation(),
2235 aShape->GetEllipseStartAngle(),
2236 aShape->GetEllipseEndAngle() ) );
2237 }
2238 else
2239 {
2240 shapes = aShape->MakeEffectiveShapes( true );
2241 }
2242
2243 for( SHAPE* shape : shapes )
2244 {
2245 STROKE_PARAMS::Stroke( shape, lineStyle, KiROUND( lineWidth ), &m_schSettings,
2246 [this]( const VECTOR2I& a, const VECTOR2I& b )
2247 {
2248 if( a == b )
2249 m_gal->DrawLine( a + 1, b );
2250 else
2251 m_gal->DrawLine( a, b );
2252 } );
2253 }
2254
2255 for( SHAPE* shape : shapes )
2256 delete shape;
2257 }
2258 }
2259 }
2260}
2261
2262
2263void SCH_PAINTER::draw( const SCH_TEXT* aText, int aLayer, bool aDimmed )
2264{
2265 if( !isUnitAndConversionShown( aText ) )
2266 return;
2267
2268 if( aText->IsPrivate() && !m_schSettings.m_IsSymbolEditor )
2269 return;
2270
2271 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
2272
2273 if( m_schSettings.IsPrinting() && drawingShadows )
2274 return;
2275
2276 if( drawingShadows && !( aText->IsBrightened() || aText->IsSelected() ) )
2277 return;
2278
2279 switch( aText->Type() )
2280 {
2281 case SCH_SHEET_PIN_T: aLayer = LAYER_SHEETLABEL; break;
2282 case SCH_HIER_LABEL_T: aLayer = LAYER_HIERLABEL; break;
2283 case SCH_GLOBAL_LABEL_T: aLayer = LAYER_GLOBLABEL; break;
2284 case SCH_DIRECTIVE_LABEL_T: aLayer = LAYER_NETCLASS_REFS; break;
2285 case SCH_LABEL_T: aLayer = LAYER_LOCLABEL; break;
2286 case SCH_TEXT_T: aLayer = aText->GetParentSymbol() ? LAYER_DEVICE
2287 : LAYER_NOTES; break;
2288 default: aLayer = LAYER_NOTES; break;
2289 }
2290
2291 COLOR4D color = getRenderColor( aText, aLayer, drawingShadows, aDimmed );
2292
2293 if( m_schematic )
2294 {
2295 SCH_CONNECTION* conn = nullptr;
2296
2297 if( !aText->IsConnectivityDirty() )
2298 conn = aText->Connection();
2299
2300 if( conn && conn->IsBus() )
2301 color = getRenderColor( aText, LAYER_BUS, drawingShadows );
2302 }
2303
2304 if( !( aText->IsVisible() || aText->IsForceVisible() ) )
2305 {
2306 if( m_schSettings.m_IsSymbolEditor || eeconfig()->m_Appearance.show_hidden_fields )
2307 color = getRenderColor( aText, LAYER_HIDDEN, drawingShadows );
2308 else
2309 return;
2310 }
2311
2312 m_gal->SetStrokeColor( color );
2313 m_gal->SetFillColor( color );
2314 m_gal->SetHoverColor( color );
2315
2316 wxString shownText( aText->GetShownText( true ) );
2317 VECTOR2I text_offset = aText->GetSchematicTextOffset( &m_schSettings );
2318 TEXT_ATTRIBUTES attrs = aText->GetAttributes();
2319 KIFONT::FONT* font = getFont( aText );
2320
2321 attrs.m_Angle = aText->GetDrawRotation();
2322 attrs.m_StrokeWidth = KiROUND( getTextThickness( aText ) );
2323
2324 if( drawingShadows && font->IsOutline() )
2325 {
2326 // Trying to draw glyph-shaped shadows on outline text is a fool's errand. Just box it.
2327 // Use GetBoundingBox() which correctly handles multiline text dimensions.
2328 BOX2I bbox = aText->GetBoundingBox();
2329
2330 bbox.Inflate( attrs.m_StrokeWidth / 2, attrs.m_StrokeWidth * 2 );
2331
2332 m_gal->SetIsFill( true );
2333 m_gal->SetIsStroke( false );
2334 m_gal->DrawRectangle( bbox.GetOrigin(), bbox.GetEnd() );
2335 }
2336 else if( aText->GetLayer() == LAYER_DEVICE )
2337 {
2338 BOX2I bBox = aText->GetBoundingBox();
2339 VECTOR2D pos = bBox.Centre();
2340
2341 // Due to the fact a shadow text can be drawn left or right aligned, it needs to be
2342 // offset by shadowWidth/2 to be drawn at the same place as normal text.
2343 double shadowOffset = 0.0;
2344
2345 if( drawingShadows )
2346 {
2347 double shadowWidth = getShadowWidth( !aText->IsSelected() );
2348 attrs.m_StrokeWidth += getShadowWidth( !aText->IsSelected() );
2349
2350 const double adjust = 1.2f; // Value chosen after tests
2351 shadowOffset = shadowWidth/2.0f * adjust;
2352 }
2353
2354 if( attrs.m_Angle == ANGLE_VERTICAL )
2355 {
2356 switch( attrs.m_Halign )
2357 {
2359 pos.y = bBox.GetBottom() + shadowOffset;
2360 break;
2362 pos.y = ( bBox.GetTop() + bBox.GetBottom() ) / 2.0;
2363 break;
2365 pos.y = bBox.GetTop() - shadowOffset;
2366 break;
2368 wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) );
2369 break;
2370 }
2371 }
2372 else
2373 {
2374 switch( attrs.m_Halign )
2375 {
2377 pos.x = bBox.GetLeft() - shadowOffset;
2378 break;
2380 pos.x = ( bBox.GetLeft() + bBox.GetRight() ) / 2.0;
2381 break;
2383 pos.x = bBox.GetRight() + shadowOffset;
2384 break;
2386 wxFAIL_MSG( wxT( "Indeterminate state legal only in dialogs." ) );
2387 break;
2388 }
2389 }
2390
2391 // Because the text vertical position is the bounding box center, the text is drawn as
2392 // vertically centered.
2394
2395 strokeText( *m_gal, shownText, pos, attrs, aText->GetFontMetrics() );
2396 }
2397 else if( drawingShadows )
2398 {
2399 m_gal->SetIsFill( false );
2400 m_gal->SetIsStroke( true );
2401 attrs.m_StrokeWidth += KiROUND( getShadowWidth( !aText->IsSelected() ) );
2402 attrs.m_Underlined = false;
2403
2404 // Fudge factors to match 6.0 positioning
2405 // New text stroking has width dependent offset but we need to center the shadow on the
2406 // stroke. NB this offset is in font.cpp also.
2407 int fudge = KiROUND( getShadowWidth( !aText->IsSelected() ) / 1.52 );
2408
2409 if( attrs.m_Halign == GR_TEXT_H_ALIGN_LEFT && attrs.m_Angle == ANGLE_0 )
2410 text_offset.x -= fudge;
2411 else if( attrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT && attrs.m_Angle == ANGLE_90 )
2412 text_offset.y -= fudge;
2413 else if( attrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT && attrs.m_Angle == ANGLE_0 )
2414 text_offset.x += fudge;
2415 else if( attrs.m_Halign == GR_TEXT_H_ALIGN_LEFT && attrs.m_Angle == ANGLE_90 )
2416 text_offset.y += fudge;
2417
2418 strokeText( *m_gal, shownText, aText->GetDrawPos() + text_offset, attrs, aText->GetFontMetrics() );
2419 }
2420 else
2421 {
2422 wxString activeUrl;
2423
2424 if( aText->IsRollover() && !aText->IsMoving() )
2425 {
2426 // Highlight any urls found within the text
2427 m_gal->SetHoverColor( m_schSettings.GetLayerColor( LAYER_HOVERED ) );
2428
2429 // Highlight the whole text if it has a link definition
2430 if( aText->HasHyperlink() )
2431 {
2432 attrs.m_Hover = true;
2433 attrs.m_Underlined = true;
2434 activeUrl = aText->GetHyperlink();
2435 }
2436 }
2437
2438 if( aText->Type() == SCH_TEXT_T )
2439 text_offset += aText->GetOffsetToMatchSCH_FIELD( nullptr );
2440
2441 if( nonCached( aText ) && aText->RenderAsBitmap( m_gal->GetWorldScale() )
2442 && !shownText.Contains( wxT( "\n" ) ) )
2443 {
2444 bitmapText( *m_gal, shownText, aText->GetDrawPos() + text_offset, attrs );
2445 const_cast<SCH_TEXT*>( aText )->SetFlags( IS_SHOWN_AS_BITMAP );
2446 }
2447 else
2448 {
2449 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
2450
2451 if( !aText->IsRollover() && font->IsOutline() )
2452 cache = aText->GetRenderCache( font, shownText, text_offset );
2453
2454 if( cache )
2455 {
2456 m_gal->SetLineWidth( attrs.m_StrokeWidth );
2457 m_gal->DrawGlyphs( *cache );
2458 }
2459 else
2460 {
2461 strokeText( *m_gal, shownText, aText->GetDrawPos() + text_offset, attrs,
2462 aText->GetFontMetrics(), aText->GetRolloverPos(), &activeUrl );
2463 }
2464
2465 const_cast<SCH_TEXT*>( aText )->ClearFlags( IS_SHOWN_AS_BITMAP );
2466 }
2467
2468 aText->SetActiveUrl( activeUrl );
2469 }
2470
2471 // Draw anchor
2472 if( aText->IsSelected() )
2473 {
2474 bool showAnchor;
2475
2476 switch( aText->Type() )
2477 {
2478 case SCH_TEXT_T:
2479 showAnchor = true;
2480 break;
2481
2482 case SCH_LABEL_T:
2483 // Don't clutter things up if we're already showing a dangling indicator
2484 showAnchor = !static_cast<const SCH_LABEL*>( aText )->IsDangling();
2485 break;
2486
2488 case SCH_HIER_LABEL_T:
2489 case SCH_GLOBAL_LABEL_T:
2490 case SCH_SHEET_PIN_T:
2491 // These all have shapes and so don't need anchors
2492 showAnchor = false;
2493 break;
2494
2495 default:
2496 showAnchor = false;
2497 break;
2498 }
2499
2500 if( showAnchor )
2501 drawAnchor( aText->GetPosition(), drawingShadows );
2502 }
2503}
2504
2505
2506void SCH_PAINTER::draw( const SCH_TEXTBOX* aTextBox, int aLayer, bool aDimmed )
2507{
2508 if( aTextBox->Type() == SCH_TABLECELL_T )
2509 {
2510 const SCH_TABLECELL* cell = static_cast<const SCH_TABLECELL*>( aTextBox );
2511
2512 if( cell->GetColSpan() == 0 || cell->GetRowSpan() == 0 )
2513 return;
2514 }
2515
2516 if( !isUnitAndConversionShown( aTextBox ) )
2517 return;
2518
2519 if( aTextBox->IsPrivate() && !m_schSettings.m_IsSymbolEditor )
2520 return;
2521
2522 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
2523
2524 if( m_schSettings.IsPrinting() && drawingShadows )
2525 return;
2526
2527 COLOR4D color = getRenderColor( aTextBox, aLayer, drawingShadows, aDimmed );
2528 COLOR4D bg = m_schSettings.GetLayerColor( LAYER_SCHEMATIC_BACKGROUND );
2529 float borderWidth = getLineWidth( aTextBox, drawingShadows );
2530 KIFONT::FONT* font = getFont( aTextBox );
2531
2532 auto drawText =
2533 [&]()
2534 {
2535 wxString shownText = aTextBox->GetShownText( true );
2536 TEXT_ATTRIBUTES attrs = aTextBox->GetAttributes();
2537 wxString activeUrl;
2538
2539 attrs.m_Angle = aTextBox->GetDrawRotation();
2540 attrs.m_StrokeWidth = KiROUND( getTextThickness( aTextBox ) );
2541
2542 if( aTextBox->IsRollover() && !aTextBox->IsMoving() )
2543 {
2544 // Highlight any urls found within the text
2545 m_gal->SetHoverColor( m_schSettings.GetLayerColor( LAYER_HOVERED ) );
2546
2547 // Highlight the whole text if it has a link definition
2548 if( aTextBox->HasHyperlink() )
2549 {
2550 attrs.m_Hover = true;
2551 attrs.m_Underlined = true;
2552 activeUrl = aTextBox->GetHyperlink();
2553 }
2554 }
2555
2556 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
2557
2558 if( !aTextBox->IsRollover() && font->IsOutline() )
2559 cache = aTextBox->GetRenderCache( font, shownText );
2560
2561 if( cache )
2562 {
2563 m_gal->SetLineWidth( attrs.m_StrokeWidth );
2564 m_gal->DrawGlyphs( *cache );
2565 }
2566 else
2567 {
2568 strokeText( *m_gal, shownText, aTextBox->GetDrawPos(), attrs,
2569 aTextBox->GetFontMetrics(), aTextBox->GetRolloverPos(), &activeUrl );
2570 }
2571
2572 aTextBox->SetActiveUrl( activeUrl );
2573 };
2574
2575 if( drawingShadows && !( aTextBox->IsBrightened() || aTextBox->IsSelected() ) )
2576 return;
2577
2578 m_gal->SetFillColor( color );
2579 m_gal->SetStrokeColor( color );
2580 m_gal->SetHoverColor( color );
2581
2582 if( aLayer == LAYER_SELECTION_SHADOWS )
2583 {
2584 m_gal->SetIsFill( true );
2585 m_gal->SetIsStroke( false );
2586 m_gal->SetLineWidth( borderWidth );
2587
2588 m_gal->DrawRectangle( aTextBox->GetPosition(), aTextBox->GetEnd() );
2589 }
2590 else if( aLayer == LAYER_DEVICE_BACKGROUND || aLayer == LAYER_NOTES_BACKGROUND
2591 || aLayer == LAYER_SHAPES_BACKGROUND )
2592 {
2593 // Do not fill the shape in B&W print mode, to avoid to visible items
2594 // inside the shape
2595 if( aTextBox->IsSolidFill() && !m_schSettings.PrintBlackAndWhiteReq() )
2596 {
2597 m_gal->SetIsFill( true );
2598 m_gal->SetIsStroke( false );
2599 m_gal->SetLineWidth( borderWidth );
2600
2601 m_gal->DrawRectangle( aTextBox->GetPosition(), aTextBox->GetEnd() );
2602 }
2603 }
2604 else if( aLayer == LAYER_DEVICE || aLayer == LAYER_NOTES || aLayer == LAYER_PRIVATE_NOTES )
2605 {
2606 drawText();
2607
2608 if( aTextBox->Type() != SCH_TABLECELL_T && borderWidth > 0 )
2609 {
2610 COLOR4D borderColor = aTextBox->GetStroke().GetColor();
2611 LINE_STYLE borderStyle = aTextBox->GetEffectiveLineStyle();
2612 double transparency = aTextBox->GetForcedTransparency();
2613
2614 if( m_schSettings.m_OverrideItemColors || aTextBox->IsBrightened()
2615 || borderColor == COLOR4D::UNSPECIFIED )
2616 {
2617 borderColor = m_schSettings.GetLayerColor( aLayer );
2618 }
2619
2620 if( transparency > 0.0 )
2621 borderColor = borderColor.WithAlpha( borderColor.a * ( 1.0 - transparency ) );
2622
2623 if( aDimmed )
2624 {
2625 borderColor = borderColor.Mix( bg, 0.5f );
2626 borderColor.Desaturate( );
2627 }
2628
2629 m_gal->SetIsFill( false );
2630 m_gal->SetIsStroke( true );
2631 m_gal->SetStrokeColor( borderColor );
2632 m_gal->SetLineWidth( borderWidth );
2633
2634 if( borderStyle <= LINE_STYLE::FIRST_TYPE || drawingShadows )
2635 {
2636 m_gal->DrawRectangle( aTextBox->GetPosition(), aTextBox->GetEnd() );
2637 }
2638 else
2639 {
2640 std::vector<SHAPE*> shapes = aTextBox->MakeEffectiveShapes( true );
2641
2642 for( SHAPE* shape : shapes )
2643 {
2644 STROKE_PARAMS::Stroke( shape, borderStyle, KiROUND( borderWidth ),
2646 [this]( const VECTOR2I& a, const VECTOR2I& b )
2647 {
2648 // DrawLine has problem with 0 length lines so enforce minimum
2649 if( a == b )
2650 m_gal->DrawLine( a+1, b );
2651 else
2652 m_gal->DrawLine( a, b );
2653 } );
2654 }
2655
2656 for( SHAPE* shape : shapes )
2657 delete shape;
2658 }
2659 }
2660 }
2661}
2662
2663
2664void SCH_PAINTER::draw( const SCH_TABLE* aTable, int aLayer, bool aDimmed )
2665{
2666 if( aTable->GetCells().empty() )
2667 return;
2668
2669 for( SCH_TABLECELL* cell : aTable->GetCells() )
2670 draw( cell, aLayer, aDimmed );
2671
2672 if( aLayer == LAYER_SELECTION_SHADOWS )
2673 return;
2674
2675 aTable->DrawBorders(
2676 [&]( const VECTOR2I& ptA, const VECTOR2I& ptB, const STROKE_PARAMS& stroke )
2677 {
2678 int lineWidth = stroke.GetWidth();
2679 COLOR4D color = stroke.GetColor();
2680 LINE_STYLE lineStyle = stroke.GetLineStyle();
2681
2682 if( lineWidth == 0 )
2683 lineWidth = m_schSettings.GetDefaultPenWidth();
2684
2685 if( color == COLOR4D::UNSPECIFIED )
2686 color = m_schSettings.GetLayerColor( LAYER_NOTES );
2687
2688 if( lineStyle == LINE_STYLE::DEFAULT )
2689 lineStyle = LINE_STYLE::SOLID;
2690
2691 m_gal->SetIsFill( false );
2692 m_gal->SetIsStroke( true );
2693 m_gal->SetStrokeColor( color );
2694 m_gal->SetLineWidth( (float) lineWidth );
2695
2696 if( lineStyle <= LINE_STYLE::FIRST_TYPE )
2697 {
2698 m_gal->DrawLine( ptA, ptB );
2699 }
2700 else
2701 {
2702 SHAPE_SEGMENT seg( ptA, ptB );
2703 STROKE_PARAMS::Stroke( &seg, lineStyle, lineWidth, &m_schSettings,
2704 [&]( const VECTOR2I& a, const VECTOR2I& b )
2705 {
2706 // DrawLine has problem with 0 length lines so enforce minimum
2707 if( a == b )
2708 m_gal->DrawLine( a+1, b );
2709 else
2710 m_gal->DrawLine( a, b );
2711 } );
2712 }
2713 } );
2714}
2715
2716
2717wxString SCH_PAINTER::expandLibItemTextVars( const wxString& aSourceText,
2718 const SCH_SYMBOL* aSymbolContext )
2719{
2720 std::function<bool( wxString* )> symbolResolver =
2721 [&]( wxString* token ) -> bool
2722 {
2723 if( !m_schematic )
2724 return false;
2725
2726 return aSymbolContext->ResolveTextVar( &m_schematic->CurrentSheet(), token );
2727 };
2728
2729 return ExpandTextVars( aSourceText, &symbolResolver );
2730}
2731
2732
2733void SCH_PAINTER::draw( const SCH_SYMBOL* aSymbol, int aLayer )
2734{
2735 auto t1 = std::chrono::high_resolution_clock::now();
2736 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
2737
2738 std::optional<SCH_SHEET_PATH> optSheetPath;
2739
2740 wxString variantName;
2741
2742 if( m_schematic )
2743 {
2744 optSheetPath = m_schematic->CurrentSheet();
2745 variantName = m_schematic->GetCurrentVariant();
2746 wxLogTrace( traceSchPainter,
2747 "SCH_PAINTER::draw symbol %s: Current sheet path='%s', variant='%s', size=%zu, empty=%d",
2748 aSymbol->m_Uuid.AsString(),
2749 variantName.IsEmpty() ? GetDefaultVariantName() : variantName,
2750 optSheetPath->Path().AsString(),
2751 optSheetPath->size(),
2752 optSheetPath->empty() ? 1 : 0 );
2753 }
2754
2755 SCH_SHEET_PATH* sheetPath = optSheetPath ? &optSheetPath.value() : nullptr;
2756 bool DNP = aSymbol->GetDNP( sheetPath, variantName );
2757 bool markExclusion = eeconfig()->m_Appearance.mark_sim_exclusions && aSymbol->GetExcludedFromSim( sheetPath,
2758 variantName );
2759
2760 if( m_schSettings.IsPrinting() && drawingShadows )
2761 return;
2762
2763 if( !drawingShadows || eeconfig()->m_Selection.draw_selected_children )
2764 {
2765 for( const SCH_FIELD& field : aSymbol->GetFields() )
2766 draw( &field, aLayer, DNP );
2767 }
2768
2769 if( isFieldsLayer( aLayer ) )
2770 return;
2771
2772 if( drawingShadows && !( aSymbol->IsBrightened() || aSymbol->IsSelected() ) )
2773 {
2774 // Don't exit here; symbol may still have selected pins
2775 // return;
2776 }
2777
2778 int unit = m_schematic ? aSymbol->GetUnitSelection( &m_schematic->CurrentSheet() ) : 1;
2779 int bodyStyle = aSymbol->GetBodyStyle();
2780
2781 // Use dummy symbol if the actual couldn't be found (or couldn't be locked).
2782 LIB_SYMBOL* originalSymbol = aSymbol->GetLibSymbolRef() ? aSymbol->GetLibSymbolRef().get()
2784 std::vector<SCH_PIN*> originalPins = originalSymbol->GetGraphicalPins( unit, bodyStyle );
2785
2786 // Copy the source so we can re-orient and translate it.
2787 auto tCopy1 = std::chrono::high_resolution_clock::now();
2788 LIB_SYMBOL tempSymbol( *originalSymbol, nullptr, false );
2789 auto tCopy2 = std::chrono::high_resolution_clock::now();
2790
2791 if( std::chrono::duration_cast<std::chrono::microseconds>( tCopy2 - tCopy1 ).count() > 100 )
2792 {
2793 wxLogTrace( traceSchPainter, "SCH_PAINTER::draw symbol copy %s: %lld us", aSymbol->m_Uuid.AsString(),
2794 std::chrono::duration_cast<std::chrono::microseconds>( tCopy2 - tCopy1 ).count() );
2795 }
2796
2797 std::vector<SCH_PIN*> tempPins = tempSymbol.GetGraphicalPins( unit, bodyStyle );
2798
2799 tempSymbol.SetFlags( aSymbol->GetFlags() );
2800
2801 OrientAndMirrorSymbolItems( &tempSymbol, aSymbol->GetOrientation() );
2802
2803 for( SCH_ITEM& tempItem : tempSymbol.GetDrawItems() )
2804 {
2805 tempItem.SetFlags( aSymbol->GetFlags() ); // SELECTED, HIGHLIGHTED, BRIGHTENED,
2806 tempItem.Move( aSymbol->GetPosition() );
2807
2808 if( tempItem.Type() == SCH_TEXT_T )
2809 {
2810 SCH_TEXT* textItem = static_cast<SCH_TEXT*>( &tempItem );
2811
2812 if( textItem->HasTextVars() )
2813 textItem->SetText( expandLibItemTextVars( textItem->GetText(), aSymbol ) );
2814 }
2815 else if( tempItem.Type() == SCH_TEXTBOX_T )
2816 {
2817 SCH_TEXTBOX* textboxItem = static_cast<SCH_TEXTBOX*>( &tempItem );
2818
2819 if( textboxItem->HasTextVars() )
2820 textboxItem->SetText( expandLibItemTextVars( textboxItem->GetText(), aSymbol ) );
2821 }
2822 }
2823
2824 // Copy the pin info from the symbol to the temp pins
2825 for( unsigned i = 0; i < tempPins.size(); ++ i )
2826 {
2827 SCH_PIN* symbolPin = aSymbol->GetPin( originalPins[ i ] );
2828 SCH_PIN* tempPin = tempPins[ i ];
2829
2830 if( !symbolPin )
2831 continue;
2832
2833 tempPin->ClearFlags();
2834 tempPin->SetFlags( symbolPin->GetFlags() ); // SELECTED, HIGHLIGHTED, BRIGHTENED,
2835 // IS_SHOWN_AS_BITMAP
2836
2837 tempPin->SetName( expandLibItemTextVars( symbolPin->GetShownName(), aSymbol ) );
2838 tempPin->SetType( symbolPin->GetType() );
2839 tempPin->SetShape( symbolPin->GetShape() );
2840
2841 if( symbolPin->IsDangling() )
2842 tempPin->SetFlags( IS_DANGLING );
2843 else
2844 tempPin->ClearFlags( IS_DANGLING );
2845
2846 tempPin->SetOperatingPoint( symbolPin->GetOperatingPoint() );
2847 }
2848
2849 draw( &tempSymbol, aLayer, false, aSymbol->GetUnit(), aSymbol->GetBodyStyle(), DNP );
2850
2851 for( unsigned i = 0; i < tempPins.size(); ++i )
2852 {
2853 SCH_PIN* symbolPin = aSymbol->GetPin( originalPins[ i ] );
2854 SCH_PIN* tempPin = tempPins[ i ];
2855
2856 if( !symbolPin )
2857 continue;
2858
2859 symbolPin->ClearFlags();
2860 tempPin->ClearFlags( IS_DANGLING ); // Clear this temporary flag
2861 symbolPin->SetFlags( tempPin->GetFlags() ); // SELECTED, HIGHLIGHTED, BRIGHTENED,
2862 // IS_SHOWN_AS_BITMAP
2863 }
2864
2865 // Draw DNP and EXCLUDE from SIM markers.
2866 // These drawings are associated to the symbol body, so draw them only when the LAYER_DEVICE
2867 // is drawn (to avoid draw artifacts).
2868 if( DNP && aLayer == LAYER_DEVICE )
2869 {
2870 COLOR4D marker_color = m_schSettings.GetLayerColor( LAYER_DNP_MARKER );
2871 BOX2I bbox = aSymbol->GetBodyBoundingBox();
2872 BOX2I pins = aSymbol->GetBodyAndPinsBoundingBox();
2873 VECTOR2D margins( std::max( bbox.GetX() - pins.GetX(), pins.GetEnd().x - bbox.GetEnd().x ),
2874 std::max( bbox.GetY() - pins.GetY(), pins.GetEnd().y - bbox.GetEnd().y ) );
2875 int strokeWidth = 3 * schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
2876
2877 margins.x = std::max( margins.x * 0.6, margins.y * 0.3 );
2878 margins.y = std::max( margins.y * 0.6, margins.x * 0.3 );
2879 bbox.Inflate( KiROUND( margins.x ), KiROUND( margins.y ) );
2880
2881 VECTOR2I pt1 = bbox.GetOrigin();
2882 VECTOR2I pt2 = bbox.GetEnd();
2883
2885 m_gal->AdvanceDepth();
2886 m_gal->SetIsStroke( true );
2887 m_gal->SetIsFill( true );
2888 m_gal->SetStrokeColor( marker_color );
2889 m_gal->SetFillColor( marker_color );
2890
2891 m_gal->DrawSegment( pt1, pt2, strokeWidth );
2892 std::swap( pt1.x, pt2.x );
2893 m_gal->DrawSegment( pt1, pt2, strokeWidth );
2894 }
2895
2896 if( markExclusion && aLayer == LAYER_DEVICE )
2897 {
2898 COLOR4D marker_color = m_schSettings.GetLayerColor( LAYER_EXCLUDED_FROM_SIM );
2899 BOX2I bbox = aSymbol->GetBodyBoundingBox();
2900 int strokeWidth = schIUScale.MilsToIU( ADVANCED_CFG::GetCfg().m_ExcludeFromSimulationLineWidth );
2901
2902 bbox.Inflate( KiROUND( strokeWidth * 0.5 ) );
2903
2905 m_gal->AdvanceDepth();
2906 m_gal->SetIsStroke( true );
2907 m_gal->SetIsFill( true );
2908 m_gal->SetStrokeColor( marker_color );
2909 m_gal->SetFillColor( marker_color );
2910
2911 m_gal->DrawSegment( bbox.GetPosition(), VECTOR2D( bbox.GetEnd().x, bbox.GetY() ), strokeWidth );
2912 m_gal->DrawSegment( VECTOR2D( bbox.GetEnd().x, bbox.GetY() ), bbox.GetEnd(), strokeWidth );
2913 m_gal->DrawSegment( bbox.GetEnd(), VECTOR2D( bbox.GetX(), bbox.GetEnd().y ), strokeWidth );
2914 m_gal->DrawSegment( VECTOR2D( bbox.GetX(), bbox.GetEnd().y ), bbox.GetPosition(), strokeWidth );
2915
2916 int offset = 2 * strokeWidth;
2917 VECTOR2D center = bbox.GetEnd() + VECTOR2D( offset + strokeWidth, -offset );
2918 VECTOR2D left = center + VECTOR2D( -offset, 0 );
2919 VECTOR2D right = center + VECTOR2D( offset, 0 );
2920 VECTOR2D top = center + VECTOR2D( 0, offset );
2921 VECTOR2D bottom = center + VECTOR2D( 0, -offset );
2922
2923 m_gal->SetFillColor( marker_color.WithAlpha( 0.1 ) );
2924 m_gal->DrawCircle( center, offset );
2925 m_gal->AdvanceDepth();
2926 m_gal->SetFillColor( marker_color );
2927 m_gal->DrawCurve( left, top, bottom, right, 1 );
2928 }
2929
2930 auto t2 = std::chrono::high_resolution_clock::now();
2931
2932 if( std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count() > 100 )
2933 {
2934 wxLogTrace( traceSchPainter, "SCH_PAINTER::draw symbol %s: %lld us", aSymbol->m_Uuid.AsString(),
2935 std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count() );
2936 }
2937}
2938
2939
2940void SCH_PAINTER::draw( const SCH_FIELD* aField, int aLayer, bool aDimmed )
2941{
2942 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
2943
2944 if( m_schSettings.IsPrinting() && drawingShadows )
2945 return;
2946
2947 if( drawingShadows && !( aField->IsBrightened() || aField->IsSelected() ) )
2948 return;
2949
2950 if( !isUnitAndConversionShown( aField ) )
2951 return;
2952
2953 if( aField->IsPrivate() && !m_schSettings.m_IsSymbolEditor )
2954 return;
2955
2956 // Must check layer as fields are sometimes drawn by their parent rather than directly
2957 // from the view.
2958 std::vector<int> layers = aField->ViewGetLayers();
2959
2960 if( std::find( layers.begin(), layers.end(), aLayer ) == layers.end() )
2961 return;
2962
2963 aLayer = aField->GetLayer();
2964
2965 COLOR4D color = getRenderColor( aField, aLayer, drawingShadows, aDimmed );
2966
2967 if( !( aField->IsVisible() || aField->IsForceVisible() ) )
2968 {
2970 : m_schSettings.m_ShowHiddenFields;
2971
2972 if( force_show )
2973 color = getRenderColor( aField, LAYER_HIDDEN, drawingShadows, aDimmed );
2974 else
2975 return;
2976 }
2977
2978 SCH_SHEET_PATH* sheetPath = nullptr;
2979 wxString variant;
2980
2981 if( m_schematic )
2982 {
2983 sheetPath = &m_schematic->CurrentSheet();
2984 variant = m_schematic->GetCurrentVariant();
2985 }
2986
2987 wxString shownText = aField->GetShownText( sheetPath, true, 0, variant );
2988
2989 if( shownText.IsEmpty() )
2990 return;
2991
2992 // Calculate the text orientation according to the parent orientation.
2993 EDA_ANGLE orient = aField->GetTextAngle();
2994
2995 if( aField->GetParent() && aField->GetParent()->Type() == SCH_SYMBOL_T )
2996 {
2997 if( static_cast<SCH_SYMBOL*>( aField->GetParent() )->GetTransform().y1 )
2998 {
2999 // Rotate symbol 90 degrees.
3000 if( orient.IsHorizontal() )
3001 orient = ANGLE_VERTICAL;
3002 else
3003 orient = ANGLE_HORIZONTAL;
3004 }
3005 }
3006
3007 /*
3008 * Calculate the text justification, according to the symbol orientation/mirror.
3009 * This is a bit complicated due to cumulative calculations:
3010 * - numerous cases (mirrored or not, rotation)
3011 * - the DrawGraphicText function recalculate also H and H justifications according to the
3012 * text orientation.
3013 * - when symbol is mirrored, the text is not mirrored and justifications are complicated
3014 * to calculate so the easier way is to use no justifications (centered text) and use
3015 * GetBoundingBox to know the text coordinate considered as centered
3016 */
3017 BOX2I bbox = aField->GetBoundingBox();
3018
3019 if( aField->GetParent() && aField->GetParent()->Type() == SCH_GLOBAL_LABEL_T )
3020 {
3021 SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( aField->GetParent() );
3022 bbox.Offset( label->GetSchematicTextOffset( &m_schSettings ) );
3023 }
3024
3025 if( m_schSettings.GetDrawBoundingBoxes() )
3026 drawItemBoundingBox( aField );
3027
3028 TEXT_ATTRIBUTES attributes = aField->GetAttributes();
3029 attributes.m_StrokeWidth = KiROUND( getTextThickness( aField ) );
3030
3031 m_gal->SetStrokeColor( color );
3032 m_gal->SetFillColor( color );
3033 m_gal->SetHoverColor( color );
3034
3035 if( drawingShadows && getFont( aField )->IsOutline() )
3036 {
3037 // Trying to draw glyph-shaped shadows on outline text is a fool's errand. Just box it.
3038 VECTOR2I textpos = bbox.Centre();
3039
3040 attributes.m_Halign = GR_TEXT_H_ALIGN_CENTER;
3041 attributes.m_Valign = GR_TEXT_V_ALIGN_CENTER;
3042 attributes.m_Angle = orient;
3043 boxText( *m_gal, shownText, textpos, attributes, aField->GetFontMetrics() );
3044 }
3045 else
3046 {
3047 VECTOR2I textpos = bbox.Centre();
3048
3049 attributes.m_Halign = GR_TEXT_H_ALIGN_CENTER;
3050 attributes.m_Valign = GR_TEXT_V_ALIGN_CENTER;
3051 attributes.m_Angle = orient;
3052
3053 if( drawingShadows )
3054 attributes.m_StrokeWidth += getShadowWidth( !aField->IsSelected() );
3055
3056 if( aField->IsRollover() && !aField->IsMoving() )
3057 {
3058 // Highlight any urls found within the text
3059 m_gal->SetHoverColor( m_schSettings.GetLayerColor( LAYER_HOVERED ) );
3060
3061 // Highlight the whole text if it has a link definition
3062 if( aField->HasHyperlink() )
3063 {
3064 attributes.m_Hover = true;
3065 attributes.m_Underlined = true;
3066 }
3067 }
3068
3069 if( nonCached( aField ) && aField->RenderAsBitmap( m_gal->GetWorldScale() ) )
3070 {
3071 bitmapText( *m_gal, shownText, textpos, attributes );
3072 const_cast<SCH_FIELD*>( aField )->SetFlags( IS_SHOWN_AS_BITMAP );
3073 }
3074 else
3075 {
3076 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
3077
3078 if( !aField->IsRollover() )
3079 cache = aField->GetRenderCache( shownText, textpos, attributes );
3080
3081 if( cache )
3082 {
3083 m_gal->SetLineWidth( attributes.m_StrokeWidth );
3084 m_gal->DrawGlyphs( *cache );
3085 }
3086 else
3087 {
3088 strokeText( *m_gal, shownText, textpos, attributes, aField->GetFontMetrics(),
3089 aField->GetRolloverPos() );
3090 }
3091
3092 const_cast<SCH_FIELD*>( aField )->ClearFlags( IS_SHOWN_AS_BITMAP );
3093 }
3094 }
3095
3096 if( aField->GetParent() && aField->GetParent()->Type() == SCH_SYMBOL_T )
3097 {
3098 SCH_SYMBOL* parent = static_cast<SCH_SYMBOL*>( aField->GetParent() );
3099 bool rotated = !orient.IsHorizontal();
3100
3101 VECTOR2D pos;
3102 double size = bbox.GetHeight() / 1.5;
3103
3104 if( rotated )
3105 {
3106 pos = VECTOR2D( bbox.GetRight() - bbox.GetWidth() / 6.0,
3107 bbox.GetBottom() + bbox.GetWidth() / 2.0 );
3108 size = bbox.GetWidth() / 1.5;
3109 }
3110 else
3111 {
3112 pos = VECTOR2D( bbox.GetLeft() - bbox.GetHeight() / 2.0,
3113 bbox.GetBottom() - bbox.GetHeight() / 6.0 );
3114 }
3115
3116 if( parent->IsSymbolLikePowerLocalLabel() && aField->GetId() == FIELD_T::VALUE )
3117 drawLocalPowerIcon( pos, size, rotated, color, drawingShadows, aField->IsBrightened() );
3118 }
3119
3120 // Draw anchor or umbilical line. The umbilical line shows independent motion of a field
3121 // relative to its parent; suppress it when the parent is also moving (e.g. dragging the
3122 // whole label) or its endpoints would span the entire label, drawing a long stray line.
3123 SCH_ITEM* fieldParent = dynamic_cast<SCH_ITEM*>( aField->GetParent() );
3124 bool parentMoving = fieldParent && fieldParent->IsMoving();
3125
3126 if( aField->IsMoving() && !parentMoving && m_schematic )
3127 {
3128 VECTOR2I parentPos = aField->GetParentPosition();
3129
3130 m_gal->SetLineWidth( m_schSettings.GetOutlineWidth() );
3131 m_gal->SetStrokeColor( getRenderColor( aField, LAYER_SCHEMATIC_ANCHOR, drawingShadows ) );
3132 m_gal->DrawLine( aField->GetPosition(), parentPos );
3133 }
3134 else if( aField->IsSelected() && !parentMoving )
3135 {
3136 drawAnchor( aField->GetPosition(), drawingShadows );
3137 }
3138}
3139
3140
3141void SCH_PAINTER::draw( const SCH_GLOBALLABEL* aLabel, int aLayer, bool aDimmed )
3142{
3143 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
3144
3145 if( m_schSettings.IsPrinting() && drawingShadows )
3146 return;
3147
3148 bool drawingDangling = aLayer == LAYER_DANGLING;
3149
3150 if( !drawingShadows || eeconfig()->m_Selection.draw_selected_children )
3151 {
3152 for( const SCH_FIELD& field : aLabel->GetFields() )
3153 draw( &field, aLayer, false );
3154 }
3155
3156 if( isFieldsLayer( aLayer ) )
3157 return;
3158
3159 if( drawingShadows && !( aLabel->IsBrightened() || aLabel->IsSelected() ) )
3160 return;
3161
3162 COLOR4D color = getRenderColor( aLabel, LAYER_GLOBLABEL, drawingShadows, aDimmed, true );
3163
3164 if( drawingDangling )
3165 {
3166 if( aLabel->IsDangling() )
3167 {
3168 drawDanglingIndicator( aLabel->GetTextPos(), color,
3169 schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE / 2 ), true,
3170 drawingShadows, aLabel->IsBrightened() );
3171 }
3172
3173 return;
3174 }
3175
3176 std::vector<VECTOR2I> pts;
3177 std::deque<VECTOR2D> pts2;
3178
3179 aLabel->CreateGraphicShape( &m_schSettings, pts, aLabel->GetTextPos() );
3180
3181 for( const VECTOR2I& p : pts )
3182 pts2.emplace_back( VECTOR2D( p.x, p.y ) );
3183
3184 m_gal->SetIsStroke( true );
3185 m_gal->SetLineWidth( getLineWidth( aLabel, drawingShadows ) );
3186 m_gal->SetStrokeColor( color );
3187
3188 if( drawingShadows )
3189 {
3190 m_gal->SetIsFill( eeconfig()->m_Selection.fill_shapes );
3191 m_gal->SetFillColor( color );
3192 m_gal->DrawPolygon( pts2 );
3193 }
3194 else
3195 {
3196 m_gal->SetIsFill( false );
3197 m_gal->DrawPolyline( pts2 );
3198 }
3199
3200 draw( static_cast<const SCH_TEXT*>( aLabel ), aLayer, false );
3201}
3202
3203
3204void SCH_PAINTER::draw( const SCH_LABEL* aLabel, int aLayer, bool aDimmed )
3205{
3206 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
3207
3208 if( m_schSettings.IsPrinting() && drawingShadows )
3209 return;
3210
3211 bool drawingDangling = aLayer == LAYER_DANGLING;
3212
3213 if( !drawingShadows || eeconfig()->m_Selection.draw_selected_children )
3214 {
3215 for( const SCH_FIELD& field : aLabel->GetFields() )
3216 draw( &field, aLayer, false );
3217 }
3218
3219 if( isFieldsLayer( aLayer ) )
3220 return;
3221
3222 if( drawingShadows && !( aLabel->IsBrightened() || aLabel->IsSelected() ) )
3223 return;
3224
3225 COLOR4D color = getRenderColor( aLabel, LAYER_HIERLABEL, drawingShadows, aDimmed, true );
3226
3227 if( drawingDangling )
3228 {
3229 if( aLabel->IsDangling() )
3230 {
3231 drawDanglingIndicator( aLabel->GetTextPos(), color,
3232 schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE / 2 ), true,
3233 drawingShadows, aLabel->IsBrightened() );
3234 }
3235
3236 return;
3237 }
3238
3239 draw( static_cast<const SCH_TEXT*>( aLabel ), aLayer, false );
3240}
3241
3242
3243void SCH_PAINTER::draw( const SCH_HIERLABEL* aLabel, int aLayer, bool aDimmed )
3244{
3245 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
3246
3247 if( m_schSettings.IsPrinting() && drawingShadows )
3248 return;
3249
3250 bool drawingDangling = aLayer == LAYER_DANGLING;
3251
3252 if( !( drawingShadows || drawingDangling ) || eeconfig()->m_Selection.draw_selected_children )
3253 {
3254 for( const SCH_FIELD& field : aLabel->GetFields() )
3255 draw( &field, aLayer, false );
3256 }
3257
3258 if( isFieldsLayer( aLayer ) )
3259 return;
3260
3261 if( drawingShadows && !( aLabel->IsBrightened() || aLabel->IsSelected() ) )
3262 return;
3263
3264 COLOR4D color = getRenderColor( aLabel, LAYER_HIERLABEL, drawingShadows, aDimmed, true );
3265
3266 if( drawingDangling )
3267 {
3268 if( aLabel->IsDangling() )
3269 {
3270 drawDanglingIndicator( aLabel->GetTextPos(), color,
3271 schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE / 2 ), true,
3272 drawingShadows, aLabel->IsBrightened() );
3273 }
3274
3275 return;
3276 }
3277
3278 std::vector<VECTOR2I> i_pts;
3279 std::deque<VECTOR2D> d_pts;
3280
3281 aLabel->CreateGraphicShape( &m_schSettings, i_pts, (VECTOR2I)aLabel->GetTextPos() );
3282
3283 for( const VECTOR2I& i_pt : i_pts )
3284 d_pts.emplace_back( VECTOR2D( i_pt.x, i_pt.y ) );
3285
3286 m_gal->SetIsFill( true );
3287 m_gal->SetFillColor( m_schSettings.GetLayerColor( LAYER_SCHEMATIC_BACKGROUND ) );
3288 m_gal->SetIsStroke( true );
3289 m_gal->SetLineWidth( getLineWidth( aLabel, drawingShadows ) );
3290 m_gal->SetStrokeColor( color );
3291 m_gal->DrawPolyline( d_pts );
3292
3293 draw( static_cast<const SCH_TEXT*>( aLabel ), aLayer, aDimmed );
3294}
3295
3296
3297void SCH_PAINTER::draw( const SCH_DIRECTIVE_LABEL* aLabel, int aLayer, bool aDimmed )
3298{
3299 if( !eeconfig()->m_Appearance.show_directive_labels && !aLabel->IsSelected() )
3300 return;
3301
3302 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
3303
3304 if( m_schSettings.IsPrinting() && drawingShadows )
3305 return;
3306
3307 if( !drawingShadows || eeconfig()->m_Selection.draw_selected_children )
3308 {
3309 for( const SCH_FIELD& field : aLabel->GetFields() )
3310 draw( &field, aLayer, false );
3311 }
3312
3313 if( isFieldsLayer( aLayer ) )
3314 return;
3315
3316 if( drawingShadows && !( aLabel->IsBrightened() || aLabel->IsSelected() ) )
3317 return;
3318
3319 COLOR4D color = getRenderColor( aLabel, LAYER_NETCLASS_REFS, drawingShadows, aDimmed, true );
3320
3321 if( aLayer == LAYER_DANGLING )
3322 {
3323 if( aLabel->IsDangling() )
3324 {
3325 drawDanglingIndicator( aLabel->GetTextPos(), color,
3326 schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE / 2 ), true,
3327 drawingShadows, aLabel->IsBrightened() );
3328 }
3329
3330 return;
3331 }
3332
3333 std::vector<VECTOR2I> pts;
3334 std::deque<VECTOR2D> pts2;
3335
3336 aLabel->CreateGraphicShape( &m_schSettings, pts, aLabel->GetTextPos() );
3337
3338 for( const VECTOR2I& p : pts )
3339 pts2.emplace_back( VECTOR2D( p.x, p.y ) );
3340
3341 m_gal->SetIsFill( false );
3342 m_gal->SetFillColor( color );
3343 m_gal->SetIsStroke( true );
3344 m_gal->SetLineWidth( getLineWidth( aLabel, drawingShadows ) );
3345 m_gal->SetStrokeColor( color );
3346
3347 if( aLabel->GetShape() == LABEL_FLAG_SHAPE::F_DOT )
3348 {
3349 m_gal->DrawLine( pts2[0], pts2[1] );
3350 m_gal->SetIsFill( true );
3351 m_gal->DrawCircle( pts2[2], ( pts2[2] - pts2[1] ).EuclideanNorm() );
3352 }
3353 else if( aLabel->GetShape() == LABEL_FLAG_SHAPE::F_ROUND )
3354 {
3355 m_gal->DrawLine( pts2[0], pts2[1] );
3356 m_gal->DrawCircle( pts2[2], ( pts2[2] - pts2[1] ).EuclideanNorm() );
3357 }
3358 else
3359 {
3360 m_gal->DrawPolyline( pts2 );
3361 }
3362}
3363
3364
3365void SCH_PAINTER::draw( const SCH_SHEET* aSheet, int aLayer )
3366{
3367 SCH_SHEET_PATH* sheetPath = nullptr;
3368 wxString variant;
3369 bool DNP = false;
3370
3371 if( m_schematic )
3372 {
3373 sheetPath = &m_schematic->CurrentSheet();
3374 variant = m_schematic->GetCurrentVariant();
3375 DNP = aSheet->GetDNP( sheetPath, variant );
3376 }
3377
3378 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
3379 bool markExclusion = eeconfig()->m_Appearance.mark_sim_exclusions
3380 && aSheet->GetExcludedFromSim( sheetPath, variant );
3381
3382 if( m_schSettings.IsPrinting() && drawingShadows )
3383 return;
3384
3385 if( !drawingShadows || eeconfig()->m_Selection.draw_selected_children )
3386 {
3387 for( const SCH_FIELD& field : aSheet->GetFields() )
3388 draw( &field, aLayer, DNP );
3389
3390 for( SCH_SHEET_PIN* sheetPin : aSheet->GetPins() )
3391 draw( static_cast<SCH_HIERLABEL*>( sheetPin ), aLayer, DNP );
3392 }
3393
3394 if( isFieldsLayer( aLayer ) )
3395 return;
3396
3397 VECTOR2D pos = aSheet->GetPosition();
3398 VECTOR2D size = aSheet->GetSize();
3399
3400 if( aLayer == LAYER_SHEET_BACKGROUND )
3401 {
3402 // Do not fill the shape in B&W print mode, to avoid to visible items
3403 // inside the shape
3404 if( !m_schSettings.PrintBlackAndWhiteReq() )
3405 {
3406 COLOR4D backgroundColor = aSheet->GetBackgroundColor();
3407
3408 if( m_schSettings.m_OverrideItemColors || backgroundColor == COLOR4D::UNSPECIFIED )
3409 backgroundColor = m_schSettings.GetLayerColor( LAYER_SHEET_BACKGROUND );
3410
3411 // Only draw the background if it has a visible alpha value
3412 if( backgroundColor.a > 0.0 )
3413 {
3414 m_gal->SetFillColor( getRenderColor( aSheet, LAYER_SHEET_BACKGROUND, false ) );
3415 m_gal->SetIsFill( true );
3416 m_gal->SetIsStroke( false );
3417
3418 m_gal->DrawRectangle( pos, pos + size );
3419 }
3420 }
3421 }
3422
3423 if( aLayer == LAYER_SHEET || aLayer == LAYER_SELECTION_SHADOWS )
3424 {
3425 m_gal->SetStrokeColor( getRenderColor( aSheet, LAYER_SHEET, drawingShadows, DNP ) );
3426 m_gal->SetIsStroke( true );
3427 m_gal->SetLineWidth( getLineWidth( aSheet, drawingShadows ) );
3428 m_gal->SetIsFill( false );
3429
3430 m_gal->DrawRectangle( pos, pos + size );
3431 }
3432
3433 if( DNP && aLayer == LAYER_SHEET )
3434 {
3435 int layer = LAYER_DNP_MARKER;
3436 BOX2I bbox = aSheet->GetBodyBoundingBox();
3437 BOX2I pins = aSheet->GetBoundingBox();
3438 VECTOR2D margins( std::max( bbox.GetX() - pins.GetX(), pins.GetEnd().x - bbox.GetEnd().x ),
3439 std::max( bbox.GetY() - pins.GetY(), pins.GetEnd().y - bbox.GetEnd().y ) );
3440 int strokeWidth = 3 * schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
3441
3442 margins.x = std::max( margins.x * 0.6, margins.y * 0.3 );
3443 margins.y = std::max( margins.y * 0.6, margins.x * 0.3 );
3444 bbox.Inflate( KiROUND( margins.x ), KiROUND( margins.y ) );
3445
3446 VECTOR2I pt1 = bbox.GetOrigin();
3447 VECTOR2I pt2 = bbox.GetEnd();
3448
3450 m_gal->AdvanceDepth();
3451 m_gal->SetIsStroke( true );
3452 m_gal->SetIsFill( true );
3453 m_gal->SetStrokeColor( m_schSettings.GetLayerColor( layer ) );
3454 m_gal->SetFillColor( m_schSettings.GetLayerColor( layer ) );
3455
3456 m_gal->DrawSegment( pt1, pt2, strokeWidth );
3457 std::swap( pt1.x, pt2.x );
3458 m_gal->DrawSegment( pt1, pt2, strokeWidth );
3459 }
3460
3461 if( markExclusion )
3462 {
3463 int layer = LAYER_EXCLUDED_FROM_SIM;
3464 BOX2I bbox = aSheet->GetBodyBoundingBox();
3465 int strokeWidth = schIUScale.MilsToIU( ADVANCED_CFG::GetCfg().m_ExcludeFromSimulationLineWidth );
3466
3467 bbox.Inflate( KiROUND( strokeWidth * 0.5 ) );
3468
3470 m_gal->AdvanceDepth();
3471 m_gal->SetIsStroke( true );
3472 m_gal->SetIsFill( true );
3473 m_gal->SetStrokeColor( m_schSettings.GetLayerColor( layer ).WithAlpha( 0.5 ) );
3474 m_gal->SetFillColor( m_schSettings.GetLayerColor( layer ).WithAlpha( 0.5 ) );
3475
3476 m_gal->DrawSegment( bbox.GetPosition(), VECTOR2D( bbox.GetEnd().x, bbox.GetY() ), strokeWidth );
3477 m_gal->DrawSegment( VECTOR2D( bbox.GetEnd().x, bbox.GetY() ), bbox.GetEnd(), strokeWidth );
3478 m_gal->DrawSegment( bbox.GetEnd(), VECTOR2D( bbox.GetX(), bbox.GetEnd().y ), strokeWidth );
3479 m_gal->DrawSegment( VECTOR2D( bbox.GetX(), bbox.GetEnd().y ), bbox.GetPosition(), strokeWidth );
3480
3481 int offset = 2 * strokeWidth;
3482 VECTOR2D center = bbox.GetEnd() + VECTOR2D( offset + strokeWidth, -offset );
3483 VECTOR2D left = center + VECTOR2D( -offset, 0 );
3484 VECTOR2D right = center + VECTOR2D( offset, 0 );
3485 VECTOR2D top = center + VECTOR2D( 0, offset );
3486 VECTOR2D bottom = center + VECTOR2D( 0, -offset );
3487
3488 m_gal->SetFillColor( m_schSettings.GetLayerColor( layer ).WithAlpha( 0.1 ) );
3489 m_gal->DrawCircle( center, offset );
3490 m_gal->AdvanceDepth();
3491 m_gal->SetFillColor( m_schSettings.GetLayerColor( layer ).WithAlpha( 0.5 ) );
3492 m_gal->DrawCurve( left, top, bottom, right, 1 );
3493 }
3494}
3495
3496
3497void SCH_PAINTER::draw( const SCH_NO_CONNECT* aNC, int aLayer )
3498{
3499 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
3500
3501 if( m_schSettings.IsPrinting() && drawingShadows )
3502 return;
3503
3504 if( drawingShadows && !( aNC->IsBrightened() || aNC->IsSelected() ) )
3505 return;
3506
3507 m_gal->SetIsStroke( true );
3508 m_gal->SetLineWidth( getLineWidth( aNC, drawingShadows ) );
3509 m_gal->SetStrokeColor( getRenderColor( aNC, LAYER_NOCONNECT, drawingShadows ) );
3510 m_gal->SetIsFill( false );
3511
3512 VECTOR2D p = aNC->GetPosition();
3513 int delta = std::max( aNC->GetSize(), m_schSettings.GetDefaultPenWidth() * 3 ) / 2;
3514
3515 m_gal->DrawLine( p + VECTOR2D( -delta, -delta ), p + VECTOR2D( delta, delta ) );
3516 m_gal->DrawLine( p + VECTOR2D( -delta, delta ), p + VECTOR2D( delta, -delta ) );
3517}
3518
3519
3520void SCH_PAINTER::draw( const SCH_BUS_ENTRY_BASE *aEntry, int aLayer )
3521{
3523 SCH_LINE line( VECTOR2I(), layer );
3524 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
3525 bool drawingNetColorHighlights = aLayer == LAYER_NET_COLOR_HIGHLIGHT;
3526 bool drawingDangling = aLayer == LAYER_DANGLING;
3527 bool drawingWires = aLayer == LAYER_WIRE;
3528 bool drawingBusses = aLayer == LAYER_BUS;
3529
3530 if( m_schSettings.IsPrinting() && drawingShadows )
3531 return;
3532
3533 bool highlightNetclassColors = false;
3534 EESCHEMA_SETTINGS* eeschemaCfg = eeconfig();
3535
3536 if( eeschemaCfg )
3537 {
3538 highlightNetclassColors = eeschemaCfg->m_Selection.highlight_netclass_colors;
3539 }
3540
3541 if( !highlightNetclassColors && drawingNetColorHighlights )
3542 return;
3543
3544 if( m_schSettings.m_OverrideItemColors && drawingNetColorHighlights )
3545 return;
3546
3547 if( drawingShadows && !( aEntry->IsBrightened() || aEntry->IsSelected() ) )
3548 return;
3549
3550 if( aEntry->IsSelected() )
3551 {
3552 line.SetSelected();
3553
3554 // Never show unselected endpoints on bus entries
3555 line.SetFlags( STARTPOINT | ENDPOINT );
3556 }
3557 else if( aEntry->IsBrightened() )
3558 {
3559 line.SetBrightened();
3560 }
3561
3562 line.SetStartPoint( aEntry->GetPosition() );
3563 line.SetEndPoint( aEntry->GetEnd() );
3564 line.SetStroke( aEntry->GetStroke() );
3565 line.SetLineWidth( KiROUND( getLineWidth( aEntry, false ) ) );
3566
3567 COLOR4D color = getRenderColor( aEntry, LAYER_WIRE, drawingShadows );
3568
3569 if( aEntry->Type() == SCH_BUS_BUS_ENTRY_T )
3570 color = getRenderColor( aEntry, LAYER_BUS, drawingShadows );
3571
3572 if( highlightNetclassColors )
3573 {
3574 // Force default color for nets we are going to highlight
3575 if( drawingWires )
3576 color = m_schSettings.GetLayerColor( LAYER_WIRE );
3577 else if( drawingBusses )
3578 color = m_schSettings.GetLayerColor( LAYER_BUS );
3579 }
3580
3581 if( drawingNetColorHighlights )
3582 {
3583 // Don't draw highlights for default-colored nets
3584 if( ( aEntry->Type() == SCH_BUS_WIRE_ENTRY_T
3585 && color == m_schSettings.GetLayerColor( LAYER_WIRE ) )
3586 || ( aEntry->Type() == SCH_BUS_BUS_ENTRY_T
3587 && color == m_schSettings.GetLayerColor( LAYER_BUS ) ) )
3588 {
3589 return;
3590 }
3591 }
3592
3593 if( drawingDangling )
3594 {
3595 m_gal->SetIsFill( false );
3596 m_gal->SetIsStroke( true );
3597 m_gal->SetStrokeColor( color.Brightened( 0.3 ) );
3598 m_gal->SetLineWidth( m_schSettings.GetDanglingIndicatorThickness() );
3599
3600 if( aEntry->IsStartDangling() )
3601 {
3602 m_gal->DrawCircle( aEntry->GetPosition(),
3603 aEntry->GetPenWidth() + KiROUND( TARGET_BUSENTRY_RADIUS / 2.0 ) );
3604 }
3605
3606 if( aEntry->IsEndDangling() )
3607 {
3608 m_gal->DrawCircle( aEntry->GetEnd(),
3609 aEntry->GetPenWidth() + KiROUND( TARGET_BUSENTRY_RADIUS / 2.0 ) );
3610 }
3611 }
3612 else
3613 {
3614 line.SetLineColor( color );
3615 line.SetLineStyle( aEntry->GetEffectiveLineStyle() );
3616
3617 draw( &line, aLayer );
3618 }
3619}
3620
3621
3622void SCH_PAINTER::draw( const SCH_BITMAP* aBitmap, int aLayer )
3623{
3624 auto t1 = std::chrono::high_resolution_clock::now();
3625 m_gal->Save();
3626 m_gal->Translate( aBitmap->GetPosition() );
3627
3628 const REFERENCE_IMAGE& refImage = aBitmap->GetReferenceImage();
3629
3630 // When the image scale factor is not 1.0, we need to modify the actual as the image scale
3631 // factor is similar to a local zoom
3632 const double img_scale = refImage.GetImageScale();
3633
3634 if( img_scale != 1.0 )
3635 m_gal->Scale( VECTOR2D( img_scale, img_scale ) );
3636
3637 if( aLayer == LAYER_DRAW_BITMAPS )
3638 {
3639 m_gal->DrawBitmap( refImage.GetImage() );
3640 }
3641
3642 if( aLayer == LAYER_SELECTION_SHADOWS )
3643 {
3644 if( aBitmap->IsSelected() || aBitmap->IsBrightened() )
3645 {
3646 const COLOR4D color = getRenderColor( aBitmap, LAYER_DRAW_BITMAPS, true );
3647 m_gal->SetIsStroke( true );
3648 m_gal->SetStrokeColor( color );
3649 m_gal->SetLineWidth ( getShadowWidth( aBitmap->IsBrightened() ) );
3650 m_gal->SetIsFill( false );
3651
3652 // Draws a bounding box.
3653 VECTOR2D bm_size( refImage.GetSize() );
3654
3655 // bm_size is the actual image size in UI.
3656 // but m_canvas scale was previously set to img_scale
3657 // so recalculate size relative to this image size.
3658 bm_size.x /= img_scale;
3659 bm_size.y /= img_scale;
3660 const VECTOR2D origin( -bm_size.x / 2.0, -bm_size.y / 2.0 );
3661 const VECTOR2D end = origin + bm_size;
3662
3663 m_gal->DrawRectangle( origin, end );
3664 }
3665 }
3666
3667 m_gal->Restore();
3668 auto t2 = std::chrono::high_resolution_clock::now();
3669
3670 if( std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count() > 100 )
3671 {
3672 wxLogTrace( traceSchPainter, "SCH_PAINTER::draw bitmap %s: %lld us", aBitmap->m_Uuid.AsString(),
3673 std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count() );
3674 }
3675}
3676
3677
3678void SCH_PAINTER::draw( const SCH_MARKER* aMarker, int aLayer )
3679{
3680 bool drawingShadows = aLayer == LAYER_SELECTION_SHADOWS;
3681
3682 if( m_schSettings.IsPrinting() && drawingShadows )
3683 return;
3684
3685 if( drawingShadows && !( aMarker->IsBrightened() || aMarker->IsSelected() ) )
3686 return;
3687
3688 COLOR4D color = getRenderColor( aMarker, aMarker->GetColorLayer(), drawingShadows );
3689
3690 m_gal->Save();
3691 m_gal->Translate( aMarker->GetPosition() );
3692 m_gal->SetIsFill( !drawingShadows );
3693 m_gal->SetFillColor( color );
3694 m_gal->SetIsStroke( drawingShadows );
3695 m_gal->SetLineWidth( getLineWidth( aMarker, drawingShadows ) );
3696 m_gal->SetStrokeColor( color );
3697
3698 SHAPE_LINE_CHAIN polygon;
3699 aMarker->ShapeToPolygon( polygon );
3700
3701 m_gal->DrawPolygon( polygon );
3702 m_gal->Restore();
3703}
3704
3705
3706void SCH_PAINTER::draw( const SCH_GROUP* aGroup, int aLayer )
3707{
3708 const bool drawingShadows = false;
3709
3710 if( aLayer == LAYER_SCHEMATIC_ANCHOR )
3711 {
3712 if( aGroup->IsSelected() && !( aGroup->GetParent() && aGroup->GetParent()->IsSelected() ) )
3713 {
3714 // Selected on our own; draw enclosing box
3715 }
3716 else if( aGroup->IsEntered() )
3717 {
3718 // Entered group; draw enclosing box
3719 }
3720 else
3721 {
3722 // Neither selected nor entered; draw nothing at the group level (ie: only draw
3723 // its members)
3724 return;
3725 }
3726
3727 const COLOR4D color = getRenderColor( aGroup, LAYER_SCHEMATIC_ANCHOR, drawingShadows );
3728
3729 m_gal->SetStrokeColor( color );
3730 m_gal->SetLineWidth( m_schSettings.GetOutlineWidth() * 2.0f );
3731
3732 BOX2I bbox = aGroup->GetBoundingBox();
3733 VECTOR2I topLeft = bbox.GetPosition();
3734 VECTOR2I width = VECTOR2I( bbox.GetWidth(), 0 );
3735 VECTOR2I height = VECTOR2I( 0, bbox.GetHeight() );
3736
3737 m_gal->DrawLine( topLeft, topLeft + width );
3738 m_gal->DrawLine( topLeft + width, topLeft + width + height );
3739 m_gal->DrawLine( topLeft + width + height, topLeft + height );
3740 m_gal->DrawLine( topLeft + height, topLeft );
3741
3742 wxString name = aGroup->GetName();
3743
3744 if( name.IsEmpty() )
3745 return;
3746
3747 int ptSize = 12;
3748 int scaledSize = abs( KiROUND( m_gal->GetScreenWorldMatrix().GetScale().x * ptSize ) );
3749 int unscaledSize = schIUScale.MilsToIU( ptSize );
3750
3751 // Scale by zoom a bit, but not too much
3752 int textSize = ( scaledSize + ( unscaledSize * 2 ) ) / 3;
3753 VECTOR2I textOffset = KiROUND( width.x / 2.0, -textSize * 0.5 );
3754 VECTOR2I titleHeight = KiROUND( 0.0, textSize * 2.0 );
3755
3756 if( PrintableCharCount( name ) * textSize < bbox.GetWidth() )
3757 {
3758 m_gal->DrawLine( topLeft, topLeft - titleHeight );
3759 m_gal->DrawLine( topLeft - titleHeight, topLeft + width - titleHeight );
3760 m_gal->DrawLine( topLeft + width - titleHeight, topLeft + width );
3761
3762 TEXT_ATTRIBUTES attrs;
3763 attrs.m_Italic = true;
3766 attrs.m_Size = VECTOR2I( textSize, textSize );
3767 attrs.m_StrokeWidth = GetPenSizeForNormal( textSize );
3768
3769 KIFONT::FONT::GetFont()->Draw( m_gal, aGroup->GetName(), topLeft + textOffset, attrs,
3770 aGroup->GetFontMetrics() );
3771 }
3772 }
3773}
3774
3775
3776void SCH_PAINTER::drawLine( const VECTOR2I& aStartPoint, const VECTOR2I& aEndPoint,
3777 LINE_STYLE aLineStyle, bool aDrawDirectLine, int aWidth )
3778{
3779 if( aDrawDirectLine )
3780 {
3781 m_gal->DrawLine( aStartPoint, aEndPoint );
3782 }
3783 else
3784 {
3785 SHAPE_SEGMENT segment( aStartPoint, aEndPoint );
3786
3787 STROKE_PARAMS::Stroke( &segment, aLineStyle, KiROUND( aWidth ), &m_schSettings,
3788 [&]( const VECTOR2I& start, const VECTOR2I& end )
3789 {
3790 if( start == end )
3791 m_gal->DrawLine( start + 1, end );
3792 else
3793 m_gal->DrawLine( start, end );
3794 } );
3795 }
3796}
3797
3798}; // namespace KIGFX
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:127
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
std::optional< BOX2I > OPT_BOX2I
Definition box2.h:926
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
constexpr const Vec & GetPosition() const
Definition box2.h:211
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:558
constexpr const Vec GetEnd() const
Definition box2.h:212
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:146
constexpr coord_type GetY() const
Definition box2.h:208
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr Vec Centre() const
Definition box2.h:97
constexpr coord_type GetX() const
Definition box2.h:207
constexpr size_type GetHeight() const
Definition box2.h:215
constexpr coord_type GetLeft() const
Definition box2.h:228
constexpr void SetX(coord_type val)
Definition box2.h:277
constexpr const Vec & GetOrigin() const
Definition box2.h:210
constexpr void SetY(coord_type val)
Definition box2.h:282
const BOX2< Vec > GetBoundingBoxRotated(const VECTOR2I &aRotCenter, const EDA_ANGLE &aAngle) const
Useful to calculate bounding box of rotated items, when rotation is not cardinal.
Definition box2.h:720
constexpr coord_type GetRight() const
Definition box2.h:217
constexpr coord_type GetTop() const
Definition box2.h:229
constexpr void Offset(coord_type dx, coord_type dy)
Definition box2.h:259
constexpr coord_type GetBottom() const
Definition box2.h:222
Represent basic circle geometry with utility geometry functions.
Definition circle.h:33
VECTOR2I Center
Public to make access simpler.
Definition circle.h:130
int Radius
Public to make access simpler.
Definition circle.h:129
bool IsHorizontal() const
Definition eda_angle.h:142
EDA_ANGLE Normalize180()
Definition eda_angle.h:268
wxString GetName() const
Definition eda_group.h:51
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:100
VECTOR2I GetRolloverPos() const
Definition eda_item.h:134
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:120
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:149
const KIID m_Uuid
Definition eda_item.h:528
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
bool IsEntered() const
Definition eda_item.h:130
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition eda_item.h:151
bool IsSelected() const
Definition eda_item.h:129
void SetSelected()
Definition eda_item.h:141
void SetBrightened()
Definition eda_item.h:142
EDA_ITEM * GetParent() const
Definition eda_item.h:114
bool IsRollover() const
Definition eda_item.h:133
bool HasFlag(EDA_ITEM_FLAGS aFlag) const
Definition eda_item.h:153
bool IsBrightened() const
Definition eda_item.h:131
bool IsForceVisible() const
Definition eda_item.h:218
EDA_ITEM_FLAGS GetFlags() const
Definition eda_item.h:152
bool IsMoving() const
Definition eda_item.h:127
bool IsNew() const
Definition eda_item.h:126
int GetEllipseMinorRadius() const
Definition eda_shape.h:306
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:288
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:334
FILL_T GetFillMode() const
Definition eda_shape.h:162
int GetEllipseMajorRadius() const
Definition eda_shape.h:297
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:315
SHAPE_T GetShape() const
Definition eda_shape.h:189
const std::vector< SEG > & GetHatchLines() const
bool IsSolidFill() const
Definition eda_shape.h:137
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:236
COLOR4D GetFillColor() const
Definition eda_shape.h:173
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:325
virtual void UpdateHatching() const
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:93
const VECTOR2I & GetTextPos() const
Definition eda_text.h:298
COLOR4D GetTextColor() const
Definition eda_text.h:295
void SetActiveUrl(const wxString &aUrl) const
Definition eda_text.h:303
bool IsItalic() const
Definition eda_text.h:194
const EDA_ANGLE & GetTextAngle() const
Definition eda_text.h:172
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:114
virtual bool IsVisible() const
Definition eda_text.h:212
std::vector< std::unique_ptr< KIFONT::GLYPH > > * GetRenderCache(const KIFONT::FONT *aFont, const wxString &forResolvedText, const VECTOR2I &aOffset={ 0, 0 }) const
Definition eda_text.cpp:702
virtual EDA_ANGLE GetDrawRotation() const
Definition eda_text.h:404
virtual VECTOR2I GetDrawPos() const
Definition eda_text.h:405
virtual bool HasHyperlink() const
Definition eda_text.h:427
wxString GetHyperlink() const
Definition eda_text.h:428
virtual KIFONT::FONT * GetDrawFont(const RENDER_SETTINGS *aSettings) const
Definition eda_text.cpp:666
bool HasTextVars() const
Indicates the ShownText has text var references which need to be processed.
Definition eda_text.h:133
const TEXT_ATTRIBUTES & GetAttributes() const
Definition eda_text.h:256
bool IsBold() const
Definition eda_text.h:209
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:269
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:95
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:98
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:147
virtual bool IsStroke() const
Definition font.h:105
void Draw(KIGFX::GAL *aGal, const wxString &aText, const VECTOR2I &aPosition, const VECTOR2I &aCursor, const TEXT_ATTRIBUTES &aAttributes, const METRICS &aFontMetrics, std::optional< VECTOR2I > aMousePos=std::nullopt, wxString *aActiveUrl=nullptr) const
Draw a string.
Definition font.cpp:250
virtual bool IsOutline() const
Definition font.h:106
VECTOR2I StringBoundaryLimits(const wxString &aText, const VECTOR2I &aSize, int aThickness, bool aBold, bool aItalic, const METRICS &aFontMetrics) const
Compute the boundary limits of aText (the bounding box of all shapes).
Definition font.cpp:451
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
COLOR4D WithAlpha(double aAlpha) const
Return a color with the same color, but the given alpha.
Definition color4d.h:312
COLOR4D & Invert()
Makes the color inverted, alpha remains the same.
Definition color4d.h:243
COLOR4D & Darken(double aFactor)
Makes the color darker by a given factor.
Definition color4d.h:227
std::shared_ptr< wxString > m_text
Definition color4d.h:399
double a
Alpha component.
Definition color4d.h:396
COLOR4D Brightened(double aFactor) const
Return a color that is brighter by a given factor, without modifying object.
Definition color4d.h:269
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:402
COLOR4D & Desaturate()
Removes color (in HSL model)
Definition color4d.cpp:532
COLOR4D Mix(const COLOR4D &aColor, double aFactor) const
Return a color that is mixed with the input by a factor.
Definition color4d.h:296
Attribute save/restore for GAL attributes.
Abstract interface for drawing on a 2D-surface.
virtual void DrawPolygon(const std::deque< VECTOR2D > &aPointList)
Draw a polygon.
virtual void SetIsFill(bool aIsFillEnabled)
Enable/disable fill.
virtual void Rotate(double aAngle)
Rotate the context.
virtual void DrawRectangle(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a rectangle.
void SetVerticalJustify(const GR_TEXT_V_ALIGN_T aVerticalJustify)
void SetHorizontalJustify(const GR_TEXT_H_ALIGN_T aHorizontalJustify)
virtual void SetFillColor(const COLOR4D &aColor)
Set the fill color.
virtual void Translate(const VECTOR2D &aTranslation)
Translate the context.
virtual void Restore()
Restore the context.
virtual void SetLineWidth(float aLineWidth)
Set the line width.
virtual void SetStrokeColor(const COLOR4D &aColor)
Set the stroke color.
virtual void SetIsStroke(bool aIsStrokeEnabled)
Enable/disable stroked outlines.
virtual void DrawLine(const VECTOR2D &aStartPoint, const VECTOR2D &aEndPoint)
Draw a line.
void SetGlyphSize(const VECTOR2I aSize)
virtual void DrawArc(const VECTOR2D &aCenterPoint, double aRadius, const EDA_ANGLE &aStartAngle, const EDA_ANGLE &aAngle)
Draw an arc.
virtual void BitmapText(const wxString &aText, const VECTOR2I &aPosition, const EDA_ANGLE &aAngle)
Draw a text using a bitmap font.
virtual void Save()
Save the context.
double GetWorldScale() const
Get the world scale.
GAL * m_gal
Instance of graphic abstraction layer that gives an interface to call commands used to draw (eg.
Definition painter.h:102
PAINTER(GAL *aGal)
Initialize this object for painting on any of the polymorphic GRAPHICS_ABSTRACTION_LAYER* derivatives...
Definition painter.cpp:33
virtual bool Draw(const VIEW_ITEM *, int) override
Takes an instance of VIEW_ITEM and passes it to a function that knows how to draw the item.
void drawPinDanglingIndicator(const SCH_PIN &aPin, const COLOR4D &aColor, bool aDrawingShadows, bool aBrightened)
float getTextThickness(const SCH_ITEM *aItem) const
void drawLocalPowerIcon(const VECTOR2D &aPos, double aSize, bool aRotate, const COLOR4D &aColor, bool aDrawingShadows, bool aBrightened)
Draw an local power pin indicator icon.
float getShadowWidth(bool aForHighlight) const
COLOR4D getRenderColor(const SCH_ITEM *aItem, int aLayer, bool aDrawingShadows, bool aDimmed=false, bool aIgnoreNets=false) const
int externalPinDecoSize(const SCH_PIN &aPin)
KIFONT::FONT * getFont(const EDA_TEXT *aText) const
void draw(const EDA_ITEM *, int, bool aDimmed)
wxString expandLibItemTextVars(const wxString &aSourceText, const SCH_SYMBOL *aSymbolContext)
static std::vector< KICAD_T > g_ScaledSelectionTypes
SCHEMATIC * m_schematic
void drawLine(const VECTOR2I &aStartPoint, const VECTOR2I &aEndPoint, LINE_STYLE aLineStyle, bool aDrawDirectLine=false, int aWidth=0)
int getOperatingPointTextSize() const
float getLineWidth(const SCH_ITEM *aItem, bool aDrawingShadows, bool aDrawingWireColorHighlights=false) const
void triLine(const VECTOR2D &a, const VECTOR2D &b, const VECTOR2D &c)
SCH_RENDER_SETTINGS m_schSettings
void drawAnchor(const VECTOR2I &aPos, bool aDrawingShadows)
Draw anchor indicating the anchor position of text objects, local labels, or fields.
bool nonCached(const EDA_ITEM *aItem)
Indicates the item is drawn on a non-cached layer in OpenGL.
void drawDanglingIndicator(const VECTOR2I &aPos, const COLOR4D &aColor, int aWidth, bool aDangling, bool aDrawingShadows, bool aBrightened)
Draw the target (an open square) for a wire or label which has no connection or is being moved.
void drawItemBoundingBox(const EDA_ITEM *aItem)
int internalPinDecoSize(const SCH_PIN &aPin)
bool isUnitAndConversionShown(const SCH_ITEM *aItem) const
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:86
double GetForcedTransparency() const
Definition view_item.h:171
wxString AsString() const
Definition kiid.cpp:244
Define a library symbol object.
Definition lib_symbol.h:83
std::vector< const SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
bool IsDerived() const
Definition lib_symbol.h:200
static LIB_SYMBOL * GetDummy()
Returns a dummy LIB_SYMBOL, used when one is missing in the schematic.
LIB_ITEMS_CONTAINER & GetDrawItems()
Return a reference to the draw item list.
Definition lib_symbol.h:713
std::unique_ptr< LIB_SYMBOL > Flatten() const
Return a flattened symbol inheritance to the caller.
void ShapeToPolygon(SHAPE_LINE_CHAIN &aPolygon, int aScale=-1) const
Return the shape polygon in internal units in a SHAPE_LINE_CHAIN the coordinates are relatives to the...
VECTOR2< T > GetScale() const
Get the scale components of the matrix.
Definition matrix3x3.h:295
A pin layout helper is a class that manages the layout of the parts of a pin on a schematic symbol:
OPT_BOX2I GetAltIconBBox()
Get the box of the alt mode icon, if there is one.
std::optional< TEXT_INFO > GetPinNameInfo(int aShadowWidth)
Get the text info for the pin name.
std::optional< TEXT_INFO > GetPinElectricalTypeInfo(int aShadowWidth)
CIRCLE GetDanglingIndicator() const
Gets the dangling indicator geometry for this pin, if the pin were to be dangling.
std::optional< TEXT_INFO > GetPinNumberInfo(int aShadowWidth)
void SetRenderParameters(int aNameThickness, int aNumberThickness, bool aShowElectricalType, bool aShowAltIcons)
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
VECTOR2I GetSize() const
const BITMAP_BASE & GetImage() const
Get the underlying image.
double GetImageScale() const
A round rectangle shape, based on a rectangle and a radius.
Definition roundrect.h:36
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aMaxError) const
Get the polygonal representation of the roundrect.
Definition roundrect.cpp:83
double GetHopOverScale()
Accessor that computes the current hop-over size.
SCHEMATIC_SETTINGS & Settings() const
SCH_SCREEN * GetCurrentScreen() const
Definition schematic.h:198
Object to handle a bitmap image that can be inserted in a schematic.
Definition sch_bitmap.h:40
VECTOR2I GetPosition() const override
REFERENCE_IMAGE & GetReferenceImage()
Definition sch_bitmap.h:54
Base class for a bus or wire entry.
bool IsStartDangling() const
VECTOR2I GetPosition() const override
bool IsEndDangling() const
virtual STROKE_PARAMS GetStroke() const override
int GetPenWidth() const override
VECTOR2I GetEnd() const
LINE_STYLE GetEffectiveLineStyle() const
Class for a wire to bus entry.
Each graphical item can have a SCH_CONNECTION describing its logical connection (to a bus or net).
wxString Name(bool aIgnoreSheet=false) const
bool IsBus() const
void CreateGraphicShape(const RENDER_SETTINGS *aSettings, std::vector< VECTOR2I > &aPoints, const VECTOR2I &aPos) const override
Calculate the graphic shape (a polygon) associated to the text.
virtual bool IsDangling() const override
Determines dangling state from connectivity and cached connected rule areas.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
VECTOR2I GetPosition() const override
FIELD_T GetId() const
Definition sch_field.h:130
wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0, const wxString &aVariantName=wxEmptyString) const
std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
std::vector< std::unique_ptr< KIFONT::GLYPH > > * GetRenderCache(const wxString &forResolvedText, const VECTOR2I &forPosition, TEXT_ATTRIBUTES &aAttrs) const
VECTOR2I GetParentPosition() const
void CreateGraphicShape(const RENDER_SETTINGS *aRenderSettings, std::vector< VECTOR2I > &aPoints, const VECTOR2I &aPos) const override
Calculate the graphic shape (a polygon) associated to the text.
VECTOR2I GetSchematicTextOffset(const RENDER_SETTINGS *aSettings) const override
This offset depends on the orientation, the type of text, and the area required to draw the associate...
A set of SCH_ITEMs (i.e., without duplicates).
Definition sch_group.h:52
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
void CreateGraphicShape(const RENDER_SETTINGS *aSettings, std::vector< VECTOR2I > &aPoints, const VECTOR2I &aPos) const override
Calculate the graphic shape (a polygon) associated to the text.
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition sch_item.h:168
const SYMBOL * GetParentSymbol() const
Definition sch_item.cpp:278
SCHEMATIC * Schematic() const
Search the item hierarchy to find a SCHEMATIC.
Definition sch_item.cpp:272
int GetBodyStyle() const
Definition sch_item.h:248
std::vector< int > ViewGetLayers() const override
Return the layers the item is drawn on (which may be more than its "home" layer)
Definition sch_item.cpp:475
int GetUnit() const
Definition sch_item.h:239
bool IsPrivate() const
Definition sch_item.h:254
SCH_LAYER_ID GetLayer() const
Return the layer this item is on.
Definition sch_item.h:344
bool RenderAsBitmap(double aWorldScale) const override
Definition sch_item.cpp:815
bool IsConnectivityDirty() const
Definition sch_item.h:591
SCH_CONNECTION * Connection(const SCH_SHEET_PATH *aSheet=nullptr) const
Retrieve the connection associated with this object in the given sheet.
Definition sch_item.cpp:491
wxString ResolveText(const wxString &aText, const SCH_SHEET_PATH *aPath, int aDepth=0) const
Definition sch_item.cpp:381
wxString GetClass() const override
Return the class name.
Definition sch_item.h:178
const KIFONT::METRICS & GetFontMetrics() const
Definition sch_item.cpp:783
int GetEffectivePenWidth(const SCH_RENDER_SETTINGS *aSettings) const
Definition sch_item.cpp:792
bool IsType(const std::vector< KICAD_T > &aScanTypes) const override
Check whether the item is one of the listed types.
Definition sch_item.h:183
int GetEffectiveDiameter() const
VECTOR2I GetPosition() const override
bool IsDangling() const override
Definition sch_label.h:339
COLOR4D GetLabelColor() const
LABEL_FLAG_SHAPE GetShape() const
Definition sch_label.h:182
std::vector< SCH_FIELD > & GetFields()
Definition sch_label.h:214
Segment description base class to describe items which have 2 end points (track, wire,...
Definition sch_line.h:42
void SetStartPoint(const VECTOR2I &aPosition)
Definition sch_line.h:140
std::vector< VECTOR3I > BuildWireWithHopShape(const SCH_SCREEN *aScreen, double aArcRadius) const
For wires only: build the list of points to draw the shape using segments and 180 deg arcs Points are...
bool IsWire() const
Return true if the line is a wire.
bool IsStartDangling() const
Definition sch_line.h:303
void SetLineColor(const COLOR4D &aColor)
Definition sch_line.cpp:323
void SetLineWidth(const int aSize)
Definition sch_line.cpp:389
LINE_STYLE GetEffectiveLineStyle() const
Definition sch_line.cpp:376
VECTOR2I GetMidPoint() const
Definition sch_line.h:146
VECTOR2I GetEndPoint() const
Definition sch_line.h:148
VECTOR2I GetStartPoint() const
Definition sch_line.h:139
bool IsEndDangling() const
Definition sch_line.h:304
bool IsBus() const
Return true if the line is a bus.
void SetLineStyle(const LINE_STYLE aStyle)
Definition sch_line.cpp:360
virtual void SetStroke(const STROKE_PARAMS &aStroke) override
Definition sch_line.h:202
void SetEndPoint(const VECTOR2I &aPosition)
Definition sch_line.h:149
const wxString & GetOperatingPoint() const
Definition sch_line.h:347
SCH_LAYER_ID GetColorLayer() const
VECTOR2I GetPosition() const override
Definition sch_marker.h:102
A net chain is a collection of nets that are connected together through passive components.
int GetSize() const
VECTOR2I GetPosition() const override
int GetNumberTextSize() const
Definition sch_pin.cpp:765
int GetLength() const
Definition sch_pin.cpp:379
const wxString & GetOperatingPoint() const
Definition sch_pin.h:342
void SetName(const wxString &aName)
Definition sch_pin.cpp:503
bool IsGlobalPower() const
Return whether this pin forms a global power connection: i.e., is part of a power symbol and of type ...
Definition sch_pin.cpp:439
bool IsVisible() const
Definition sch_pin.cpp:471
VECTOR2I GetPinRoot() const
Definition sch_pin.cpp:791
bool IsDangling() const override
Definition sch_pin.cpp:546
void SetShape(GRAPHIC_PINSHAPE aShape)
Definition sch_pin.h:99
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:336
int GetNameTextSize() const
Definition sch_pin.cpp:739
const wxString & GetShownName() const
Definition sch_pin.cpp:662
PIN_LAYOUT_CACHE & GetLayoutCache() const
Get the layout cache associated with this pin.
Definition sch_pin.cpp:1671
void SetOperatingPoint(const wxString &aText)
Definition sch_pin.h:343
void SetType(ELECTRICAL_PINTYPE aType)
Definition sch_pin.cpp:413
GRAPHIC_PINSHAPE GetShape() const
Definition sch_pin.cpp:358
ELECTRICAL_PINTYPE GetType() const
Definition sch_pin.cpp:393
std::vector< SHAPE * > MakeEffectiveShapes(bool aEdgeOnly=false) const override
Make a set of SHAPE objects representing the SCH_SHAPE.
Definition sch_shape.h:120
LINE_STYLE GetEffectiveLineStyle() const
Definition sch_shape.h:66
int GetHatchLineWidth() const override
Definition sch_shape.h:74
STROKE_PARAMS GetStroke() const override
Definition sch_shape.h:61
VECTOR2I GetPosition() const override
Definition sch_shape.h:88
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Define a sheet pin (label) used in sheets to create hierarchical schematics.
Sheet symbol placed in a schematic, and is the entry point for a sub schematic.
Definition sch_sheet.h:48
std::vector< SCH_FIELD > & GetFields()
Return a reference to the vector holding the sheet's fields.
Definition sch_sheet.h:91
bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
VECTOR2I GetSize() const
Definition sch_sheet.h:145
VECTOR2I GetPosition() const override
Definition sch_sheet.h:494
const BOX2I GetBodyBoundingBox() const
Return a bounding box for the sheet body but not the fields.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
KIGFX::COLOR4D GetBorderColor() const
Definition sch_sheet.h:151
bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flags.
std::vector< SCH_SHEET_PIN * > & GetPins()
Definition sch_sheet.h:231
KIGFX::COLOR4D GetBackgroundColor() const
Definition sch_sheet.h:154
Schematic symbol object.
Definition sch_symbol.h:76
bool GetExcludedFromSim(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
BOX2I GetBodyAndPinsBoundingBox() const override
Return a bounding box for the symbol body and pins but not the fields.
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
VECTOR2I GetPosition() const override
Definition sch_symbol.h:892
bool ResolveTextVar(const SCH_SHEET_PATH *aPath, wxString *token, int aDepth=0) const
Resolve any references to system tokens supported by the symbol.
static void BuildLocalPowerIconShape(std::vector< SCH_SHAPE > &aShapeList, const VECTOR2D &aPos, double aSize, double aLineWidth, bool aHorizontal)
Build the local power pin indicator icon shape, at coordinate aPos.
int GetUnitSelection(const SCH_SHEET_PATH *aSheet) const
Return the instance-specific unit selection for the given sheet path.
SCH_PIN * GetPin(const wxString &number) const
Find a symbol pin by number.
int GetOrientation() const override
Get the display symbol orientation.
bool IsSymbolLikePowerLocalLabel() const
std::unique_ptr< LIB_SYMBOL > & GetLibSymbolRef()
Definition sch_symbol.h:184
BOX2I GetBodyBoundingBox() const override
Return a bounding box for the symbol body but not the pins or fields.
virtual bool GetDNP(const SCH_SHEET_PATH *aInstance=nullptr, const wxString &aVariantName=wxEmptyString) const override
Set or clear the 'Do Not Populate' flag.
int GetColSpan() const
int GetRowSpan() const
std::vector< SCH_TABLECELL * > GetCells() const
Definition sch_table.h:157
void DrawBorders(const std::function< void(const VECTOR2I &aPt1, const VECTOR2I &aPt2, const STROKE_PARAMS &aStroke)> &aCallback) const
virtual wxString GetShownText(const RENDER_SETTINGS *aSettings, const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
VECTOR2I GetDrawPos() const override
VECTOR2I GetPosition() const override
Definition sch_text.h:150
VECTOR2I GetOffsetToMatchSCH_FIELD(SCH_RENDER_SETTINGS *aRenderSettings) const
Definition sch_text.cpp:504
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition sch_text.cpp:345
virtual wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
Definition sch_text.cpp:366
virtual VECTOR2I GetSchematicTextOffset(const RENDER_SETTINGS *aSettings) const
This offset depends on the orientation, the type of text, and the area required to draw the associate...
Definition sch_text.cpp:124
Definition seg.h:42
VECTOR2I B
Definition seg.h:50
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
const SEG & GetSeg() const
An abstract shape on 2D plane.
Definition shape.h:128
Simple container to manage line stroke parameters.
int GetWidth() const
LINE_STYLE GetLineStyle() const
KIGFX::COLOR4D GetColor() const
static void Stroke(const SHAPE *aShape, LINE_STYLE aLineStyle, int aWidth, const KIGFX::RENDER_SETTINGS *aRenderSettings, const std::function< void(const VECTOR2I &a, const VECTOR2I &b)> &aStroker)
const TRANSFORM & GetTransform() const
Definition symbol.h:247
KIGFX::COLOR4D m_Color
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
KIFONT::FONT * m_Font
@ LIGHTRED
Definition color4d.h:65
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, int aFlags)
Definition common.cpp:63
The common library.
#define DANGLING_SYMBOL_SIZE
The size of the rectangle indicating an unconnected wire or label.
#define DEFAULT_LINE_WIDTH_MILS
The default wire width in mils. (can be changed in preference menu)
#define UNSELECTED_END_SIZE
The size of the rectangle indicating the anchor of a text object (including fields)
#define TEXT_ANCHOR_SIZE
The default pin len value when creating pins(can be changed in preference menu)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:408
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:407
static constexpr EDA_ANGLE ANGLE_270
Definition eda_angle.h:416
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
#define IS_SHOWN_AS_BITMAP
#define ENDPOINT
ends. (Used to support dragging.)
#define IS_DANGLING
indicates a pin is dangling
#define STARTPOINT
When a line is selected, these flags indicate which.
@ ELLIPSE
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:51
@ ELLIPSE_ARC
Definition eda_shape.h:57
@ FILLED_WITH_COLOR
Definition eda_shape.h:67
@ NO_FILL
Definition eda_shape.h:64
@ REVERSE_HATCH
Definition eda_shape.h:69
@ HATCH
Definition eda_shape.h:68
@ FILLED_WITH_BG_BODYCOLOR
Definition eda_shape.h:66
@ FILLED_SHAPE
Fill with object color.
Definition eda_shape.h:65
@ CROSS_HATCH
Definition eda_shape.h:70
int GetPenSizeForDemiBold(int aTextSize)
Definition gr_text.cpp:43
int GetPenSizeForNormal(int aTextSize)
Definition gr_text.cpp:61
int ClampTextPenSize(int aPenSize, int aSize, bool aStrict)
Pen width should not allow characters to become cluttered up in their own fatness.
Definition gr_text.cpp:73
const wxChar *const traceSchPainter
Flag to enable debug output of schematic painter operations.
@ LAYER_DRAW_BITMAPS
Draw images.
Definition layer_ids.h:284
SCH_LAYER_ID
Eeschema drawing layers.
Definition layer_ids.h:451
@ LAYER_DANGLING
Definition layer_ids.h:479
@ LAYER_SHAPES_BACKGROUND
Definition layer_ids.h:485
@ LAYER_SHEETNAME
Definition layer_ids.h:474
@ LAYER_SCHEMATIC_ANCHOR
Definition layer_ids.h:502
@ LAYER_SHEETLABEL
Definition layer_ids.h:477
@ LAYER_PINNUM
Definition layer_ids.h:460
@ LAYER_RULE_AREAS
Definition layer_ids.h:467
@ LAYER_DEVICE
Definition layer_ids.h:468
@ LAYER_SHEET_BACKGROUND
Definition layer_ids.h:487
@ LAYER_EXCLUDED_FROM_SIM
Definition layer_ids.h:484
@ LAYER_BRIGHTENED
Definition layer_ids.h:493
@ LAYER_HIDDEN
Definition layer_ids.h:494
@ LAYER_HIERLABEL
Definition layer_ids.h:459
@ LAYER_PINNAM
Definition layer_ids.h:461
@ LAYER_PRIVATE_NOTES
Definition layer_ids.h:470
@ LAYER_HOVERED
Definition layer_ids.h:492
@ LAYER_GLOBLABEL
Definition layer_ids.h:458
@ LAYER_WIRE
Definition layer_ids.h:454
@ LAYER_NOTES
Definition layer_ids.h:469
@ LAYER_NET_COLOR_HIGHLIGHT
Definition layer_ids.h:495
@ LAYER_PIN
Definition layer_ids.h:472
@ LAYER_VALUEPART
Definition layer_ids.h:463
@ LAYER_BUS
Definition layer_ids.h:455
@ LAYER_FIELDS
Definition layer_ids.h:464
@ LAYER_DEVICE_BACKGROUND
Definition layer_ids.h:486
@ LAYER_LOCLABEL
Definition layer_ids.h:457
@ LAYER_SHEETFIELDS
Definition layer_ids.h:476
@ LAYER_REFERENCEPART
Definition layer_ids.h:462
@ LAYER_NETCLASS_REFS
Definition layer_ids.h:466
@ LAYER_NOTES_BACKGROUND
Definition layer_ids.h:471
@ LAYER_OP_CURRENTS
Definition layer_ids.h:504
@ LAYER_SHEET
Definition layer_ids.h:473
@ LAYER_SELECTION_SHADOWS
Definition layer_ids.h:497
@ LAYER_SCHEMATIC_BACKGROUND
Definition layer_ids.h:490
@ LAYER_INTERSHEET_REFS
Definition layer_ids.h:465
@ LAYER_OP_VOLTAGES
Definition layer_ids.h:503
@ LAYER_SHEETFILENAME
Definition layer_ids.h:475
@ LAYER_DNP_MARKER
Definition layer_ids.h:480
@ LAYER_NOCONNECT
Definition layer_ids.h:478
#define UNIMPLEMENTED_FOR(type)
Definition macros.h:96
MATRIX3x3< double > MATRIX3x3D
Definition matrix3x3.h:473
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:33
static void boxText(KIGFX::GAL &aGal, const wxString &aText, const VECTOR2D &aPosition, const TEXT_ATTRIBUTES &aAttrs, const KIFONT::METRICS &aFontMetrics)
static void bitmapText(KIGFX::GAL &aGal, const wxString &aText, const VECTOR2D &aPosition, const TEXT_ATTRIBUTES &aAttrs)
EESCHEMA_SETTINGS * eeconfig()
static void strokeText(KIGFX::GAL &aGal, const wxString &aText, const VECTOR2D &aPosition, const TEXT_ATTRIBUTES &aAttrs, const KIFONT::METRICS &aFontMetrics, std::optional< VECTOR2I > aMousePos=std::nullopt, wxString *aActiveUrl=nullptr)
static void knockoutText(KIGFX::GAL &aGal, const wxString &aText, const VECTOR2D &aPosition, const TEXT_ATTRIBUTES &aAttrs, const KIFONT::METRICS &aFontMetrics)
static void drawAltPinModesIcon(GAL &aGal, const VECTOR2D &aPos, double aSize, bool aBaseSelected, bool aRotate, int aExtraLineWidth, const COLOR4D &aColor)
Draw an alternate pin mode indicator icon.
static bool isFieldsLayer(int aLayer)
static BOX2I GetTextExtents(const wxString &aText, const VECTOR2D &aPosition, KIFONT::FONT &aFont, const TEXT_ATTRIBUTES &aAttrs, const KIFONT::METRICS &aFontMetrics)
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition kicad_algo.h:100
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
see class PGM_BASE
@ PT_NC
not connected (must be left open)
Definition pin_type.h:50
#define TARGET_BUSENTRY_RADIUS
Class to handle a set of SCH_ITEMs.
#define BITMAP_FONT_SIZE_THRESHOLD
Definition sch_item.cpp:47
@ F_DOT
Definition sch_label.h:109
@ F_ROUND
Definition sch_label.h:110
#define TARGET_PIN_RADIUS
Definition sch_pin.h:37
Utility functions for working with shapes.
wxString GetDefaultVariantName()
void wxStringSplit(const wxString &aText, wxArrayString &aStrings, wxChar aSplitter)
Split aString to a string list separated at aSplitter.
int PrintableCharCount(const wxString &aString)
Return the number of printable (ie: non-formatting) chars.
LINE_STYLE
Dashed line types.
void OrientAndMirrorSymbolItems(LIB_SYMBOL *aSymbol, int aOrientation)
Rotate and/or mirror graphic objects of LIB_SYMBOL aSymbol according to aOrientMirror.
@ VALUE
Field Value of part, i.e. "3.3K".
KIBIS top(path, &reporter)
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int radius
VECTOR2I end
int delta
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_H_ALIGN_INDETERMINATE
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_INDETERMINATE
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP
wxLogTrace helper definitions.
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition trigo.cpp:534
@ SCH_GROUP_T
Definition typeinfo.h:174
@ SCH_TABLE_T
Definition typeinfo.h:166
@ SCH_LINE_T
Definition typeinfo.h:164
@ LIB_SYMBOL_T
Definition typeinfo.h:149
@ SCH_NO_CONNECT_T
Definition typeinfo.h:161
@ SCH_SYMBOL_T
Definition typeinfo.h:173
@ SCH_TABLECELL_T
Definition typeinfo.h:167
@ SCH_FIELD_T
Definition typeinfo.h:151
@ SCH_DIRECTIVE_LABEL_T
Definition typeinfo.h:172
@ SCH_LABEL_T
Definition typeinfo.h:168
@ SCH_SHEET_T
Definition typeinfo.h:176
@ SCH_MARKER_T
Definition typeinfo.h:159
@ SCH_SHAPE_T
Definition typeinfo.h:150
@ SCH_RULE_AREA_T
Definition typeinfo.h:171
@ SCH_HIER_LABEL_T
Definition typeinfo.h:170
@ SCH_BUS_BUS_ENTRY_T
Definition typeinfo.h:163
@ SCH_SHEET_PIN_T
Definition typeinfo.h:175
@ SCH_TEXT_T
Definition typeinfo.h:152
@ SCH_BUS_WIRE_ENTRY_T
Definition typeinfo.h:162
@ SCH_BITMAP_T
Definition typeinfo.h:165
@ SCH_TEXTBOX_T
Definition typeinfo.h:153
@ SCH_GLOBAL_LABEL_T
Definition typeinfo.h:169
@ SCH_JUNCTION_T
Definition typeinfo.h:160
@ SCH_PIN_T
Definition typeinfo.h:154
constexpr int sign(T val)
Definition util.h:145
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686