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