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