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