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