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