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