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