KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_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) 2013-2019 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Tomasz Wlostowski <[email protected]>
8 * @author Maciej Suminski <[email protected]>
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
24#include <advanced_config.h>
25#include <board.h>
26#include <netinfo.h>
28#include <pcb_track.h>
29#include <pcb_group.h>
30#include <footprint.h>
31#include <pad.h>
32#include <pcb_shape.h>
33#include <string_utils.h>
34#include <zone.h>
35#include <pcb_reference_image.h>
36#include <pcb_text.h>
37#include <pcb_textbox.h>
38#include <pcb_table.h>
39#include <pcb_tablecell.h>
40#include <pcb_marker.h>
41#include <pcb_dimension.h>
42#include <pcb_point.h>
43#include <pcb_barcode.h>
44#include <pcb_target.h>
45#include <pcb_board_outline.h>
46
47#include <layer_ids.h>
48#include <lset.h>
49#include <pcb_painter.h>
50#include <pcb_display_options.h>
56#include <pcbnew_settings.h>
58
61#include <callback_gal.h>
64#include <geometry/shape_rect.h>
66#include <geometry/roundrect.h>
70#include <geometry/shape_arc.h>
72#include <stroke_params.h>
73#include <bezier_curves.h>
74#include <kiface_base.h>
75#include <gr_text.h>
76#include <pgm_base.h>
77
78using namespace KIGFX;
79
80
82{
83 return dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
84}
85
86// Helpers for display options existing in Cvpcb and Pcbnew
87// Note, when running Cvpcb, pcbconfig() returns nullptr and viewer_settings()
88// returns the viewer options existing to Cvpcb and Pcbnew
110
111
113{
114 m_backgroundColor = COLOR4D( 0.0, 0.0, 0.0, 1.0 );
118
119 m_trackOpacity = 1.0;
120 m_viaOpacity = 1.0;
121 m_padOpacity = 1.0;
122 m_zoneOpacity = 1.0;
123 m_imageOpacity = 1.0;
125
127
128 m_PadEditModePad = nullptr;
129
130 SetDashLengthRatio( 12 ); // From ISO 128-2
131 SetGapLengthRatio( 3 ); // From ISO 128-2
132
134
135 update();
136}
137
138
140{
142
143 // Init board layers colors:
144 for( int i = 0; i < PCB_LAYER_ID_COUNT; i++ )
145 {
146 m_layerColors[i] = aSettings->GetColor( i );
147
148 // Guard: if the alpha channel is too small, the layer is not visible.
149 if( m_layerColors[i].a < 0.2 )
150 m_layerColors[i].a = 0.2;
151 }
152
153 // Init specific graphic layers colors:
154 for( int i = GAL_LAYER_ID_START; i < GAL_LAYER_ID_END; i++ )
155 m_layerColors[i] = aSettings->GetColor( i );
156
157 // Colors for layers that aren't theme-able
160
161 // Netnames for copper layers
162 const COLOR4D lightLabel = aSettings->GetColor( NETNAMES_LAYER_ID_START );
163 const COLOR4D darkLabel = lightLabel.Inverted();
164
165 for( PCB_LAYER_ID layer : LSET::AllCuMask().CuStack() )
166 {
167 if( m_layerColors[layer].GetBrightness() > 0.5 )
168 m_layerColors[GetNetnameLayer( layer )] = darkLabel;
169 else
170 m_layerColors[GetNetnameLayer( layer )] = lightLabel;
171 }
172
173 if( PgmOrNull() ) // can be null if used without project (i.e. from python script)
175 else
176 m_hiContrastFactor = 1.0f - 0.8f; // default value
177
178 update();
179}
180
181
196
197
198COLOR4D PCB_RENDER_SETTINGS::GetColor( const VIEW_ITEM* aItem, int aLayer ) const
199{
200 return GetColor( dynamic_cast<const BOARD_ITEM*>( aItem ), aLayer );
201}
202
203
204COLOR4D PCB_RENDER_SETTINGS::GetColor( const BOARD_ITEM* aItem, int aLayer ) const
205{
206 int netCode = -1;
207 int originalLayer = aLayer;
208
209 if( aLayer == LAYER_MARKER_SHADOWS )
210 return m_backgroundColor.WithAlpha( 0.6 );
211
213 return m_layerColors.at( aLayer );
214
215 if( aLayer == LAYER_CONSTRAINT_SHADOW )
216 return m_layerColors.at( aLayer );
217
218 // SMD pads use the copper netname layer
219 if( aLayer == LAYER_PAD_FR_NETNAMES )
220 aLayer = GetNetnameLayer( F_Cu );
221 else if( aLayer == LAYER_PAD_BK_NETNAMES )
222 aLayer = GetNetnameLayer( B_Cu );
223
224 if( IsHoleLayer( aLayer ) && m_isPrinting )
225 {
226 // Careful that we don't end up with the same colour for the annular ring and the hole
227 // when printing in B&W.
228 const PAD* pad = dynamic_cast<const PAD*>( aItem );
229 const PCB_VIA* via = dynamic_cast<const PCB_VIA*>( aItem );
230 int holeLayer = aLayer;
231 int annularRingLayer = UNDEFINED_LAYER;
232
233 if( pad && pad->GetAttribute() == PAD_ATTRIB::PTH )
234 {
235 LSET copperLayers = pad->GetLayerSet() & LSET::AllCuMask();
236
237 if( !copperLayers.empty() )
238 annularRingLayer = copperLayers.Seq().front();
239 }
240 else if( via )
241 {
242 annularRingLayer = F_Cu;
243 }
244
245 if( annularRingLayer != UNDEFINED_LAYER )
246 {
247 auto it = m_layerColors.find( holeLayer );
248 auto it2 = m_layerColors.find( annularRingLayer );
249
250 if( it != m_layerColors.end() && it2 != m_layerColors.end() && it->second == it2->second )
251 aLayer = LAYER_PCB_BACKGROUND;
252 }
253 }
254
255 // Zones should pull from the copper layer
256 if( aItem && aItem->Type() == PCB_ZONE_T )
257 {
258 if( IsZoneFillLayer( aLayer ) )
259 aLayer = aLayer - LAYER_ZONE_START;
260 }
261
262 // Points use the LAYER_POINTS color for their virtual per-layer layers
263 if( IsPointsLayer( aLayer ) )
264 aLayer = LAYER_POINTS;
265
266 // Pad and via copper and clearance outlines take their color from the copper layer
267 if( IsPadCopperLayer( aLayer ) )
268 {
269 if( pcbconfig() && aItem && aItem->Type() == PCB_PAD_T )
270 {
271 const PAD* pad = static_cast<const PAD*>( aItem );
272
273 // Old-skool display for people who struggle with change
274 if( pcbconfig()->m_Display.m_UseViaColorForNormalTHPadstacks
275 && pad->GetAttribute() == PAD_ATTRIB::PTH
276 && pad->Padstack().Mode() == PADSTACK::MODE::NORMAL )
277 {
278 aLayer = LAYER_VIA_HOLES;
279 }
280 else
281 {
282 aLayer = aLayer - LAYER_PAD_COPPER_START;
283 }
284 }
285 else
286 {
287 aLayer = aLayer - LAYER_PAD_COPPER_START;
288 }
289 }
290 else if( IsViaCopperLayer( aLayer ) )
291 aLayer = aLayer - LAYER_VIA_COPPER_START;
292 else if( IsClearanceLayer( aLayer ) )
293 aLayer = aLayer - LAYER_CLEARANCE_START;
294
295 // Use via "golden copper" hole color for pad hole walls for contrast
296 else if( aLayer == LAYER_PAD_HOLEWALLS )
297 aLayer = LAYER_VIA_HOLES;
298
299 // Show via mask layers if appropriate
300 if( aLayer == LAYER_VIA_THROUGH && !m_isPrinting )
301 {
302 if( aItem && aItem->GetBoard() )
303 {
304 LSET visibleLayers = aItem->GetBoard()->GetVisibleLayers()
305 & aItem->GetBoard()->GetEnabledLayers()
306 & aItem->GetLayerSet();
307
308 if( GetActiveLayer() == F_Mask && visibleLayers.test( F_Mask ) )
309 {
310 aLayer = F_Mask;
311 }
312 else if( GetActiveLayer() == B_Mask && visibleLayers.test( B_Mask ) )
313 {
314 aLayer = B_Mask;
315 }
316 else if( ( visibleLayers & LSET::AllCuMask() ).none() )
317 {
318 if( visibleLayers.any() )
319 aLayer = visibleLayers.Seq().back();
320 }
321 }
322 }
323
324 // Normal path: get the layer base color
325 auto it = m_layerColors.find( aLayer );
326 COLOR4D color = it == m_layerColors.end() ? COLOR4D::WHITE : it->second;
327
328 if( !aItem )
329 return color;
330
331 // Selection disambiguation
332 if( aItem->IsBrightened() || ( aItem->Type() == PCB_MARKER_T && aItem->IsSelected() ) )
333 {
334 if( aItem->Type() == PCB_MARKER_T )
335 {
336 auto itemLayerIter = m_layerColors.find( LAYER_DRC_HIGHLIGHTED );
337
338 if( itemLayerIter != m_layerColors.end() )
339 return itemLayerIter->second;
340 }
341
342 return color.Brightened( m_selectFactor ).WithAlpha( 0.8 );
343 }
344
345 // Normal selection
346 if( aItem->IsSelected() )
347 {
348 // Selection for tables is done with a background wash, so pass in nullptr to GetColor()
349 // so we just get the "normal" (un-selected/un-brightened) color for the borders.
350 if( aItem->Type() != PCB_TABLE_T && aItem->Type() != PCB_TABLECELL_T )
351 {
352 auto it_selected = m_layerColorsSel.find( aLayer );
353 color = it_selected == m_layerColorsSel.end() ? color.Brightened( 0.8 ) : it_selected->second;
354 }
355 }
356
357 // Some graphic objects are BOARD_CONNECTED_ITEM, but they are seen here as
358 // actually board connected objects only if on a copper layer
359 const BOARD_CONNECTED_ITEM* conItem = nullptr;
360
361 if( aItem->IsConnected() && aItem->IsOnCopperLayer() )
362 conItem = static_cast<const BOARD_CONNECTED_ITEM*>( aItem );
363
364 // Try to obtain the netcode for the aItem
365 if( conItem )
366 netCode = conItem->GetNetCode();
367
368 bool highlighted = m_highlightEnabled && m_highlightNetcodes.count( netCode );
369 bool selected = aItem->IsSelected();
370
371 // Apply net color overrides
372 if( conItem && m_netColorMode == NET_COLOR_MODE::ALL && IsCopperLayer( aLayer ) )
373 {
374 COLOR4D netColor = COLOR4D::UNSPECIFIED;
375
376 auto ii = m_netColors.find( netCode );
377
378 if( ii != m_netColors.end() )
379 netColor = ii->second;
380
381 if( netColor == COLOR4D::UNSPECIFIED )
382 {
383 const NETCLASS* nc = conItem->GetEffectiveNetClass();
384
385 if( nc->HasPcbColor() )
386 netColor = nc->GetPcbColor();
387 }
388
389 if( netColor == COLOR4D::UNSPECIFIED )
390 netColor = color;
391
392 if( selected )
393 {
394 // Selection brightening overrides highlighting
395 netColor.Brighten( m_selectFactor );
396 }
397 else if( m_highlightEnabled )
398 {
399 // Highlight brightens objects on all layers and darkens everything else for contrast
400 if( highlighted )
401 netColor.Brighten( m_highlightFactor );
402 else
403 netColor.Darken( 1.0 - m_highlightFactor );
404 }
405
406 color = netColor;
407 }
408 else if( !selected && m_highlightEnabled )
409 {
410 // Single net highlight mode
411 if( m_highlightNetcodes.contains( netCode ) )
412 {
413 auto it_hi = m_layerColorsHi.find( aLayer );
414 color = it_hi == m_layerColorsHi.end() ? color.Brightened( m_highlightFactor ) : it_hi->second;
415 }
416 else
417 {
418 auto it_dark = m_layerColorsDark.find( aLayer );
419 color = it_dark == m_layerColorsDark.end() ? color.Darkened( 1.0 - m_highlightFactor ) : it_dark->second;
420 }
421 }
422
423 // Apply high-contrast dimming
424 if( m_hiContrastEnabled && m_highContrastLayers.size() && !highlighted && !selected )
425 {
427 bool isActive = m_highContrastLayers.count( aLayer );
428 bool hide = false;
429
430 switch( originalLayer )
431 {
432 case LAYER_PADS:
433 {
434 const PAD* pad = static_cast<const PAD*>( aItem );
435
436 if( pad->IsOnLayer( primary ) && !pad->FlashLayer( primary ) )
437 {
438 isActive = false;
439
440 if( IsCopperLayer( primary ) )
441 hide = true;
442 }
443
445 isActive = false;
446
447 break;
448 }
449
450 case LAYER_VIA_BLIND:
451 case LAYER_VIA_BURIED:
453 {
454 const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem );
455
456 // Target graphic is active if the via crosses the primary layer
457 if( via->GetLayerSet().test( primary ) == 0 )
458 {
459 isActive = false;
460 hide = true;
461 }
462
463 break;
464 }
465
467 {
468 const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem );
469
470 if( !via->FlashLayer( primary ) )
471 {
472 isActive = false;
473
474 if( IsCopperLayer( primary ) )
475 hide = true;
476 }
477
478 break;
479 }
480
484 // Pad holes are active is any physical layer is active
485 if( LSET::PhysicalLayersMask().test( primary ) == 0 )
486 isActive = false;
487
488 break;
489
490 case LAYER_VIA_HOLES:
492 {
493 const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem );
494
495 if( via->GetViaType() == VIATYPE::THROUGH )
496 {
497 // A through via's hole is active if any physical layer is active
498 if( LSET::PhysicalLayersMask().test( primary ) == 0 )
499 isActive = false;
500 }
501 else
502 {
503 // A blind/buried or micro via's hole is active if it crosses the primary layer
504 if( via->GetLayerSet().test( primary ) == 0 )
505 isActive = false;
506 }
507
508 break;
509 }
510
511 case LAYER_DRC_ERROR:
514 case LAYER_DRC_SHAPES:
515 isActive = true;
516 break;
517
518 default:
519 break;
520 }
521
522 if( !isActive )
523 {
524 // Graphics on Edge_Cuts layer are not fully dimmed or hidden because they are
525 // useful when working on another layer
526 // We could use a dim factor = m_hiContrastFactor, but to have a sufficient
527 // contrast whenever m_hiContrastFactor value, we clamp the factor to 0.3f
528 // (arbitray choice after tests)
529 float dim_factor_Edge_Cuts = std::max( m_hiContrastFactor, 0.3f );
530
532 || IsNetnameLayer( aLayer )
533 || hide )
534 {
535 if( originalLayer == Edge_Cuts )
536 {
538
539 if( it != m_layerColors.end() )
540 color = color.Mix( it->second, dim_factor_Edge_Cuts );
541 else
542 color = color.Mix( COLOR4D::BLACK, dim_factor_Edge_Cuts );
543 }
544 else
545 color = COLOR4D::CLEAR;
546 }
547 else
548 {
550 COLOR4D backgroundColor = it == m_layerColors.end() ? COLOR4D::BLACK : it->second;
551
552 if( originalLayer == Edge_Cuts )
553 color = color.Mix( backgroundColor, dim_factor_Edge_Cuts );
554 else
555 color = color.Mix( backgroundColor, m_hiContrastFactor );
556
557 // Reference images can't have their color mixed so just reduce the opacity a bit
558 // so they show through less
559 if( aItem->Type() == PCB_REFERENCE_IMAGE_T )
560 color.a *= m_hiContrastFactor;
561 }
562 }
563 }
564 else if( originalLayer == LAYER_VIA_BLIND
565 || originalLayer == LAYER_VIA_BURIED
566 || originalLayer == LAYER_VIA_MICROVIA )
567 {
568 const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem );
569 const BOARD* board = via->GetBoard();
570 LSET visibleLayers = board->GetVisibleLayers() & board->GetEnabledLayers();
571
572 // Target graphic is visible if the via crosses a visible layer
573 if( ( via->GetLayerSet() & visibleLayers ).none() )
574 color = COLOR4D::CLEAR;
575 }
576
577 // Apply per-type opacity overrides
578 if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_ARC_T )
579 color.a *= m_trackOpacity;
580 else if( aItem->Type() == PCB_VIA_T )
581 color.a *= m_viaOpacity;
582 else if( aItem->Type() == PCB_PAD_T )
583 color.a *= m_padOpacity;
584 else if( aItem->Type() == PCB_ZONE_T && static_cast<const ZONE*>( aItem )->IsTeardropArea() )
585 color.a *= m_trackOpacity;
586 else if( aItem->Type() == PCB_ZONE_T )
587 color.a *= m_zoneOpacity;
588 else if( aItem->Type() == PCB_REFERENCE_IMAGE_T )
589 color.a *= m_imageOpacity;
590 else if( aItem->Type() == PCB_SHAPE_T && static_cast<const PCB_SHAPE*>( aItem )->IsAnyFill() )
591 color.a *= m_filledShapeOpacity;
592 else if( aItem->Type() == PCB_SHAPE_T && aItem->IsOnCopperLayer() )
593 color.a *= m_trackOpacity;
594
595 if( aItem->GetForcedTransparency() > 0.0 )
596 color = color.WithAlpha( color.a * ( 1.0 - aItem->GetForcedTransparency() ) );
597
598 // No special modifiers enabled
599 return color;
600}
601
602
607
608
610 PAINTER( aGal ),
611 m_frameType( aFrameType ),
615{
616}
617
618
619int PCB_PAINTER::getLineThickness( int aActualThickness ) const
620{
621 // if items have 0 thickness, draw them with the outline
622 // width, otherwise respect the set value (which, no matter
623 // how small will produce something)
624 if( aActualThickness == 0 )
625 return m_pcbSettings.m_outlineWidth;
626
627 return aActualThickness;
628}
629
630
632{
633 return aPad->GetDrillShape();
634}
635
636
638{
639 SHAPE_SEGMENT segm = *aPad->GetEffectiveHoleShape().get();
640 return segm;
641}
642
643
644int PCB_PAINTER::getViaDrillSize( const PCB_VIA* aVia ) const
645{
646 return aVia->GetDrillValue();
647}
648
649
650bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
651{
652 if( !aItem->IsBOARD_ITEM() )
653 return false;
654
655 const BOARD_ITEM* item = static_cast<const BOARD_ITEM*>( aItem );
656
657 if( const BOARD* board = item->GetBoard() )
658 {
663
664 if( item->GetParentFootprint() && !board->IsFootprintHolder() )
665 {
666 FOOTPRINT* parentFP = item->GetParentFootprint();
667
668 // Never draw footprint reference images on board
669 if( item->Type() == PCB_REFERENCE_IMAGE_T )
670 {
671 return false;
672 }
673 else if( item->GetLayerSet().count() > 1 )
674 {
675 // For multi-layer objects, exclude only those layers that are private
676 if( IsPcbLayer( aLayer ) && parentFP->GetPrivateLayers().test( aLayer ) )
677 return false;
678 }
679 else if( item->GetLayerSet().count() == 1 )
680 {
681 // For single-layer objects, exclude all layers including ancillary layers
682 // such as holes, netnames, etc.
683 PCB_LAYER_ID singleLayer = item->GetLayerSet().ExtractLayer();
684
685 if( parentFP->GetPrivateLayers().test( singleLayer ) )
686 return false;
687 }
688 }
689 }
690 else
691 {
694 }
695
696 // the "cast" applied in here clarifies which overloaded draw() is called
697 switch( item->Type() )
698 {
699 case PCB_TRACE_T:
700 draw( static_cast<const PCB_TRACK*>( item ), aLayer );
701 break;
702
703 case PCB_ARC_T:
704 draw( static_cast<const PCB_ARC*>( item ), aLayer );
705 break;
706
707 case PCB_VIA_T:
708 draw( static_cast<const PCB_VIA*>( item ), aLayer );
709 break;
710
711 case PCB_PAD_T:
712 draw( static_cast<const PAD*>( item ), aLayer );
713 break;
714
715 case PCB_SHAPE_T:
716 draw( static_cast<const PCB_SHAPE*>( item ), aLayer );
717 break;
718
720 draw( static_cast<const PCB_REFERENCE_IMAGE*>( item ), aLayer );
721 break;
722
723 case PCB_FIELD_T:
724 draw( static_cast<const PCB_FIELD*>( item ), aLayer );
725 break;
726
727 case PCB_TEXT_T:
728 draw( static_cast<const PCB_TEXT*>( item ), aLayer );
729 break;
730
731 case PCB_TEXTBOX_T:
732 draw( static_cast<const PCB_TEXTBOX*>( item ), aLayer );
733 break;
734
735 case PCB_TABLE_T:
736 draw( static_cast<const PCB_TABLE*>( item ), aLayer );
737 break;
738
739 case PCB_FOOTPRINT_T:
740 draw( static_cast<const FOOTPRINT*>( item ), aLayer );
741 break;
742
743 case PCB_GROUP_T:
744 draw( static_cast<const PCB_GROUP*>( item ), aLayer );
745 break;
746
747 case PCB_ZONE_T:
748 draw( static_cast<const ZONE*>( item ), aLayer );
749 break;
750
752 case PCB_DIM_CENTER_T:
753 case PCB_DIM_RADIAL_T:
755 case PCB_DIM_LEADER_T:
756 draw( static_cast<const PCB_DIMENSION_BASE*>( item ), aLayer );
757 break;
758
759 case PCB_BARCODE_T:
760 draw( static_cast<const PCB_BARCODE*>( item ), aLayer );
761 break;
762
763 case PCB_TARGET_T:
764 draw( static_cast<const PCB_TARGET*>( item ) );
765 break;
766
767 case PCB_POINT_T:
768 draw( static_cast<const PCB_POINT*>( item ), aLayer );
769 break;
770
771 case PCB_MARKER_T:
772 draw( static_cast<const PCB_MARKER*>( item ), aLayer );
773 break;
774
776 draw( static_cast<const PCB_BOARD_OUTLINE*>( item ), aLayer );
777 break;
778
779 default:
780 // Painter does not know how to draw the object
781 return false;
782 }
783
784 // Draw bounding boxes after drawing objects so they can be seen.
785 if( m_pcbSettings.GetDrawBoundingBoxes() )
786 {
787 // Show bounding boxes of painted objects for debugging.
788 BOX2I box = item->GetBoundingBox();
789
790 m_gal->SetIsFill( false );
791 m_gal->SetIsStroke( true );
792
793 if( item->Type() == PCB_FOOTPRINT_T )
794 {
795 m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) :
796 COLOR4D( MAGENTA ) );
797 }
798 else
799 {
800 m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) :
801 COLOR4D( 0.4, 0.4, 0.4, 1 ) );
802 }
803
804 m_gal->SetLineWidth( 1 );
805 m_gal->DrawRectangle( box.GetOrigin(), box.GetEnd() );
806
807 if( item->Type() == PCB_FOOTPRINT_T )
808 {
809 m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) :
810 COLOR4D( CYAN ) );
811
812 const FOOTPRINT* fp = static_cast<const FOOTPRINT*>( item );
813
814 if( fp )
815 {
816 const SHAPE_POLY_SET& convex = fp->GetBoundingHull();
817
818 m_gal->DrawPolyline( convex.COutline( 0 ) );
819 }
820 }
821 }
822
823 return true;
824}
825
826
827void PCB_PAINTER::draw( const PCB_TRACK* aTrack, int aLayer )
828{
829 VECTOR2I start( aTrack->GetStart() );
830 VECTOR2I end( aTrack->GetEnd() );
831 int track_width = aTrack->GetWidth();
832 COLOR4D color = m_pcbSettings.GetColor( aTrack, aLayer );
833
834 // If a chain highlight is active and the track belongs to the highlighted
835 // chain, and the chain has a colour override configured on the board,
836 // prefer that colour. Only do this when we're drawing the actual copper
837 // (not netname labels, clearance outlines, etc.).
838 if( IsCopperLayer( aLayer ) && !m_pcbSettings.m_highlightedNetChain.IsEmpty() )
839 {
840 if( NETINFO_ITEM* netinfo = aTrack->GetNet() )
841 {
842 if( netinfo->GetNetChain() == m_pcbSettings.m_highlightedNetChain )
843 {
844 if( const BOARD* board = aTrack->GetBoard() )
845 {
846 COLOR4D chainColor =
847 board->GetNetChainColor( m_pcbSettings.m_highlightedNetChain );
848
849 if( chainColor != COLOR4D::UNSPECIFIED )
850 color = chainColor.WithAlpha( color.a );
851 }
852 }
853 }
854 }
855
856 if( IsNetnameLayer( aLayer ) )
857 {
858 if( !pcbconfig() || pcbconfig()->m_Display.m_NetNames < 2 )
859 return;
860
861 if( aTrack->GetNetCode() <= NETINFO_LIST::UNCONNECTED )
862 return;
863
864 SHAPE_SEGMENT trackShape( { aTrack->GetStart(), aTrack->GetEnd() }, aTrack->GetWidth() );
865 renderNetNameForSegment( trackShape, color, aTrack->GetDisplayNetname() );
866 return;
867 }
868 else if( IsCopperLayer( aLayer ) || IsSolderMaskLayer( aLayer )
869 || aLayer == LAYER_LOCKED_ITEM_SHADOW )
870 {
871 // Draw a regular track
872 bool outline_mode = pcbconfig()
874 && aLayer != LAYER_LOCKED_ITEM_SHADOW;
875 m_gal->SetStrokeColor( color );
876 m_gal->SetFillColor( color );
877 m_gal->SetIsStroke( outline_mode );
878 m_gal->SetIsFill( not outline_mode );
879 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
880
881 if( IsSolderMaskLayer( aLayer ) )
882 track_width = track_width + aTrack->GetSolderMaskExpansion() * 2;
883
884 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
885 track_width = track_width + m_lockedShadowMargin;
886
887 m_gal->DrawSegment( start, end, track_width );
888 }
889
890 // Clearance lines
891 if( IsClearanceLayer( aLayer ) && pcbconfig()
892 && pcbconfig()->m_Display.m_TrackClearance == SHOW_WITH_VIA_ALWAYS
893 && !m_pcbSettings.m_isPrinting )
894 {
895 const PCB_LAYER_ID copperLayerForClearance = ToLAYER_ID( aLayer - LAYER_CLEARANCE_START );
896
897 int clearance = aTrack->GetOwnClearance( copperLayerForClearance );
898
899 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
900 m_gal->SetIsFill( false );
901 m_gal->SetIsStroke( true );
902 m_gal->SetStrokeColor( color );
903 m_gal->DrawSegment( start, end, track_width + clearance * 2 );
904 }
905}
906
907
909 const wxString& aNetName ) const
910{
911 // When drawing netnames, clip the track to the viewport
912 BOX2D viewport;
913 VECTOR2D screenSize = m_gal->GetScreenPixelSize();
914 const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix();
915
916 viewport.SetOrigin( VECTOR2D( matrix * VECTOR2D( 0, 0 ) ) );
917 viewport.SetEnd( VECTOR2D( matrix * screenSize ) );
918 viewport.Normalize();
919
920 int num_char = aNetName.size();
921
922 // Check if the track is long enough to have a netname displayed
923 int seg_minlength = aSeg.GetWidth() * num_char;
924 SEG::ecoord seg_minlength_sq = (SEG::ecoord)seg_minlength * seg_minlength;
925
926 if( aSeg.GetSeg().SquaredLength() < seg_minlength_sq )
927 return;
928
929 double textSize = aSeg.GetWidth();
930 double penWidth = textSize / 12.0;
931 EDA_ANGLE textOrientation;
932 int num_names = 1;
933
934 VECTOR2I start = aSeg.GetSeg().A;
935 VECTOR2I end = aSeg.GetSeg().B;
936 VECTOR2D segV = end - start;
937
938 if( end.y == start.y ) // horizontal
939 {
940 textOrientation = ANGLE_HORIZONTAL;
941 num_names = std::max( num_names, KiROUND( aSeg.GetSeg().Length() / viewport.GetWidth() ) );
942 }
943 else if( end.x == start.x ) // vertical
944 {
945 textOrientation = ANGLE_VERTICAL;
946 num_names = std::max( num_names, KiROUND( aSeg.GetSeg().Length() / viewport.GetHeight() ) );
947 }
948 else
949 {
950 textOrientation = -EDA_ANGLE( segV );
951 textOrientation.Normalize90();
952
953 double min_size = std::min( viewport.GetWidth(), viewport.GetHeight() );
954 num_names = std::max( num_names, KiROUND( aSeg.GetSeg().Length() / ( M_SQRT2 * min_size ) ) );
955 }
956
957 m_gal->SetIsStroke( true );
958 m_gal->SetIsFill( false );
959 m_gal->SetStrokeColor( aColor );
960 m_gal->SetLineWidth( penWidth );
961 m_gal->SetFontBold( false );
962 m_gal->SetFontItalic( false );
963 m_gal->SetFontUnderlined( false );
964 m_gal->SetTextMirrored( false );
965 m_gal->SetGlyphSize( VECTOR2D( textSize * 0.55, textSize * 0.55 ) );
966 m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER );
967 m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER );
968
969 int divisions = num_names + 1;
970
971 for( int ii = 1; ii < divisions; ++ii )
972 {
973 VECTOR2I textPosition = start + segV * ( (double) ii / divisions );
974
975 if( viewport.Contains( textPosition ) )
976 m_gal->BitmapText( aNetName, textPosition, textOrientation );
977 }
978}
979
980
981void PCB_PAINTER::draw( const PCB_ARC* aArc, int aLayer )
982{
983 VECTOR2D center( aArc->GetCenter() );
984 int width = aArc->GetWidth();
985 COLOR4D color = m_pcbSettings.GetColor( aArc, aLayer );
986 double radius = aArc->GetRadius();
987 EDA_ANGLE start_angle = aArc->GetArcAngleStart();
988 EDA_ANGLE angle = aArc->GetAngle();
989
990 if( IsNetnameLayer( aLayer ) )
991 {
992 if( !pcbconfig() || pcbconfig()->m_Display.m_NetNames < 2 )
993 return;
994
996 return;
997
998 wxString netname = aArc->GetDisplayNetname();
999
1000 if( netname.IsEmpty() )
1001 return;
1002
1003 // Arc length must accommodate the label width.
1004 double arcLen = std::abs( radius * angle.AsRadians() );
1005
1006 if( arcLen < (double) width * (double) netname.size() )
1007 return;
1008
1009 // Tangent at the arc midpoint is perpendicular to the radius there.
1010 VECTOR2I midPt = aArc->GetMid();
1011 VECTOR2D radial = midPt - aArc->GetCenter();
1012 EDA_ANGLE textOrientation( VECTOR2D( -radial.y, radial.x ) );
1013 textOrientation = -textOrientation;
1014 textOrientation.Normalize90();
1015
1016 double textSize = width;
1017 double penWidth = textSize / 12.0;
1018
1019 m_gal->SetIsStroke( true );
1020 m_gal->SetIsFill( false );
1021 m_gal->SetStrokeColor( color );
1022 m_gal->SetLineWidth( penWidth );
1023 m_gal->SetFontBold( false );
1024 m_gal->SetFontItalic( false );
1025 m_gal->SetFontUnderlined( false );
1026 m_gal->SetTextMirrored( false );
1027 m_gal->SetGlyphSize( VECTOR2D( textSize * 0.55, textSize * 0.55 ) );
1028 m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER );
1029 m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER );
1030
1031 m_gal->BitmapText( netname, midPt, textOrientation );
1032 return;
1033 }
1034 else if( IsCopperLayer( aLayer ) || IsSolderMaskLayer( aLayer ) || aLayer == LAYER_LOCKED_ITEM_SHADOW )
1035 {
1036 // Draw a regular track
1037 bool outline_mode = pcbconfig()
1039 && aLayer != LAYER_LOCKED_ITEM_SHADOW;
1040 m_gal->SetStrokeColor( color );
1041 m_gal->SetFillColor( color );
1042 m_gal->SetIsStroke( outline_mode );
1043 m_gal->SetIsFill( not outline_mode );
1044 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1045
1046 if( IsSolderMaskLayer( aLayer ) )
1047 width = width + aArc->GetSolderMaskExpansion() * 2;
1048
1049 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
1050 width = width + m_lockedShadowMargin;
1051
1052 m_gal->DrawArcSegment( center, radius, start_angle, angle, width, m_maxError );
1053 }
1054
1055 // Clearance lines
1056 if( IsClearanceLayer( aLayer )
1057 && pcbconfig() && pcbconfig()->m_Display.m_TrackClearance == SHOW_WITH_VIA_ALWAYS
1058 && !m_pcbSettings.m_isPrinting )
1059 {
1060 /*
1061 * Showing the clearance area is not obvious for optionally-flashed pads and vias, so we
1062 * choose to not display clearance lines at all on non-copper active layers. We follow
1063 * the same rule for tracks to be consistent (even though they don't have the same issue).
1064 */
1065 const PCB_LAYER_ID activeLayer = m_pcbSettings.GetActiveLayer();
1066 const BOARD& board = *aArc->GetBoard();
1067
1068 if( IsCopperLayer( activeLayer ) && board.GetVisibleLayers().test( activeLayer ) )
1069 {
1070 int clearance = aArc->GetOwnClearance( activeLayer );
1071
1072 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1073 m_gal->SetIsFill( false );
1074 m_gal->SetIsStroke( true );
1075 m_gal->SetStrokeColor( color );
1076
1077 m_gal->DrawArcSegment( center, radius, start_angle, angle, width + clearance * 2, m_maxError );
1078 }
1079 }
1080
1081#if 0
1082 // Debug only: enable this code only to test the TransformArcToPolygon function and display the polygon
1083 // outline created by it.
1084 // arcs on F_Cu are approximated with ERROR_INSIDE, others with ERROR_OUTSIDE
1085 SHAPE_POLY_SET cornerBuffer;
1087 TransformArcToPolygon( cornerBuffer, aArc->GetStart(), aArc->GetMid(), aArc->GetEnd(), width,
1088 m_maxError, errorloc );
1089 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1090 m_gal->SetIsFill( false );
1091 m_gal->SetIsStroke( true );
1092 m_gal->SetStrokeColor( COLOR4D( 0, 0, 1.0, 1.0 ) );
1093 m_gal->DrawPolygon( cornerBuffer );
1094#endif
1095
1096#if 0
1097 // Debug only: enable this code only to test the arc geometry.
1098 // Draw 3 lines from arc center to arc start, arc middle, arc end to show how the arc is defined
1099 SHAPE_ARC arc( aArc->GetStart(), aArc->GetMid(), aArc->GetEnd(), m_pcbSettings.m_outlineWidth );
1100 m_gal->SetIsFill( false );
1101 m_gal->SetIsStroke( true );
1102 m_gal->SetStrokeColor( COLOR4D( 0, 0, 1.0, 1.0 ) );
1103 m_gal->DrawSegment( arc.GetStart(), arc.GetCenter(), m_pcbSettings.m_outlineWidth );
1104 m_gal->DrawSegment( aArc->GetFocusPosition(), arc.GetCenter(), m_pcbSettings.m_outlineWidth );
1105 m_gal->DrawSegment( arc.GetEnd(), arc.GetCenter(), m_pcbSettings.m_outlineWidth );
1106#endif
1107
1108#if 0
1109 // Debug only: enable this code only to test the SHAPE_ARC::ConvertToPolyline function and display the
1110 // polyline created by it.
1111 SHAPE_ARC arc( aArc->GetCenter(), aArc->GetStart(), aArc->GetAngle(), aArc->GetWidth() );
1113 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1114 m_gal->SetIsFill( false );
1115 m_gal->SetIsStroke( true );
1116 m_gal->SetStrokeColor( COLOR4D( 0.3, 0.2, 0.5, 1.0 ) );
1117
1118 for( int idx = 1; idx < arcSpine.PointCount(); idx++ )
1119 m_gal->DrawSegment( arcSpine.CPoint( idx-1 ), arcSpine.CPoint( idx ), aArc->GetWidth() );
1120#endif
1121}
1122
1123
1124void PCB_PAINTER::draw( const PCB_VIA* aVia, int aLayer )
1125{
1126 const BOARD* board = aVia->GetBoard();
1127 COLOR4D color = m_pcbSettings.GetColor( aVia, aLayer );
1128 VECTOR2D center( aVia->GetStart() );
1129
1130 // draw hidden vias transparent not skipped so a recolour restores them without re-tessellating
1131
1132 // Chain highlight colour override for copper/hole layers.
1133 if( board && !m_pcbSettings.m_highlightedNetChain.IsEmpty()
1134 && ( IsCopperLayer( aLayer ) || IsViaCopperLayer( aLayer )
1135 || aLayer == LAYER_VIA_HOLES ) )
1136 {
1137 if( NETINFO_ITEM* netinfo = aVia->GetNet() )
1138 {
1139 if( netinfo->GetNetChain() == m_pcbSettings.m_highlightedNetChain )
1140 {
1141 COLOR4D chainColor =
1142 board->GetNetChainColor( m_pcbSettings.m_highlightedNetChain );
1143
1144 if( chainColor != COLOR4D::UNSPECIFIED )
1145 color = chainColor.WithAlpha( color.a );
1146 }
1147 }
1148 }
1149
1150 const int copperLayer = IsViaCopperLayer( aLayer ) ? aLayer - LAYER_VIA_COPPER_START : aLayer;
1151
1152 PCB_LAYER_ID currentLayer = ToLAYER_ID( copperLayer );
1153 PCB_LAYER_ID layerTop, layerBottom;
1154 aVia->LayerPair( &layerTop, &layerBottom );
1155
1156 // Blind/buried vias (and microvias) will use different hole and label rendering
1157 bool isBlindBuried = aVia->GetViaType() == VIATYPE::BLIND
1158 || aVia->GetViaType() == VIATYPE::BURIED
1159 || ( aVia->GetViaType() == VIATYPE::MICROVIA
1160 && ( layerTop != F_Cu || layerBottom != B_Cu ) );
1161
1162 // Draw description layer
1163 if( IsNetnameLayer( aLayer ) )
1164 {
1165 VECTOR2D position( center );
1166
1167 // Is anything that we can display enabled (netname and/or layers ids)?
1168 bool showNets = pcbconfig() && pcbconfig()->m_Display.m_NetNames != 0
1169 && !aVia->GetNetname().empty();
1170 bool showLayers = aVia->GetViaType() != VIATYPE::THROUGH;
1171
1172 if( !showNets && !showLayers )
1173 return;
1174
1175 double maxSize = PCB_RENDER_SETTINGS::MAX_FONT_SIZE;
1176 double size = aVia->GetWidth( currentLayer );
1177
1178 // Font size limits
1179 if( size > maxSize )
1180 size = maxSize;
1181
1182 m_gal->Save();
1183 m_gal->Translate( position );
1184
1185 // Default font settings
1186 m_gal->ResetTextAttributes();
1187 m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER );
1188 m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER );
1189 m_gal->SetFontBold( false );
1190 m_gal->SetFontItalic( false );
1191 m_gal->SetFontUnderlined( false );
1192 m_gal->SetTextMirrored( false );
1193 m_gal->SetStrokeColor( m_pcbSettings.GetColor( aVia, aLayer ) );
1194 m_gal->SetIsStroke( true );
1195 m_gal->SetIsFill( false );
1196
1197 // Set the text position via position. if only one text, it is on the via position
1198 // For 2 lines, the netname is slightly below the center, and the layer IDs above
1199 // the netname
1200 VECTOR2D textpos( 0.0, 0.0 );
1201
1202 wxString netname = aVia->GetDisplayNetname();
1203
1204 PCB_LAYER_ID topLayerId = aVia->TopLayer();
1205 PCB_LAYER_ID bottomLayerId = aVia->BottomLayer();
1206 int topLayer; // The via top layer number (from 1 to copper layer count)
1207 int bottomLayer; // The via bottom layer number (from 1 to copper layer count)
1208
1209 switch( topLayerId )
1210 {
1211 case F_Cu: topLayer = 1; break;
1212 case B_Cu: topLayer = board->GetCopperLayerCount(); break;
1213 default: topLayer = (topLayerId - B_Cu)/2 + 1; break;
1214 }
1215
1216 switch( bottomLayerId )
1217 {
1218 case F_Cu: bottomLayer = 1; break;
1219 case B_Cu: bottomLayer = board->GetCopperLayerCount(); break;
1220 default: bottomLayer = (bottomLayerId - B_Cu)/2 + 1; break;
1221 }
1222
1223 wxString layerIds;
1224#if wxUSE_UNICODE_WCHAR
1225 layerIds << std::to_wstring( topLayer ) << L'-' << std::to_wstring( bottomLayer );
1226#else
1227 layerIds << std::to_string( topLayer ) << '-' << std::to_string( bottomLayer );
1228#endif
1229
1230 // a good size is set room for at least 6 chars, to be able to print 2 lines of text,
1231 // or at least 3 chars for only the netname
1232 // (The layerIds string has 5 chars max)
1233 int minCharCnt = showLayers ? 6 : 3;
1234
1235 // approximate the size of netname and layerIds text:
1236 double tsize = 1.5 * size / std::max( PrintableCharCount( netname ), minCharCnt );
1237 tsize = std::min( tsize, size );
1238
1239 // Use a smaller text size to handle interline, pen size..
1240 tsize *= 0.75;
1241 VECTOR2D namesize( tsize, tsize );
1242
1243 // For 2 lines, adjust the text pos (move it a small amount to the bottom)
1244 if( showLayers && showNets )
1245 textpos.y += ( tsize * 1.3 )/ 2;
1246
1247 m_gal->SetGlyphSize( namesize );
1248 m_gal->SetLineWidth( namesize.x / 10.0 );
1249
1250 if( showNets )
1251 m_gal->BitmapText( netname, textpos, ANGLE_HORIZONTAL );
1252
1253 if( showLayers )
1254 {
1255 if( showNets )
1256 textpos.y -= tsize * 1.3;
1257
1258 m_gal->BitmapText( layerIds, textpos, ANGLE_HORIZONTAL );
1259 }
1260
1261 m_gal->Restore();
1262
1263 return;
1264 }
1265
1266 bool outline_mode = pcbconfig() && !pcbconfig()->m_Display.m_DisplayViaFill;
1267
1268 m_gal->SetStrokeColor( color );
1269 m_gal->SetFillColor( color );
1270 m_gal->SetIsStroke( true );
1271 m_gal->SetIsFill( false );
1272
1273 if( outline_mode )
1274 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1275
1276 if( aLayer == LAYER_VIA_HOLEWALLS )
1277 {
1278 double thickness =
1280 double drillRadius = getViaDrillSize( aVia ) / 2.0;
1281 double maxRadius = aVia->GetWidth( layerTop ) / 2.0;
1282 double radius = drillRadius + thickness;
1283
1284 // Clamp the hole wall so it doesn't extend beyond the via's copper
1285 if( radius > maxRadius )
1286 {
1287 radius = maxRadius;
1288 thickness = radius - drillRadius;
1289 }
1290
1291 if( thickness <= 0 )
1292 return;
1293
1294 if( !outline_mode )
1295 {
1296 m_gal->SetLineWidth( thickness );
1297 radius -= thickness / 2.0;
1298 }
1299
1300 // Underpaint the hole so that there aren't artifacts at its edge
1301 m_gal->SetIsFill( true );
1302
1303 m_gal->DrawCircle( center, radius );
1304
1305 // Draw backdrill indicators (semi-circles extending into the hole) on top of the
1306 // hole wall so they remain visible regardless of layer rendering order
1307 if( !m_pcbSettings.IsPrinting() )
1308 {
1309 std::optional<int> secDrill = aVia->GetSecondaryDrillSize();
1310 std::optional<int> terDrill = aVia->GetTertiaryDrillSize();
1311
1312 if( secDrill.value_or( 0 ) > 0 )
1313 {
1314 drawBackdrillIndicator( aVia, center, *secDrill,
1316 aVia->GetSecondaryDrillEndLayer() );
1317 }
1318
1319 if( terDrill.value_or( 0 ) > 0 )
1320 {
1321 drawBackdrillIndicator( aVia, center, *terDrill,
1323 aVia->GetTertiaryDrillEndLayer() );
1324 }
1325 }
1326 }
1327 else if( aLayer == LAYER_VIA_HOLES )
1328 {
1329 double radius = getViaDrillSize( aVia ) / 2.0;
1330
1331 m_gal->SetIsStroke( false );
1332 m_gal->SetIsFill( true );
1333
1334 if( isBlindBuried && !m_pcbSettings.IsPrinting() )
1335 {
1336 m_gal->SetIsStroke( false );
1337 m_gal->SetIsFill( true );
1338
1339 m_gal->SetFillColor( m_pcbSettings.GetColor( aVia, layerTop ) );
1340 m_gal->DrawArc( center, radius, EDA_ANGLE( 180, DEGREES_T ),
1341 EDA_ANGLE( 180, DEGREES_T ) );
1342
1343 m_gal->SetFillColor( m_pcbSettings.GetColor( aVia, layerBottom ) );
1344 m_gal->DrawArc( center, radius, EDA_ANGLE( 0, DEGREES_T ),
1345 EDA_ANGLE( 180, DEGREES_T ) );
1346 }
1347 else
1348 {
1349 m_gal->DrawCircle( center, radius );
1350 }
1351
1352 }
1353 else if( ( aLayer == F_Mask && aVia->IsOnLayer( F_Mask ) )
1354 || ( aLayer == B_Mask && aVia->IsOnLayer( B_Mask ) ) )
1355 {
1356 int margin = board->GetDesignSettings().m_SolderMaskExpansion;
1357
1358 m_gal->SetIsFill( true );
1359 m_gal->SetIsStroke( false );
1360
1361 m_gal->SetLineWidth( margin );
1362 m_gal->DrawCircle( center, aVia->GetWidth( currentLayer ) / 2.0 + margin );
1363 }
1364 else if( m_pcbSettings.IsPrinting() || IsCopperLayer( currentLayer ) )
1365 {
1366 int annular_width = ( aVia->GetWidth( currentLayer ) - getViaDrillSize( aVia ) ) / 2.0;
1367 double radius = aVia->GetWidth( currentLayer ) / 2.0;
1368 bool draw = false;
1369
1370 if( m_pcbSettings.IsPrinting() )
1371 {
1372 draw = aVia->FlashLayer( m_pcbSettings.GetPrintLayers() );
1373 }
1374 else if( aVia->IsSelected() )
1375 {
1376 draw = true;
1377 }
1378 else if( aVia->FlashLayer( board->GetVisibleLayers() & board->GetEnabledLayers() ) )
1379 {
1380 draw = true;
1381 }
1382
1383 if( !aVia->FlashLayer( currentLayer ) )
1384 draw = false;
1385
1386 if( !outline_mode )
1387 {
1388 m_gal->SetLineWidth( annular_width );
1389 radius -= annular_width / 2.0;
1390 }
1391
1392 if( draw )
1393 m_gal->DrawCircle( center, radius );
1394
1395 // Draw post-machining indicator if this layer is post-machined
1396 if( !m_pcbSettings.IsPrinting() && draw )
1397 {
1398 drawPostMachiningIndicator( aVia, center, currentLayer );
1399 }
1400 }
1401 else if( aLayer == LAYER_LOCKED_ITEM_SHADOW ) // draw a ring around the via
1402 {
1403 m_gal->SetLineWidth( m_lockedShadowMargin );
1404
1405 m_gal->DrawCircle( center, ( aVia->GetWidth( currentLayer ) + m_lockedShadowMargin ) / 2.0 );
1406 }
1407
1408 // Clearance lines
1409 if( IsClearanceLayer( aLayer ) && pcbconfig()
1410 && pcbconfig()->m_Display.m_TrackClearance == SHOW_WITH_VIA_ALWAYS
1411 && !m_pcbSettings.m_isPrinting )
1412 {
1413 const PCB_LAYER_ID copperLayerForClearance = ToLAYER_ID( aLayer - LAYER_CLEARANCE_START );
1414
1415 double radius;
1416
1417 if( aVia->FlashLayer( copperLayerForClearance ) )
1418 radius = aVia->GetWidth( copperLayerForClearance ) / 2.0;
1419 else
1421
1422 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1423 m_gal->SetIsFill( false );
1424 m_gal->SetIsStroke( true );
1425 m_gal->SetStrokeColor( color );
1426 m_gal->DrawCircle( center, radius + aVia->GetOwnClearance( copperLayerForClearance ) );
1427 }
1428}
1429
1430
1431void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
1432{
1433 COLOR4D color = m_pcbSettings.GetColor( aPad, aLayer );
1434 const int copperLayer = IsPadCopperLayer( aLayer ) ? aLayer - LAYER_PAD_COPPER_START : aLayer;
1435 PCB_LAYER_ID pcbLayer = static_cast<PCB_LAYER_ID>( copperLayer );
1436
1437 if( IsNetnameLayer( aLayer ) )
1438 {
1439 PCBNEW_SETTINGS::DISPLAY_OPTIONS* displayOpts = pcbconfig() ? &pcbconfig()->m_Display : nullptr;
1440 wxString netname;
1441 wxString padNumber;
1442
1443 if( viewer_settings()->m_ViewersDisplay.m_DisplayPadNumbers )
1444 {
1445 padNumber = UnescapeString( aPad->GetNumber() );
1446
1447 if( dynamic_cast<CVPCB_SETTINGS*>( viewer_settings() ) )
1448 netname = aPad->GetPinFunction();
1449 }
1450
1451 if( displayOpts && !dynamic_cast<CVPCB_SETTINGS*>( viewer_settings() ) )
1452 {
1453 if( displayOpts->m_NetNames == 1 || displayOpts->m_NetNames == 3 )
1454 netname = aPad->GetDisplayNetname();
1455
1456 if( aPad->IsNoConnectPad() )
1457 netname = wxT( "x" );
1458 else if( aPad->IsFreePad() )
1459 netname = wxT( "*" );
1460 }
1461
1462 if( netname.IsEmpty() && padNumber.IsEmpty() )
1463 return;
1464
1465 BOX2I padBBox = aPad->GetBoundingBox();
1466 VECTOR2D position = padBBox.Centre();
1467 VECTOR2D padsize = VECTOR2D( padBBox.GetSize() );
1468
1469 if( aPad->IsEntered() )
1470 {
1471 FOOTPRINT* fp = aPad->GetParentFootprint();
1472
1473 // Find the number box
1474 for( const BOARD_ITEM* aItem : fp->GraphicalItems() )
1475 {
1476 if( aItem->Type() == PCB_SHAPE_T )
1477 {
1478 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
1479
1480 if( shape->IsProxyItem() && shape->GetShape() == SHAPE_T::RECTANGLE )
1481 {
1482 position = shape->GetCenter();
1483 padsize = shape->GetBotRight() - shape->GetTopLeft();
1484
1485 // We normally draw a bit outside the pad, but this will be somewhat
1486 // unexpected when the user has drawn a box.
1487 padsize *= 0.9;
1488
1489 break;
1490 }
1491 }
1492 }
1493 }
1494 else if( aPad->GetShape( pcbLayer ) == PAD_SHAPE::CUSTOM )
1495 {
1496 // See if we have a number box
1497 for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives( pcbLayer ) )
1498 {
1499 if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::RECTANGLE )
1500 {
1501 position = primitive->GetCenter();
1502 RotatePoint( position, aPad->GetOrientation() );
1503 position += aPad->ShapePos( pcbLayer );
1504
1505 padsize.x = abs( primitive->GetBotRight().x - primitive->GetTopLeft().x );
1506 padsize.y = abs( primitive->GetBotRight().y - primitive->GetTopLeft().y );
1507
1508 // We normally draw a bit outside the pad, but this will be somewhat
1509 // unexpected when the user has drawn a box.
1510 padsize *= 0.9;
1511
1512 break;
1513 }
1514 }
1515 }
1516
1517 if( aPad->GetShape( pcbLayer ) != PAD_SHAPE::CUSTOM )
1518 {
1519 // Don't allow a 45° rotation to bloat a pad's bounding box unnecessarily
1520 double limit = std::min( aPad->GetSize( pcbLayer ).x,
1521 aPad->GetSize( pcbLayer ).y ) * 1.1;
1522
1523 if( padsize.x > limit && padsize.y > limit )
1524 {
1525 padsize.x = limit;
1526 padsize.y = limit;
1527 }
1528 }
1529
1530 double maxSize = PCB_RENDER_SETTINGS::MAX_FONT_SIZE;
1531 double size = padsize.y;
1532
1533 m_gal->Save();
1534 m_gal->Translate( position );
1535
1536 // Keep the size ratio for the font, but make it smaller
1537 if( padsize.x < ( padsize.y * 0.95 ) )
1538 {
1539 m_gal->Rotate( -ANGLE_90.AsRadians() );
1540 size = padsize.x;
1541 std::swap( padsize.x, padsize.y );
1542 }
1543
1544 // Font size limits
1545 if( size > maxSize )
1546 size = maxSize;
1547
1548 // Default font settings
1549 m_gal->ResetTextAttributes();
1550 m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER );
1551 m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER );
1552 m_gal->SetFontBold( false );
1553 m_gal->SetFontItalic( false );
1554 m_gal->SetFontUnderlined( false );
1555 m_gal->SetTextMirrored( false );
1556 m_gal->SetStrokeColor( m_pcbSettings.GetColor( aPad, aLayer ) );
1557 m_gal->SetIsStroke( true );
1558 m_gal->SetIsFill( false );
1559
1560 // We have already translated the GAL to be centered at the center of the pad's
1561 // bounding box
1562 VECTOR2I textpos( 0, 0 );
1563
1564 // Divide the space, to display both pad numbers and netnames and set the Y text
1565 // offset position to display 2 lines
1566 int Y_offset_numpad = 0;
1567 int Y_offset_netname = 0;
1568
1569 if( !netname.IsEmpty() && !padNumber.IsEmpty() )
1570 {
1571 // The magic numbers are defined experimentally for a better look.
1572 size = size / 2.5;
1573 Y_offset_netname = size / 1.4; // netname size is usually smaller than num pad
1574 // so the offset can be smaller
1575 Y_offset_numpad = size / 1.7;
1576 }
1577
1578 // We are using different fonts to display names, depending on the graphic
1579 // engine (OpenGL or Cairo).
1580 // Xscale_for_stroked_font adjust the text X size for cairo (stroke fonts) engine
1581 const double Xscale_for_stroked_font = 0.9;
1582
1583 if( !netname.IsEmpty() )
1584 {
1585 // approximate the size of net name text:
1586 // We use a size for at least 5 chars, to give a good look even for short names
1587 // (like VCC, GND...)
1588 double tsize = 1.5 * padsize.x / std::max( PrintableCharCount( netname )+1, 5 );
1589 tsize = std::min( tsize, size );
1590
1591 // Use a smaller text size to handle interline, pen size...
1592 tsize *= 0.85;
1593
1594 // Round and oval pads have less room to display the net name than other
1595 // (i.e RECT) shapes, so reduce the text size for these shapes
1596 if( aPad->GetShape( pcbLayer ) == PAD_SHAPE::CIRCLE
1597 || aPad->GetShape( pcbLayer ) == PAD_SHAPE::OVAL )
1598 {
1599 tsize *= 0.9;
1600 }
1601
1602 VECTOR2D namesize( tsize*Xscale_for_stroked_font, tsize );
1603 textpos.y = std::min( tsize * 1.4, double( Y_offset_netname ) );
1604
1605 m_gal->SetGlyphSize( namesize );
1606 m_gal->SetLineWidth( namesize.x / 6.0 );
1607 m_gal->SetFontBold( true );
1608 m_gal->BitmapText( netname, textpos, ANGLE_HORIZONTAL );
1609 }
1610
1611 if( !padNumber.IsEmpty() )
1612 {
1613 // approximate the size of the pad number text:
1614 // We use a size for at least 3 chars, to give a good look even for short numbers
1615 double tsize = 1.5 * padsize.x / std::max( PrintableCharCount( padNumber ), 3 );
1616 tsize = std::min( tsize, size );
1617
1618 // Use a smaller text size to handle interline, pen size...
1619 tsize *= 0.85;
1620 tsize = std::min( tsize, size );
1621 VECTOR2D numsize( tsize*Xscale_for_stroked_font, tsize );
1622 textpos.y = -Y_offset_numpad;
1623
1624 m_gal->SetGlyphSize( numsize );
1625 m_gal->SetLineWidth( numsize.x / 6.0 );
1626 m_gal->SetFontBold( true );
1627 m_gal->BitmapText( padNumber, textpos, ANGLE_HORIZONTAL );
1628 }
1629
1630 m_gal->Restore();
1631
1632 return;
1633 }
1634 else if( aLayer == LAYER_PAD_HOLEWALLS )
1635 {
1636 m_gal->SetIsFill( true );
1637 m_gal->SetIsStroke( false );
1639 double lineWidth = widthFactor * m_holePlatingThickness;
1640 lineWidth = std::min( lineWidth, aPad->GetSizeX() / 2.0 );
1641 lineWidth = std::min( lineWidth, aPad->GetSizeY() / 2.0 );
1642
1643 m_gal->SetFillColor( color );
1644 m_gal->SetMinLineWidth( lineWidth );
1645
1646 std::shared_ptr<SHAPE_SEGMENT> slot = aPad->GetEffectiveHoleShape();
1647
1648 if( slot->GetSeg().A == slot->GetSeg().B ) // Circular hole
1649 {
1650 double holeRadius = slot->GetWidth() / 2.0;
1651 m_gal->DrawHoleWall( slot->GetSeg().A, holeRadius, lineWidth );
1652 }
1653 else
1654 {
1655 int holeSize = slot->GetWidth() + ( 2 * lineWidth );
1656 m_gal->DrawSegment( slot->GetSeg().A, slot->GetSeg().B, holeSize );
1657 }
1658
1659 m_gal->SetMinLineWidth( 1.0 );
1660
1661 // Draw backdrill indicators on top of the hole wall so they remain visible
1662 // regardless of layer rendering order
1663 if( !m_pcbSettings.IsPrinting() && aPad->GetDrillSizeX() > 0 )
1664 {
1665 VECTOR2I holePos = slot->GetSeg().A;
1666 VECTOR2I secDrill = aPad->GetSecondaryDrillSize();
1667 VECTOR2I terDrill = aPad->GetTertiaryDrillSize();
1668
1669 if( secDrill.x > 0 )
1670 {
1671 drawBackdrillIndicator( aPad, holePos, secDrill.x,
1673 aPad->GetSecondaryDrillEndLayer() );
1674 }
1675
1676 if( terDrill.x > 0 )
1677 {
1678 drawBackdrillIndicator( aPad, holePos, terDrill.x,
1680 aPad->GetTertiaryDrillEndLayer() );
1681 }
1682 }
1683
1684 return;
1685 }
1686
1687 bool outline_mode = !viewer_settings()->m_ViewersDisplay.m_DisplayPadFill;
1688
1689 if( m_pcbSettings.m_ForcePadSketchModeOn )
1690 outline_mode = true;
1691
1692 bool drawShape = false;
1693
1694 if( m_pcbSettings.IsPrinting() )
1695 {
1696 drawShape = aPad->FlashLayer( m_pcbSettings.GetPrintLayers() );
1697 }
1698 else if( ( aLayer < PCB_LAYER_ID_COUNT || IsPadCopperLayer( aLayer ) )
1699 && aPad->FlashLayer( pcbLayer ) )
1700 {
1701 drawShape = true;
1702 }
1703 else if( aPad->IsSelected() )
1704 {
1705 drawShape = true;
1706 outline_mode = true;
1707 }
1708 else if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
1709 {
1710 drawShape = true;
1711 outline_mode = false;
1712 }
1713
1714 // Plated holes are always filled as they use a solid BG fill to
1715 // draw the "hole" over the hole-wall segment/circle.
1716 if( outline_mode && aLayer != LAYER_PAD_PLATEDHOLES )
1717 {
1718 // Outline mode
1719 m_gal->SetIsFill( false );
1720 m_gal->SetIsStroke( true );
1721 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1722 m_gal->SetStrokeColor( color );
1723 }
1724 else
1725 {
1726 // Filled mode
1727 m_gal->SetIsFill( true );
1728 m_gal->SetIsStroke( false );
1729 m_gal->SetFillColor( color );
1730 }
1731
1732 if( aLayer == LAYER_PAD_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES )
1733 {
1734 SHAPE_SEGMENT slot = getPadHoleShape( aPad );
1735 VECTOR2I center = slot.GetSeg().A;
1736
1737 if( slot.GetSeg().A == slot.GetSeg().B ) // Circular hole
1738 m_gal->DrawCircle( center, slot.GetWidth() / 2.0 );
1739 else
1740 m_gal->DrawSegment( slot.GetSeg().A, slot.GetSeg().B, slot.GetWidth() );
1741 }
1742 else if( drawShape )
1743 {
1744 VECTOR2I pad_size = aPad->GetSize( pcbLayer );
1745 VECTOR2I margin;
1746
1747 auto getExpansion =
1748 [&]( PCB_LAYER_ID layer )
1749 {
1750 VECTOR2I expansion;
1751
1752 switch( aLayer )
1753 {
1754 case F_Mask:
1755 case B_Mask:
1756 expansion.x = expansion.y = aPad->GetSolderMaskExpansion( layer );
1757 break;
1758
1759 case F_Paste:
1760 case B_Paste:
1761 expansion = aPad->GetSolderPasteMargin( layer );
1762 break;
1763
1764 default:
1765 expansion.x = expansion.y = 0;
1766 break;
1767 }
1768
1769 return expansion;
1770 };
1771
1772 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
1773 {
1774 LSET visibleLayers = aPad->GetBoard()->GetVisibleLayers()
1775 & aPad->GetBoard()->GetEnabledLayers()
1776 & aPad->GetLayerSet();
1777
1778 for( PCB_LAYER_ID layer : visibleLayers )
1779 margin = std::max( margin, getExpansion( layer ) );
1780
1781 margin.x += m_lockedShadowMargin / 2;
1782 margin.y += m_lockedShadowMargin / 2;
1783 }
1784 else
1785 {
1786 margin = getExpansion( pcbLayer );
1787 }
1788
1789 std::unique_ptr<PAD> dummyPad;
1790 std::shared_ptr<SHAPE_COMPOUND> shapes;
1791
1792 // Drawing components of compound shapes in outline mode produces a mess.
1793 bool simpleShapes = !outline_mode;
1794
1795 // When this layer has post-machining (counterbore/countersink), GetEffectiveShape returns
1796 // the counterbore hole circle (for DRC purposes), not the actual copper shape. Force the
1797 // slower TransformShapeToPolygon path which always returns the correct copper shape.
1798 if( IsCopperLayer( pcbLayer ) && aPad->GetPostMachiningKnockout( pcbLayer ) > 0 )
1799 simpleShapes = false;
1800
1801 if( simpleShapes )
1802 {
1803 if( ( margin.x != margin.y && aPad->GetShape( pcbLayer ) != PAD_SHAPE::CUSTOM )
1804 || ( aPad->GetShape( pcbLayer ) == PAD_SHAPE::ROUNDRECT
1805 && ( margin.x < 0 || margin.y < 0 ) ) )
1806 {
1807 // Our algorithms below (polygon inflation in particular) can't handle differential
1808 // inflation along separate axes. So for those cases we build a dummy pad instead,
1809 // and inflate it.
1810
1811 // Margin is added to both sides. If the total margin is larger than the pad
1812 // then don't display this layer
1813 if( pad_size.x + 2 * margin.x <= 0 || pad_size.y + 2 * margin.y <= 0 )
1814 return;
1815
1816 dummyPad.reset( static_cast<PAD*>( aPad->Duplicate( IGNORE_PARENT_GROUP ) ) );
1817
1818 int initial_radius = dummyPad->GetRoundRectCornerRadius( pcbLayer );
1819
1820 dummyPad->SetSize( pcbLayer, pad_size + margin + margin );
1821
1822 if( dummyPad->GetShape( pcbLayer ) == PAD_SHAPE::ROUNDRECT )
1823 {
1824 // To keep the right margin around the corners, we need to modify the corner radius.
1825 // We must have only one radius correction, so use the smallest absolute margin.
1826 int radius_margin = std::max( margin.x, margin.y ); // radius_margin is < 0
1827 dummyPad->SetRoundRectCornerRadius(
1828 pcbLayer, std::max( initial_radius + radius_margin, 0 ) );
1829 }
1830
1831 shapes = std::dynamic_pointer_cast<SHAPE_COMPOUND>(
1832 dummyPad->GetEffectiveShape( pcbLayer ) );
1833 margin.x = margin.y = 0;
1834 }
1835 else
1836 {
1837 shapes = std::dynamic_pointer_cast<SHAPE_COMPOUND>(
1838 aPad->GetEffectiveShape( pcbLayer ) );
1839 }
1840
1841 // The dynamic cast above will fail if the pad returned the hole shape or a null shape
1842 // instead of a SHAPE_COMPOUND, which happens if we're on a copper layer and the pad has
1843 // no shape on that layer.
1844 if( !shapes )
1845 return;
1846
1847 if( aPad->GetShape( pcbLayer ) == PAD_SHAPE::CUSTOM && ( margin.x || margin.y ) )
1848 {
1849 // We can't draw as shapes because we don't know which edges are internal and which
1850 // are external (so we don't know when to apply the margin and when not to).
1851 simpleShapes = false;
1852 }
1853
1854 for( const SHAPE* shape : shapes->Shapes() )
1855 {
1856 if( !simpleShapes )
1857 break;
1858
1859 switch( shape->Type() )
1860 {
1861 case SH_SEGMENT:
1862 case SH_CIRCLE:
1863 case SH_RECT:
1864 case SH_SIMPLE:
1865 // OK so far
1866 break;
1867
1868 default:
1869 // Not OK
1870 simpleShapes = false;
1871 break;
1872 }
1873 }
1874 }
1875
1876 const auto drawOneSimpleShape =
1877 [&]( const SHAPE& aShape )
1878 {
1879 switch( aShape.Type() )
1880 {
1881 case SH_SEGMENT:
1882 {
1883 const SHAPE_SEGMENT& seg = (const SHAPE_SEGMENT&) aShape;
1884 int effectiveWidth = seg.GetWidth() + 2 * margin.x;
1885
1886 if( effectiveWidth > 0 )
1887 m_gal->DrawSegment( seg.GetSeg().A, seg.GetSeg().B, effectiveWidth );
1888
1889 break;
1890 }
1891
1892 case SH_CIRCLE:
1893 {
1894 const SHAPE_CIRCLE& circle = (const SHAPE_CIRCLE&) aShape;
1895 int effectiveRadius = circle.GetRadius() + margin.x;
1896
1897 if( effectiveRadius > 0 )
1898 m_gal->DrawCircle( circle.GetCenter(), effectiveRadius );
1899
1900 break;
1901 }
1902
1903 case SH_RECT:
1904 {
1905 const SHAPE_RECT& r = (const SHAPE_RECT&) aShape;
1906 VECTOR2I pos = r.GetPosition();
1907 VECTOR2I effectiveMargin = margin;
1908
1909 if( effectiveMargin.x < 0 )
1910 {
1911 // A negative margin just produces a smaller rect.
1912 VECTOR2I effectiveSize = r.GetSize() + effectiveMargin;
1913
1914 if( effectiveSize.x > 0 && effectiveSize.y > 0 )
1915 m_gal->DrawRectangle( pos - effectiveMargin, pos + effectiveSize );
1916 }
1917 else if( effectiveMargin.x > 0 )
1918 {
1919 // A positive margin produces a larger rect, but with rounded corners
1920 m_gal->DrawRectangle( r.GetPosition(), r.GetPosition() + r.GetSize() );
1921
1922 // Use segments to produce the margin with rounded corners
1923 m_gal->DrawSegment( pos,
1924 pos + VECTOR2I( r.GetWidth(), 0 ),
1925 effectiveMargin.x * 2 );
1926 m_gal->DrawSegment( pos + VECTOR2I( r.GetWidth(), 0 ),
1927 pos + r.GetSize(),
1928 effectiveMargin.x * 2 );
1929 m_gal->DrawSegment( pos + r.GetSize(),
1930 pos + VECTOR2I( 0, r.GetHeight() ),
1931 effectiveMargin.x * 2 );
1932 m_gal->DrawSegment( pos + VECTOR2I( 0, r.GetHeight() ),
1933 pos,
1934 effectiveMargin.x * 2 );
1935 }
1936 else
1937 {
1938 m_gal->DrawRectangle( r.GetPosition(), r.GetPosition() + r.GetSize() );
1939 }
1940
1941 break;
1942 }
1943
1944 case SH_SIMPLE:
1945 {
1946 const SHAPE_SIMPLE& poly = static_cast<const SHAPE_SIMPLE&>( aShape );
1947
1948 if( poly.PointCount() < 2 ) // Careful of empty pads
1949 break;
1950
1951 if( margin.x < 0 ) // The poly shape must be deflated
1952 {
1953 SHAPE_POLY_SET outline;
1954 outline.NewOutline();
1955
1956 for( int ii = 0; ii < poly.PointCount(); ++ii )
1957 outline.Append( poly.CPoint( ii ) );
1958
1960
1961 m_gal->DrawPolygon( outline );
1962 }
1963 else
1964 {
1965 m_gal->DrawPolygon( poly.Vertices() );
1966 }
1967
1968 // Now add on a rounded margin (using segments) if the margin > 0
1969 if( margin.x > 0 )
1970 {
1971 for( size_t ii = 0; ii < poly.GetSegmentCount(); ++ii )
1972 {
1973 SEG seg = poly.GetSegment( ii );
1974 m_gal->DrawSegment( seg.A, seg.B, margin.x * 2 );
1975 }
1976 }
1977
1978 break;
1979 }
1980
1981 default:
1982 // Better not get here; we already pre-flighted the shapes...
1983 break;
1984 }
1985 };
1986
1987 if( simpleShapes )
1988 {
1989 for( const SHAPE* shape : shapes->Shapes() )
1990 drawOneSimpleShape( *shape );
1991 }
1992 else
1993 {
1994 // This is expensive. Avoid if possible.
1995 SHAPE_POLY_SET polySet;
1996 aPad->TransformShapeToPolygon( polySet, ToLAYER_ID( aLayer ), margin.x, m_maxError, ERROR_INSIDE );
1997 m_gal->DrawPolygon( polySet );
1998 }
1999
2000 }
2001
2002 if( !m_pcbSettings.IsPrinting() && IsCopperLayer( pcbLayer ) && aPad->GetDrillSizeX() > 0 )
2003 {
2004 VECTOR2D holePos = aPad->GetPosition() + aPad->GetOffset( pcbLayer );
2005 drawPostMachiningIndicator( aPad, holePos, pcbLayer );
2006 }
2007
2008 if( IsClearanceLayer( aLayer )
2009 && ( ( pcbconfig() && pcbconfig()->m_Display.m_PadClearance ) || !pcbconfig() )
2010 && !m_pcbSettings.m_isPrinting )
2011 {
2012 const PCB_LAYER_ID copperLayerForClearance = ToLAYER_ID( aLayer - LAYER_CLEARANCE_START );
2013
2014 if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
2015 color = m_pcbSettings.GetLayerColor( LAYER_NON_PLATEDHOLES );
2016
2017 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2018 m_gal->SetIsStroke( true );
2019 m_gal->SetIsFill( false );
2020 m_gal->SetStrokeColor( color );
2021
2022 const int clearance = aPad->GetOwnClearance( copperLayerForClearance );
2023
2024 if( aPad->FlashLayer( copperLayerForClearance ) && clearance > 0 )
2025 {
2026 auto shape = std::dynamic_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape( pcbLayer ) );
2027
2028 if( shape && shape->Size() == 1 && shape->Shapes()[0]->Type() == SH_SEGMENT )
2029 {
2030 const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shape->Shapes()[0];
2031 m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B,
2032 seg->GetWidth() + 2 * clearance );
2033 }
2034 else if( shape && shape->Size() == 1 && shape->Shapes()[0]->Type() == SH_CIRCLE )
2035 {
2036 const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shape->Shapes()[0];
2037 m_gal->DrawCircle( circle->GetCenter(), circle->GetRadius() + clearance );
2038 }
2039 else
2040 {
2041 SHAPE_POLY_SET polySet;
2042
2043 // Use ERROR_INSIDE because it avoids Clipper and is therefore much faster.
2044 aPad->TransformShapeToPolygon( polySet, copperLayerForClearance, clearance,
2046
2047 if( polySet.Outline( 0 ).PointCount() > 2 ) // Careful of empty pads
2048 m_gal->DrawPolygon( polySet );
2049 }
2050 }
2051 else if( aPad->GetEffectiveHoleShape() && clearance > 0 )
2052 {
2053 std::shared_ptr<SHAPE_SEGMENT> slot = aPad->GetEffectiveHoleShape();
2054 m_gal->DrawSegment( slot->GetSeg().A, slot->GetSeg().B,
2055 slot->GetWidth() + 2 * clearance );
2056 }
2057 }
2058
2059 if( m_pcbSettings.IsHighlightEnabled()
2060 && m_pcbSettings.GetHighlightNetCodes().contains( aPad->GetNetCode() ) )
2061 {
2062 NETINFO_ITEM* net = aPad->GetNet();
2063 if( net && ( net->GetTerminalPad( 0 ) == aPad || net->GetTerminalPad( 1 ) == aPad ) )
2064 {
2065 BOX2I box = aPad->GetBoundingBox();
2066 m_gal->SetIsFill( false );
2067 m_gal->SetIsStroke( true );
2068
2069 // Base emphasis (net highlight)
2070 COLOR4D termColor = color.Brightened( 0.2 );
2071 int baseWidth = m_pcbSettings.m_outlineWidth * 2;
2072
2073 // If a grouped chain highlight is active and this pad belongs to that chain,
2074 // make the emphasis stronger (brighter + thicker + inset second rectangle).
2075 if( !m_pcbSettings.m_highlightedNetChain.IsEmpty()
2076 && net && net->GetNetChain() == m_pcbSettings.m_highlightedNetChain )
2077 {
2078 // Prefer the chain's own colour override if the board has one.
2079 if( const BOARD* board = aPad->GetBoard() )
2080 {
2081 COLOR4D chainColor =
2082 board->GetNetChainColor( m_pcbSettings.m_highlightedNetChain );
2083
2084 if( chainColor != COLOR4D::UNSPECIFIED )
2085 termColor = chainColor;
2086 else
2087 termColor = termColor.Brightened( 0.25 );
2088 }
2089 else
2090 {
2091 termColor = termColor.Brightened( 0.25 );
2092 }
2093
2094 baseWidth = m_pcbSettings.m_outlineWidth * 3;
2095 }
2096
2097 m_gal->SetStrokeColor( termColor );
2098 m_gal->SetLineWidth( baseWidth );
2099 m_gal->DrawRectangle( box.GetOrigin(), box.GetEnd() );
2100
2101 if( !m_pcbSettings.m_highlightedNetChain.IsEmpty()
2102 && net && net->GetNetChain() == m_pcbSettings.m_highlightedNetChain )
2103 {
2104 // Draw an inner rectangle for additional visual distinction.
2105 // Shrink by one outline width equivalent to avoid excessive size.
2106 int inset = baseWidth * 2; // screen-space approx; acceptable heuristic
2107 BOX2I inner = box;
2108 inner.Inflate( -inset, -inset );
2109 if( inner.GetWidth() > 0 && inner.GetHeight() > 0 )
2110 {
2111 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2112 m_gal->DrawRectangle( inner.GetOrigin(), inner.GetEnd() );
2113 }
2114 }
2115 }
2116 }
2117}
2118
2119
2120void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
2121{
2122 COLOR4D color = m_pcbSettings.GetColor( aShape, aLayer );
2124 int thickness = getLineThickness( aShape->GetWidth() );
2125 LINE_STYLE lineStyle = aShape->GetStroke().GetLineStyle();
2126 bool isSolidFill = aShape->IsSolidFill();
2127 bool isHatchedFill = aShape->IsHatchedFill();
2128
2129 if( lineStyle == LINE_STYLE::DEFAULT )
2130 lineStyle = LINE_STYLE::SOLID;
2131
2132 if( IsSolderMaskLayer( aLayer )
2133 && aShape->HasSolderMask()
2134 && IsExternalCopperLayer( aShape->GetLayer() ) )
2135 {
2136 lineStyle = LINE_STYLE::SOLID;
2137 thickness += aShape->GetSolderMaskExpansion() * 2;
2138
2139 if( isHatchedFill )
2140 {
2141 isSolidFill = true;
2142 isHatchedFill = false;
2143 }
2144 }
2145
2146 if( IsNetnameLayer( aLayer ) )
2147 {
2148 // Net names are shown only in board editor:
2150 return;
2151
2152 if( !pcbconfig() || pcbconfig()->m_Display.m_NetNames < 2 )
2153 return;
2154
2155 if( aShape->GetNetCode() <= NETINFO_LIST::UNCONNECTED )
2156 return;
2157
2158 const wxString& netname = aShape->GetDisplayNetname();
2159
2160 if( netname.IsEmpty() )
2161 return;
2162
2163 if( aShape->GetShape() == SHAPE_T::SEGMENT )
2164 {
2165 SHAPE_SEGMENT seg( { aShape->GetStart(), aShape->GetEnd() }, aShape->GetWidth() );
2166 renderNetNameForSegment( seg, color, netname );
2167 return;
2168 }
2169
2170 // TODO: Maybe use some of the pad code?
2171
2172 return;
2173 }
2174
2175 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
2176 {
2177 color = m_pcbSettings.GetColor( aShape, aLayer );
2178 thickness = thickness + m_lockedShadowMargin;
2179
2180 // Note: on LAYER_LOCKED_ITEM_SHADOW always draw shadow shapes as continuous lines
2181 // otherwise the look is very strange and ugly
2182 lineStyle = LINE_STYLE::SOLID;
2183 }
2184
2185 if( aLayer == LAYER_CONSTRAINT_SHADOW )
2186 {
2187 color = m_pcbSettings.GetColor( aShape, aLayer );
2188 int margin = m_lockedShadowMargin;
2189
2190 // Selected constraint members drawn brighter and thicker
2191 if( m_pcbSettings.GetHighlightedConstraintMembers().count( aShape->m_Uuid ) )
2192 {
2193 color = color.Brightened( 0.5 );
2194 margin *= 2;
2195 }
2196
2197 thickness = thickness + margin;
2198 lineStyle = LINE_STYLE::SOLID;
2199 }
2200
2201 if( outline_mode )
2202 {
2203 m_gal->SetIsFill( false );
2204 m_gal->SetIsStroke( true );
2205 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2206 }
2207
2208 m_gal->SetFillColor( color );
2209 m_gal->SetStrokeColor( color );
2210
2211 if( lineStyle == LINE_STYLE::SOLID || aShape->IsSolidFill() )
2212 {
2213 switch( aShape->GetShape() )
2214 {
2215 case SHAPE_T::SEGMENT:
2216 if( aShape->IsProxyItem() )
2217 {
2218 std::vector<VECTOR2I> pts;
2219 VECTOR2I offset = ( aShape->GetEnd() - aShape->GetStart() ).Perpendicular();
2220 offset = offset.Resize( thickness / 2 );
2221
2222 pts.push_back( aShape->GetStart() + offset );
2223 pts.push_back( aShape->GetStart() - offset );
2224 pts.push_back( aShape->GetEnd() - offset );
2225 pts.push_back( aShape->GetEnd() + offset );
2226
2227 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2228 m_gal->DrawLine( pts[0], pts[1] );
2229 m_gal->DrawLine( pts[1], pts[2] );
2230 m_gal->DrawLine( pts[2], pts[3] );
2231 m_gal->DrawLine( pts[3], pts[0] );
2232 m_gal->DrawLine( ( pts[0] + pts[1] ) / 2, ( pts[1] + pts[2] ) / 2 );
2233 m_gal->DrawLine( ( pts[1] + pts[2] ) / 2, ( pts[2] + pts[3] ) / 2 );
2234 m_gal->DrawLine( ( pts[2] + pts[3] ) / 2, ( pts[3] + pts[0] ) / 2 );
2235 m_gal->DrawLine( ( pts[3] + pts[0] ) / 2, ( pts[0] + pts[1] ) / 2 );
2236 }
2237 else if( outline_mode )
2238 {
2239 m_gal->DrawSegment( aShape->GetStart(), aShape->GetEnd(), thickness );
2240 }
2241 else if( lineStyle == LINE_STYLE::SOLID )
2242 {
2243 m_gal->SetIsFill( true );
2244 m_gal->SetIsStroke( false );
2245
2246 m_gal->DrawSegment( aShape->GetStart(), aShape->GetEnd(), thickness );
2247 }
2248
2249 break;
2250
2251 case SHAPE_T::RECTANGLE:
2252 {
2253 if( aShape->GetCornerRadius() > 0 )
2254 {
2255 // Creates a normalized ROUNDRECT item
2256 // (GetRectangleWidth() and GetRectangleHeight() can be < 0 with transforms
2257 ROUNDRECT rr( SHAPE_RECT( aShape->GetStart(), aShape->GetRectangleWidth(),
2258 aShape->GetRectangleHeight() ),
2259 aShape->GetCornerRadius(), true /* normalize */ );
2260 SHAPE_POLY_SET poly;
2261 rr.TransformToPolygon( poly, aShape->GetMaxError() );
2262 SHAPE_LINE_CHAIN outline = poly.Outline( 0 );
2263
2264 if( aShape->IsProxyItem() )
2265 {
2266 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2267 m_gal->DrawPolygon( outline );
2268 }
2269 else if( outline_mode )
2270 {
2271 m_gal->DrawSegmentChain( outline, thickness );
2272 }
2273 else
2274 {
2275 m_gal->SetIsFill( true );
2276 m_gal->SetIsStroke( false );
2277
2278 if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
2279 {
2280 m_gal->DrawSegmentChain( outline, thickness );
2281 }
2282
2283 if( isSolidFill )
2284 {
2285 if( thickness < 0 )
2286 {
2287 SHAPE_POLY_SET deflated_shape = outline;
2288 deflated_shape.Inflate( thickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, m_maxError );
2289 m_gal->DrawPolygon( deflated_shape );
2290 }
2291 else
2292 {
2293 m_gal->DrawPolygon( outline );
2294 }
2295 }
2296 }
2297 }
2298 else
2299 {
2300 std::vector<VECTOR2I> pts = aShape->GetRectCorners();
2301
2302 if( aShape->IsProxyItem() )
2303 {
2304 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2305 m_gal->DrawLine( pts[0], pts[1] );
2306 m_gal->DrawLine( pts[1], pts[2] );
2307 m_gal->DrawLine( pts[2], pts[3] );
2308 m_gal->DrawLine( pts[3], pts[0] );
2309 m_gal->DrawLine( pts[0], pts[2] );
2310 m_gal->DrawLine( pts[1], pts[3] );
2311 }
2312 else if( outline_mode )
2313 {
2314 m_gal->DrawSegment( pts[0], pts[1], thickness );
2315 m_gal->DrawSegment( pts[1], pts[2], thickness );
2316 m_gal->DrawSegment( pts[2], pts[3], thickness );
2317 m_gal->DrawSegment( pts[3], pts[0], thickness );
2318 }
2319 else
2320 {
2321 m_gal->SetIsFill( true );
2322 m_gal->SetIsStroke( false );
2323
2324 if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
2325 {
2326 m_gal->DrawSegment( pts[0], pts[1], thickness );
2327 m_gal->DrawSegment( pts[1], pts[2], thickness );
2328 m_gal->DrawSegment( pts[2], pts[3], thickness );
2329 m_gal->DrawSegment( pts[3], pts[0], thickness );
2330 }
2331
2332 if( isSolidFill )
2333 {
2334 SHAPE_POLY_SET poly;
2335 poly.NewOutline();
2336
2337 for( const VECTOR2I& pt : pts )
2338 poly.Append( pt );
2339
2340 if( thickness < 0 )
2341 poly.Inflate( thickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS,
2342 m_maxError );
2343
2344 m_gal->DrawPolygon( poly );
2345 }
2346 }
2347 }
2348
2349 break;
2350 }
2351
2352 case SHAPE_T::ARC:
2353 {
2354 EDA_ANGLE startAngle;
2355 EDA_ANGLE endAngle;
2356 aShape->CalcArcAngles( startAngle, endAngle );
2357
2358 if( outline_mode )
2359 {
2360 m_gal->DrawArcSegment( aShape->GetCenter(), aShape->GetRadius(), startAngle,
2361 endAngle - startAngle, thickness, m_maxError );
2362 }
2363 else if( lineStyle == LINE_STYLE::SOLID )
2364 {
2365 m_gal->SetIsFill( true );
2366 m_gal->SetIsStroke( false );
2367
2368 m_gal->DrawArcSegment( aShape->GetCenter(), aShape->GetRadius(), startAngle,
2369 endAngle - startAngle, thickness, m_maxError );
2370 }
2371 break;
2372 }
2373
2374 case SHAPE_T::CIRCLE:
2375 if( outline_mode )
2376 {
2377 m_gal->DrawCircle( aShape->GetStart(), aShape->GetRadius() - thickness / 2 );
2378 m_gal->DrawCircle( aShape->GetStart(), aShape->GetRadius() + thickness / 2 );
2379 }
2380 else
2381 {
2382 m_gal->SetIsFill( aShape->IsSolidFill() );
2383 m_gal->SetIsStroke( lineStyle == LINE_STYLE::SOLID && thickness > 0 );
2384 m_gal->SetLineWidth( thickness );
2385
2386 int radius = aShape->GetRadius();
2387
2388 if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
2389 {
2390 m_gal->DrawCircle( aShape->GetStart(), radius );
2391 }
2392 else if( isSolidFill )
2393 {
2394 if( thickness < 0 )
2395 {
2396 radius += thickness / 2;
2397 radius = std::max( radius, 0 );
2398 }
2399
2400 m_gal->DrawCircle( aShape->GetStart(), radius );
2401 }
2402 }
2403 break;
2404
2405 case SHAPE_T::POLY:
2406 {
2407 SHAPE_POLY_SET& shape = const_cast<PCB_SHAPE*>( aShape )->GetPolyShape();
2408
2409 if( shape.OutlineCount() == 0 )
2410 break;
2411
2412 if( outline_mode )
2413 {
2414 for( int ii = 0; ii < shape.OutlineCount(); ++ii )
2415 m_gal->DrawSegmentChain( shape.Outline( ii ), thickness );
2416 }
2417 else
2418 {
2419 m_gal->SetIsFill( true );
2420 m_gal->SetIsStroke( false );
2421
2422 if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
2423 {
2424 for( int ii = 0; ii < shape.OutlineCount(); ++ii )
2425 m_gal->DrawSegmentChain( shape.Outline( ii ), thickness );
2426 }
2427
2428 if( isSolidFill )
2429 {
2430 if( thickness < 0 )
2431 {
2432 SHAPE_POLY_SET deflated_shape = shape;
2433 deflated_shape.Inflate( thickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS,
2434 m_maxError );
2435 m_gal->DrawPolygon( deflated_shape );
2436 }
2437 else
2438 {
2439 // On Opengl, a not convex filled polygon is usually drawn by using
2440 // triangles as primitives. CacheTriangulation() can create basic triangle
2441 // primitives to draw the polygon solid shape on Opengl. GLU tessellation
2442 // is much slower, so currently we are using our tessellation.
2443 if( m_gal->IsOpenGlEngine() && !shape.IsTriangulationUpToDate() )
2444 shape.CacheTriangulation( true );
2445
2446 m_gal->DrawPolygon( shape );
2447 }
2448 }
2449 }
2450
2451 break;
2452 }
2453
2454 case SHAPE_T::BEZIER:
2455 if( outline_mode )
2456 {
2457 std::vector<VECTOR2D> output;
2458 std::vector<VECTOR2D> pointCtrl;
2459
2460 pointCtrl.push_back( aShape->GetStart() );
2461 pointCtrl.push_back( aShape->GetBezierC1() );
2462 pointCtrl.push_back( aShape->GetBezierC2() );
2463 pointCtrl.push_back( aShape->GetEnd() );
2464
2465 BEZIER_POLY converter( pointCtrl );
2466 converter.GetPoly( output, m_maxError );
2467
2468 m_gal->DrawSegmentChain( aShape->GetBezierPoints(), thickness );
2469 }
2470 else
2471 {
2472 m_gal->SetIsFill( aShape->IsSolidFill() );
2473 m_gal->SetIsStroke( lineStyle == LINE_STYLE::SOLID && thickness > 0 );
2474 m_gal->SetLineWidth( thickness );
2475
2476 if( aShape->GetBezierPoints().size() > 2 )
2477 {
2478 m_gal->DrawPolygon( aShape->GetBezierPoints() );
2479 }
2480 else
2481 {
2482 m_gal->DrawCurve( VECTOR2D( aShape->GetStart() ),
2483 VECTOR2D( aShape->GetBezierC1() ),
2484 VECTOR2D( aShape->GetBezierC2() ),
2485 VECTOR2D( aShape->GetEnd() ), m_maxError );
2486 }
2487 }
2488
2489 break;
2490
2491 case SHAPE_T::ELLIPSE:
2492 {
2493 const VECTOR2D center( aShape->GetEllipseCenter() );
2494 const int majorR = aShape->GetEllipseMajorRadius();
2495 const int minorR = aShape->GetEllipseMinorRadius();
2496 const EDA_ANGLE& rot = aShape->GetEllipseRotation();
2497
2498 if( outline_mode )
2499 {
2500 m_gal->DrawEllipse( center, majorR - thickness / 2, minorR - thickness / 2, rot );
2501 m_gal->DrawEllipse( center, majorR + thickness / 2, minorR + thickness / 2, rot );
2502 }
2503 else
2504 {
2505 m_gal->SetIsFill( aShape->IsSolidFill() );
2506 m_gal->SetIsStroke( lineStyle == LINE_STYLE::SOLID && thickness > 0 );
2507 m_gal->SetLineWidth( thickness );
2508
2509 if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
2510 m_gal->DrawEllipse( center, majorR, minorR, rot );
2511 else if( isSolidFill )
2512 m_gal->DrawEllipse( center, majorR, minorR, rot );
2513 }
2514
2515 break;
2516 }
2517
2519 {
2520 const VECTOR2D center( aShape->GetEllipseCenter() );
2521 const int majorR = aShape->GetEllipseMajorRadius();
2522 const int minorR = aShape->GetEllipseMinorRadius();
2523 const EDA_ANGLE& rot = aShape->GetEllipseRotation();
2524 const EDA_ANGLE& start = aShape->GetEllipseStartAngle();
2525 const EDA_ANGLE& end = aShape->GetEllipseEndAngle();
2526
2527 if( outline_mode )
2528 {
2529 m_gal->DrawEllipseArc( center, majorR - thickness / 2, minorR - thickness / 2, rot, start, end );
2530 m_gal->DrawEllipseArc( center, majorR + thickness / 2, minorR + thickness / 2, rot, start, end );
2531 }
2532 else if( lineStyle == LINE_STYLE::SOLID )
2533 {
2534 // no interior fill for arcs
2535 m_gal->SetIsFill( false );
2536 m_gal->SetIsStroke( thickness > 0 );
2537 m_gal->SetLineWidth( thickness );
2538 m_gal->DrawEllipseArc( center, majorR, minorR, rot, start, end );
2539 }
2540
2541 break;
2542 }
2543
2544 case SHAPE_T::UNDEFINED:
2545 break;
2546 }
2547 }
2548
2549 if( lineStyle != LINE_STYLE::SOLID )
2550 {
2551 if( !outline_mode )
2552 {
2553 m_gal->SetIsFill( true );
2554 m_gal->SetIsStroke( false );
2555 }
2556
2557 std::vector<SHAPE*> shapes;
2558
2559 // For ellipses, use SHAPE_ELLIPSE directly so STROKE_PARAMS::Stroke can
2560 // distribute dashes uniformly by arc length instead of per-tessellation-segment.
2561 if( aShape->GetShape() == SHAPE_T::ELLIPSE )
2562 {
2563 shapes.push_back( new SHAPE_ELLIPSE( aShape->GetEllipseCenter(), aShape->GetEllipseMajorRadius(),
2564 aShape->GetEllipseMinorRadius(), aShape->GetEllipseRotation() ) );
2565 }
2566 else if( aShape->GetShape() == SHAPE_T::ELLIPSE_ARC )
2567 {
2568 shapes.push_back( new SHAPE_ELLIPSE( aShape->GetEllipseCenter(), aShape->GetEllipseMajorRadius(),
2569 aShape->GetEllipseMinorRadius(), aShape->GetEllipseRotation(),
2570 aShape->GetEllipseStartAngle(), aShape->GetEllipseEndAngle() ) );
2571 }
2572 else
2573 {
2574 shapes = aShape->MakeEffectiveShapes( true );
2575 }
2576
2577 for( SHAPE* shape : shapes )
2578 {
2579 STROKE_PARAMS::Stroke( shape, lineStyle, getLineThickness( aShape->GetWidth() ),
2581 [&]( const VECTOR2I& a, const VECTOR2I& b )
2582 {
2583 m_gal->DrawSegment( a, b, thickness );
2584 } );
2585 }
2586
2587 for( SHAPE* shape : shapes )
2588 delete shape;
2589 }
2590
2591 if( isHatchedFill )
2592 {
2593 aShape->UpdateHatching();
2594 m_gal->SetIsFill( false );
2595 m_gal->SetIsStroke( true );
2596 m_gal->SetLineWidth( aShape->GetHatchLineWidth() );
2597
2598 for( const SEG& seg : aShape->GetHatchLines() )
2599 m_gal->DrawLine( seg.A, seg.B );
2600 }
2601}
2602
2603
2604void PCB_PAINTER::strokeText( const wxString& aText, const VECTOR2I& aPosition,
2605 const TEXT_ATTRIBUTES& aAttrs, const KIFONT::METRICS& aFontMetrics )
2606{
2607 KIFONT::FONT* font = aAttrs.m_Font;
2608
2609 if( !font )
2610 font = KIFONT::FONT::GetFont( wxEmptyString, aAttrs.m_Bold, aAttrs.m_Italic );
2611
2612 m_gal->SetIsFill( font->IsOutline() );
2613 m_gal->SetIsStroke( font->IsStroke() );
2614
2615 VECTOR2I pos( aPosition );
2616 VECTOR2I fudge( KiROUND( 0.16 * aAttrs.m_StrokeWidth ), 0 );
2617
2618 RotatePoint( fudge, aAttrs.m_Angle );
2619
2620 if( ( aAttrs.m_Halign == GR_TEXT_H_ALIGN_LEFT && !aAttrs.m_Mirrored )
2621 || ( aAttrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT && aAttrs.m_Mirrored ) )
2622 {
2623 pos -= fudge;
2624 }
2625 else if( ( aAttrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT && !aAttrs.m_Mirrored )
2626 || ( aAttrs.m_Halign == GR_TEXT_H_ALIGN_LEFT && aAttrs.m_Mirrored ) )
2627 {
2628 pos += fudge;
2629 }
2630
2631 font->Draw( m_gal, aText, pos, aAttrs, aFontMetrics );
2632}
2633
2634
2635void PCB_PAINTER::draw( const PCB_REFERENCE_IMAGE* aBitmap, int aLayer )
2636{
2637 m_gal->Save();
2638
2639 const REFERENCE_IMAGE& refImg = aBitmap->GetReferenceImage();
2640 m_gal->Translate( refImg.GetPosition() );
2641
2642 // When the image scale factor is not 1.0, we need to modify the actual as the image scale
2643 // factor is similar to a local zoom
2644 const double img_scale = refImg.GetImageScale();
2645
2646 if( img_scale != 1.0 )
2647 m_gal->Scale( VECTOR2D( img_scale, img_scale ) );
2648
2649 const double imgAlpha = m_pcbSettings.GetColor( aBitmap, aBitmap->GetLayer() ).a;
2650
2651 if( aBitmap->IsSelected() || aBitmap->IsBrightened() )
2652 {
2653 COLOR4D color = m_pcbSettings.GetColor( aBitmap, LAYER_ANCHOR );
2654 m_gal->SetIsStroke( true );
2655 m_gal->SetStrokeColor( color );
2656 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth * 2.0f );
2657 m_gal->SetIsFill( false );
2658
2659 // Draws a bounding box.
2660 VECTOR2D bm_size( refImg.GetSize() );
2661 // bm_size is the actual image size in UI.
2662 // but m_canvas scale was previously set to img_scale
2663 // so recalculate size relative to this image size.
2664 bm_size.x /= img_scale;
2665 bm_size.y /= img_scale;
2666 VECTOR2D origin( -bm_size.x / 2.0, -bm_size.y / 2.0 );
2667 VECTOR2D end = origin + bm_size;
2668
2669 m_gal->DrawRectangle( origin, end );
2670
2671 // Keep reference images opaque when selected (and not moving). Otherwise cached layers
2672 // will not be rendered under the selected image because cached layers are rendered
2673 // after non-cached layers (e.g. bitmaps), which will have a closer Z order.
2674 m_gal->DrawBitmap( refImg.GetImage(), aBitmap->IsMoving() ? imgAlpha : 1.0 );
2675 }
2676 else
2677 m_gal->DrawBitmap( refImg.GetImage(), imgAlpha );
2678
2679 m_gal->Restore();
2680}
2681
2682
2683void PCB_PAINTER::draw( const PCB_FIELD* aField, int aLayer )
2684{
2685 if( aField->IsVisible() )
2686 draw( static_cast<const PCB_TEXT*>( aField ), aLayer );
2687}
2688
2689
2690void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer )
2691{
2692 wxString resolvedText( aText->GetShownText( true ) );
2693
2694 if( resolvedText.Length() == 0 )
2695 return;
2696
2697 if( aLayer == LAYER_LOCKED_ITEM_SHADOW ) // happens only if locked
2698 {
2699 const COLOR4D color = m_pcbSettings.GetColor( aText, aLayer );
2700
2701 m_gal->SetIsFill( true );
2702 m_gal->SetIsStroke( true );
2703 m_gal->SetFillColor( color );
2704 m_gal->SetStrokeColor( color );
2705 m_gal->SetLineWidth( m_lockedShadowMargin );
2706
2707 SHAPE_POLY_SET poly;
2708 aText->TransformShapeToPolygon( poly, aText->GetLayer(), 0, m_maxError, ERROR_OUTSIDE );
2709 m_gal->DrawPolygon( poly );
2710
2711 return;
2712 }
2713
2714 const KIFONT::METRICS& metrics = aText->GetFontMetrics();
2715 TEXT_ATTRIBUTES attrs = aText->GetAttributes();
2716 // Raw attrs are lib frame for FP children, pull scaled values for render.
2717 attrs.m_Size = aText->GetTextSize();
2718 attrs.m_StrokeWidth = aText->GetTextThickness();
2719 const COLOR4D& color = m_pcbSettings.GetColor( aText, aLayer );
2720 bool outline_mode = !viewer_settings()->m_ViewersDisplay.m_DisplayTextFill;
2721
2722 KIFONT::FONT* font = aText->GetDrawFont( &m_pcbSettings );
2723
2724 m_gal->SetStrokeColor( color );
2725 m_gal->SetFillColor( color );
2726 attrs.m_Angle = aText->GetDrawRotation();
2727
2728 if( aText->IsKnockout() )
2729 {
2730 const SHAPE_POLY_SET& finalPoly = aText->GetKnockoutCache( font, resolvedText, m_maxError );
2731
2732 m_gal->SetIsStroke( false );
2733 m_gal->SetIsFill( true );
2734 m_gal->DrawPolygon( finalPoly );
2735 }
2736 else
2737 {
2738 if( outline_mode )
2739 attrs.m_StrokeWidth = m_pcbSettings.m_outlineWidth;
2740 else
2742
2743 if( m_gal->IsFlippedX() && !aText->IsSideSpecific() )
2744 {
2745 // We do not want to change the mirroring for this kind of text
2746 // on the mirrored canvas
2747 // (not mirrored is draw not mirrored and mirrored is draw mirrored)
2748 // So we need to recalculate the text position to keep it at the same position
2749 // on the canvas
2750 VECTOR2I textPos = aText->GetTextPos();
2751 VECTOR2I textWidth = VECTOR2I( aText->GetTextBox( &m_pcbSettings ).GetWidth(), 0 );
2752
2753 if( aText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
2754 textWidth.x = -textWidth.x;
2755 else if( aText->GetHorizJustify() == GR_TEXT_H_ALIGN_CENTER )
2756 textWidth.x = 0;
2757
2758 RotatePoint( textWidth, VECTOR2I( 0, 0 ), aText->GetDrawRotation() );
2759
2760 if( attrs.m_Mirrored )
2761 textPos -= textWidth;
2762 else
2763 textPos += textWidth;
2764
2765 attrs.m_Mirrored = !attrs.m_Mirrored;
2766 strokeText( resolvedText, textPos, attrs, metrics );
2767 return;
2768 }
2769
2770 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
2771
2772 if( font->IsOutline() )
2773 cache = aText->GetRenderCache( font, resolvedText );
2774
2775 if( cache )
2776 {
2777 m_gal->SetLineWidth( attrs.m_StrokeWidth );
2778 m_gal->DrawGlyphs( *cache );
2779 }
2780 else
2781 {
2782 strokeText( resolvedText, aText->GetTextPos(), attrs, metrics );
2783 }
2784 }
2785
2786 // Draw the umbilical line for texts in footprints
2787 FOOTPRINT* fp_parent = aText->GetParentFootprint();
2788
2789 if( fp_parent && aText->IsSelected() )
2790 {
2791 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2792 m_gal->SetStrokeColor( m_pcbSettings.GetColor( nullptr, LAYER_ANCHOR ) );
2793 m_gal->DrawLine( aText->GetTextPos(), fp_parent->GetPosition() );
2794 }
2795}
2796
2797
2798void PCB_PAINTER::draw( const PCB_TEXTBOX* aTextBox, int aLayer )
2799{
2800 if( aTextBox->Type() == PCB_TABLECELL_T )
2801 {
2802 const PCB_TABLECELL* cell = static_cast<const PCB_TABLECELL*>( aTextBox );
2803
2804 if( cell->GetColSpan() == 0 || cell->GetRowSpan() == 0 )
2805 return;
2806 }
2807
2808 COLOR4D color = m_pcbSettings.GetColor( aTextBox, aLayer );
2809 int thickness = getLineThickness( aTextBox->GetWidth() );
2810 LINE_STYLE lineStyle = aTextBox->GetStroke().GetLineStyle();
2811 wxString resolvedText( aTextBox->GetShownText( true ) );
2812 KIFONT::FONT* font = aTextBox->GetDrawFont( &m_pcbSettings );
2813
2814 if( aLayer == LAYER_LOCKED_ITEM_SHADOW ) // happens only if locked
2815 {
2816 const COLOR4D sh_color = m_pcbSettings.GetColor( aTextBox, aLayer );
2817
2818 m_gal->SetIsFill( true );
2819 m_gal->SetIsStroke( false );
2820 m_gal->SetFillColor( sh_color );
2821 m_gal->SetStrokeColor( sh_color );
2822
2823 // Draw the box with a larger thickness than box thickness to show
2824 // the shadow mask
2825 std::vector<VECTOR2I> pts = aTextBox->GetCorners();
2826 int line_thickness = std::max( thickness*3, pcbIUScale.mmToIU( 0.2 ) );
2827
2828 std::deque<VECTOR2D> dpts;
2829
2830 for( const VECTOR2I& pt : pts )
2831 dpts.push_back( VECTOR2D( pt ) );
2832
2833 dpts.push_back( VECTOR2D( pts[0] ) );
2834
2835 m_gal->SetIsStroke( true );
2836 m_gal->SetLineWidth( line_thickness );
2837 m_gal->DrawPolygon( dpts );
2838 }
2839
2840 m_gal->SetFillColor( color );
2841 m_gal->SetStrokeColor( color );
2842 m_gal->SetIsFill( true );
2843 m_gal->SetIsStroke( false );
2844
2845 if( aTextBox->Type() != PCB_TABLECELL_T && aTextBox->IsBorderEnabled() )
2846 {
2847 if( lineStyle <= LINE_STYLE::FIRST_TYPE )
2848 {
2849 if( thickness > 0 )
2850 {
2851 std::vector<VECTOR2I> pts = aTextBox->GetCorners();
2852
2853 for( size_t ii = 0; ii < pts.size(); ++ii )
2854 m_gal->DrawSegment( pts[ii], pts[( ii + 1 ) % pts.size()], thickness );
2855 }
2856 }
2857 else
2858 {
2859 std::vector<SHAPE*> shapes = aTextBox->MakeEffectiveShapes( true );
2860
2861 for( SHAPE* shape : shapes )
2862 {
2863 STROKE_PARAMS::Stroke( shape, lineStyle, thickness, &m_pcbSettings,
2864 [&]( const VECTOR2I& a, const VECTOR2I& b )
2865 {
2866 m_gal->DrawSegment( a, b, thickness );
2867 } );
2868 }
2869
2870 for( SHAPE* shape : shapes )
2871 delete shape;
2872 }
2873 }
2874
2875 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
2876 {
2877 // For now, the textbox is a filled shape.
2878 // so the text drawn on LAYER_LOCKED_ITEM_SHADOW with a thick width is disabled
2879 // If enabled, the thick text position must be offsetted to be exactly on the
2880 // initial text, which is not easy, depending on its rotation and justification.
2881#if 0
2882 const COLOR4D sh_color = m_pcbSettings.GetColor( aTextBox, aLayer );
2883 m_canvas->SetFillColor( sh_color );
2884 m_canvas->SetStrokeColor( sh_color );
2885 attrs.m_StrokeWidth += m_lockedShadowMargin;
2886#else
2887 return;
2888#endif
2889 }
2890
2891 if( aTextBox->IsKnockout() )
2892 {
2893 SHAPE_POLY_SET finalPoly;
2894 aTextBox->TransformTextToPolySet( finalPoly, 0, m_maxError, ERROR_INSIDE );
2895 finalPoly.Fracture();
2896
2897 m_gal->SetIsStroke( false );
2898 m_gal->SetIsFill( true );
2899 m_gal->DrawPolygon( finalPoly );
2900 }
2901 else
2902 {
2903 if( resolvedText.Length() == 0 )
2904 return;
2905
2906 const KIFONT::METRICS& metrics = aTextBox->GetFontMetrics();
2907 TEXT_ATTRIBUTES attrs = aTextBox->GetAttributes();
2908 // Raw attrs are lib frame for FP children, pull scaled size for render.
2909 attrs.m_Size = aTextBox->GetTextSize();
2911
2912 if( m_gal->IsFlippedX() && !aTextBox->IsSideSpecific() )
2913 {
2914 attrs.m_Mirrored = !attrs.m_Mirrored;
2915 strokeText( resolvedText, aTextBox->GetDrawPos( true ), attrs, metrics );
2916 return;
2917 }
2918
2919 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
2920
2921 if( font->IsOutline() )
2922 cache = aTextBox->GetRenderCache( font, resolvedText );
2923
2924 if( cache )
2925 {
2926 m_gal->SetLineWidth( attrs.m_StrokeWidth );
2927 m_gal->DrawGlyphs( *cache );
2928 }
2929 else
2930 {
2931 strokeText( resolvedText, aTextBox->GetDrawPos(), attrs, metrics );
2932 }
2933 }
2934}
2935
2936void PCB_PAINTER::draw( const PCB_TABLE* aTable, int aLayer )
2937{
2938 if( aTable->GetCells().empty() )
2939 return;
2940
2941 for( PCB_TABLECELL* cell : aTable->GetCells() )
2942 {
2943 if( cell->GetColSpan() > 0 || cell->GetRowSpan() > 0 )
2944 draw( static_cast<PCB_TEXTBOX*>( cell ), aLayer );
2945 }
2946
2947 COLOR4D color = m_pcbSettings.GetColor( aTable, aLayer );
2948
2949 aTable->DrawBorders(
2950 [&]( const VECTOR2I& ptA, const VECTOR2I& ptB, const STROKE_PARAMS& stroke )
2951 {
2952 int lineWidth = getLineThickness( stroke.GetWidth() );
2953 LINE_STYLE lineStyle = stroke.GetLineStyle();
2954
2955 m_gal->SetIsFill( false );
2956 m_gal->SetIsStroke( true );
2957 m_gal->SetStrokeColor( color );
2958 m_gal->SetLineWidth( lineWidth );
2959
2960 if( lineStyle <= LINE_STYLE::FIRST_TYPE )
2961 {
2962 m_gal->DrawLine( ptA, ptB );
2963 }
2964 else
2965 {
2966 SHAPE_SEGMENT seg( ptA, ptB );
2967
2968 STROKE_PARAMS::Stroke( &seg, lineStyle, lineWidth, &m_pcbSettings,
2969 [&]( VECTOR2I a, VECTOR2I b )
2970 {
2971 // DrawLine has problem with 0 length lines so enforce minimum
2972 if( a == b )
2973 m_gal->DrawLine( a+1, b );
2974 else
2975 m_gal->DrawLine( a, b );
2976 } );
2977 }
2978 } );
2979
2980 // Highlight selected tablecells with a background wash.
2981 for( PCB_TABLECELL* cell : aTable->GetCells() )
2982 {
2983 if( aTable->IsSelected() || cell->IsSelected() )
2984 {
2985 std::vector<VECTOR2I> corners = cell->GetCorners();
2986 std::deque<VECTOR2D> pts;
2987
2988 pts.insert( pts.end(), corners.begin(), corners.end() );
2989
2990 m_gal->SetFillColor( color.WithAlpha( 0.5 ) );
2991 m_gal->SetIsFill( true );
2992 m_gal->SetIsStroke( false );
2993 m_gal->DrawPolygon( pts );
2994 }
2995 }
2996}
2997
2998
2999void PCB_PAINTER::draw( const FOOTPRINT* aFootprint, int aLayer )
3000{
3001 if( aLayer == LAYER_ANCHOR )
3002 {
3003 const COLOR4D color = m_pcbSettings.GetColor( aFootprint, aLayer );
3004
3005 // Keep the size and width constant, not related to the scale because the anchor
3006 // is just a marker on screen
3007 double anchorSize = 5.0 / m_gal->GetWorldScale(); // 5 pixels size
3008 double anchorThickness = 1.0 / m_gal->GetWorldScale(); // 1 pixels width
3009
3010 // Draw anchor
3011 m_gal->SetIsFill( false );
3012 m_gal->SetIsStroke( true );
3013 m_gal->SetStrokeColor( color );
3014 m_gal->SetLineWidth( anchorThickness );
3015
3016 VECTOR2D center = aFootprint->GetPosition();
3017 m_gal->DrawLine( center - VECTOR2D( anchorSize, 0 ), center + VECTOR2D( anchorSize, 0 ) );
3018 m_gal->DrawLine( center - VECTOR2D( 0, anchorSize ), center + VECTOR2D( 0, anchorSize ) );
3019 }
3020
3021 if( aLayer == LAYER_LOCKED_ITEM_SHADOW && m_frameType == FRAME_PCB_EDITOR ) // happens only if locked
3022 {
3023 const COLOR4D color = m_pcbSettings.GetColor( aFootprint, aLayer );
3024
3025 m_gal->SetIsFill( true );
3026 m_gal->SetIsStroke( false );
3027 m_gal->SetFillColor( color );
3028
3029#if 0 // GetBoundingHull() can be very slow, especially for logos imported from graphics
3030 const SHAPE_POLY_SET& poly = aFootprint->GetBoundingHull();
3031 m_canvas->DrawPolygon( poly );
3032#else
3033 BOX2I bbox = aFootprint->GetBoundingBox( false );
3034 VECTOR2I topLeft = bbox.GetPosition();
3035 VECTOR2I botRight = bbox.GetPosition() + bbox.GetSize();
3036
3037 m_gal->DrawRectangle( topLeft, botRight );
3038
3039 // Use segments to produce a margin with rounded corners
3040 m_gal->DrawSegment( topLeft, VECTOR2I( botRight.x, topLeft.y ), m_lockedShadowMargin );
3041 m_gal->DrawSegment( VECTOR2I( botRight.x, topLeft.y ), botRight, m_lockedShadowMargin );
3042 m_gal->DrawSegment( botRight, VECTOR2I( topLeft.x, botRight.y ), m_lockedShadowMargin );
3043 m_gal->DrawSegment( VECTOR2I( topLeft.x, botRight.y ), topLeft, m_lockedShadowMargin );
3044#endif
3045 }
3046
3047 if( aLayer == LAYER_CONFLICTS_SHADOW && aFootprint->IsConflicting() )
3048 {
3049 const SHAPE_POLY_SET& frontpoly = aFootprint->GetCourtyard( F_CrtYd );
3050 const SHAPE_POLY_SET& backpoly = aFootprint->GetCourtyard( B_CrtYd );
3051
3052 const COLOR4D color = m_pcbSettings.GetColor( aFootprint, aLayer );
3053
3054 m_gal->SetIsFill( true );
3055 m_gal->SetIsStroke( false );
3056 m_gal->SetFillColor( color );
3057
3058 if( frontpoly.OutlineCount() > 0 )
3059 m_gal->DrawPolygon( frontpoly );
3060
3061 if( backpoly.OutlineCount() > 0 )
3062 m_gal->DrawPolygon( backpoly );
3063 }
3064}
3065
3066
3067void PCB_PAINTER::draw( const PCB_GROUP* aGroup, int aLayer )
3068{
3069 if( aLayer == LAYER_ANCHOR )
3070 {
3071 if( aGroup->IsSelected() && !( aGroup->GetParent() && aGroup->GetParent()->IsSelected() ) )
3072 {
3073 // Selected on our own; draw enclosing box
3074 }
3075 else if( aGroup->IsEntered() )
3076 {
3077 // Entered group; draw enclosing box
3078 }
3079 else
3080 {
3081 // Neither selected nor entered; draw nothing at the group level (ie: only draw
3082 // its members)
3083 return;
3084 }
3085
3086 const COLOR4D color = m_pcbSettings.GetColor( aGroup, LAYER_ANCHOR );
3087
3088 m_gal->SetStrokeColor( color );
3089 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth * 2.0f );
3090
3091 BOX2I bbox = aGroup->GetBoundingBox();
3092 VECTOR2I topLeft = bbox.GetPosition();
3093 VECTOR2I width = VECTOR2I( bbox.GetWidth(), 0 );
3094 VECTOR2I height = VECTOR2I( 0, bbox.GetHeight() );
3095
3096 m_gal->DrawLine( topLeft, topLeft + width );
3097 m_gal->DrawLine( topLeft + width, topLeft + width + height );
3098 m_gal->DrawLine( topLeft + width + height, topLeft + height );
3099 m_gal->DrawLine( topLeft + height, topLeft );
3100
3101 wxString name = aGroup->GetName();
3102
3103 if( name.IsEmpty() )
3104 return;
3105
3106 int ptSize = 12;
3107 int scaledSize = abs( KiROUND( m_gal->GetScreenWorldMatrix().GetScale().x * ptSize ) );
3108 int unscaledSize = pcbIUScale.MilsToIU( ptSize );
3109
3110 // Scale by zoom a bit, but not too much
3111 int textSize = ( scaledSize + ( unscaledSize * 2 ) ) / 3;
3112 VECTOR2I textOffset = KiROUND( width.x / 2.0, -textSize * 0.5 );
3113 VECTOR2I titleHeight = KiROUND( 0.0, textSize * 2.0 );
3114
3115 if( PrintableCharCount( name ) * textSize < bbox.GetWidth() )
3116 {
3117 m_gal->DrawLine( topLeft, topLeft - titleHeight );
3118 m_gal->DrawLine( topLeft - titleHeight, topLeft + width - titleHeight );
3119 m_gal->DrawLine( topLeft + width - titleHeight, topLeft + width );
3120
3121 TEXT_ATTRIBUTES attrs;
3122 attrs.m_Italic = true;
3125 attrs.m_Size = VECTOR2I( textSize, textSize );
3126 attrs.m_StrokeWidth = GetPenSizeForNormal( textSize );
3127
3128 KIFONT::FONT::GetFont()->Draw( m_gal, aGroup->GetName(), topLeft + textOffset, attrs,
3129 aGroup->GetFontMetrics() );
3130 }
3131 }
3132}
3133
3134
3135bool KIGFX::ZoneOutlineDrawnOnLayer( bool aOutlineOnly, int aLayer )
3136{
3137 if( aOutlineOnly )
3138 return IsZoneFillLayer( aLayer );
3139
3140 return !IsZoneFillLayer( aLayer );
3141}
3142
3143
3144void PCB_PAINTER::draw( const ZONE* aZone, int aLayer )
3145{
3146 if( aLayer == LAYER_CONFLICTS_SHADOW )
3147 {
3148 if( aZone->IsConflicting() && aZone->GetIsRuleArea() )
3149 {
3150 COLOR4D color = m_pcbSettings.GetColor( aZone, aLayer );
3151
3152 m_gal->SetIsFill( true );
3153 m_gal->SetIsStroke( false );
3154 m_gal->SetFillColor( color );
3155
3156 m_gal->DrawPolygon( aZone->GetBoardOutline().Outline( 0 ) );
3157 }
3158
3159 return;
3160 }
3161
3162 /*
3163 * aLayer will be the virtual zone layer (LAYER_ZONE_START, ... in GAL_LAYER_ID)
3164 * This is used for draw ordering in the GAL.
3165 * The color for the zone comes from the associated copper layer ( aLayer - LAYER_ZONE_START )
3166 * and the visibility comes from the combination of that copper layer and LAYER_ZONES
3167 */
3168 PCB_LAYER_ID layer;
3169
3170 if( IsZoneFillLayer( aLayer ) )
3171 layer = ToLAYER_ID( aLayer - LAYER_ZONE_START );
3172 else
3173 layer = ToLAYER_ID( aLayer );
3174
3175 if( !aZone->IsOnLayer( layer ) )
3176 return;
3177
3178 COLOR4D color = m_pcbSettings.GetColor( aZone, layer );
3179 std::deque<VECTOR2D> corners;
3180 ZONE_DISPLAY_MODE displayMode = m_pcbSettings.m_ZoneDisplayMode;
3181
3182 if( aZone->IsTeardropArea() )
3183 displayMode = ZONE_DISPLAY_MODE::SHOW_FILLED;
3184
3185 // A zone whose only visual is its outline (rule area, or outline-only display) draws it on
3186 // the zone layer, above copper, so tracks and pads can't paint over it.
3187 bool outlineOnly = aZone->GetIsRuleArea() || displayMode == ZONE_DISPLAY_MODE::SHOW_ZONE_OUTLINE;
3188
3189 // Draw the outline
3190 if( ZoneOutlineDrawnOnLayer( outlineOnly, aLayer ) )
3191 {
3192 const SHAPE_POLY_SET boardOutline = aZone->GetBoardOutline();
3193 const SHAPE_POLY_SET* outline = &boardOutline;
3194 bool allowDrawOutline = aZone->GetHatchStyle() != ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER;
3195
3196 if( allowDrawOutline && !m_pcbSettings.m_isPrinting && outline && outline->OutlineCount() > 0 )
3197 {
3198 m_gal->SetStrokeColor( color.a > 0.0 ? color.WithAlpha( 1.0 ) : color );
3199 m_gal->SetIsFill( false );
3200 m_gal->SetIsStroke( true );
3201 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
3202
3203 // Draw each contour (main contour and holes)
3204
3205 /*
3206 * m_canvas->DrawPolygon( *outline );
3207 * should be enough, but currently does not work to draw holes contours in a complex
3208 * polygon so each contour is draw as a simple polygon
3209 */
3210
3211 // Draw the main contour(s?)
3212 for( int ii = 0; ii < outline->OutlineCount(); ++ii )
3213 {
3214 m_gal->DrawPolyline( outline->COutline( ii ) );
3215
3216 // Draw holes
3217 int holes_count = outline->HoleCount( ii );
3218
3219 for( int jj = 0; jj < holes_count; ++jj )
3220 m_gal->DrawPolyline( outline->CHole( ii, jj ) );
3221 }
3222
3223 // Draw hatch lines
3224 for( const SEG& hatchLine : aZone->GetHatchLines() )
3225 m_gal->DrawLine( hatchLine.A, hatchLine.B );
3226 }
3227 }
3228
3229 // Draw the filling
3230 if( IsZoneFillLayer( aLayer )
3231 && ( displayMode == ZONE_DISPLAY_MODE::SHOW_FILLED
3233 || displayMode == ZONE_DISPLAY_MODE::SHOW_TRIANGULATION ) )
3234 {
3235 const std::shared_ptr<SHAPE_POLY_SET>& polySet = aZone->GetFilledPolysList( layer );
3236
3237 if( polySet->OutlineCount() == 0 ) // Nothing to draw
3238 return;
3239
3240 m_gal->SetStrokeColor( color );
3241 m_gal->SetFillColor( color );
3242 m_gal->SetLineWidth( 0 );
3243
3244 if( displayMode == ZONE_DISPLAY_MODE::SHOW_FILLED )
3245 {
3246 m_gal->SetIsFill( true );
3247 m_gal->SetIsStroke( false );
3248 }
3249 else
3250 {
3251 m_gal->SetIsFill( false );
3252 m_gal->SetIsStroke( true );
3253 }
3254
3255 // On Opengl, a not convex filled polygon is usually drawn by using triangles
3256 // as primitives. CacheTriangulation() can create basic triangle primitives to
3257 // draw the polygon solid shape on Opengl. GLU tessellation is much slower,
3258 // so currently we are using our tessellation.
3259 if( m_gal->IsOpenGlEngine() && !polySet->IsTriangulationUpToDate() )
3260 polySet->CacheTriangulation( true );
3261
3262 m_gal->DrawPolygon( *polySet, displayMode == ZONE_DISPLAY_MODE::SHOW_TRIANGULATION );
3263 }
3264}
3265
3266
3267void PCB_PAINTER::draw( const PCB_BARCODE* aBarcode, int aLayer )
3268{
3269 const COLOR4D& color = m_pcbSettings.GetColor( aBarcode, aLayer );
3270
3271 m_gal->SetIsFill( true );
3272 m_gal->SetIsStroke( false );
3273 m_gal->SetFillColor( color );
3274
3275 // Draw the barcode
3276 SHAPE_POLY_SET shape;
3277
3278 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
3279 aBarcode->GetBoundingHull( shape, aBarcode->GetLayer(), m_lockedShadowMargin, m_maxError, ERROR_INSIDE );
3280 else
3281 aBarcode->TransformShapeToPolySet( shape, aBarcode->GetLayer(), 0, m_maxError, ERROR_INSIDE );
3282
3283 if( shape.OutlineCount() != 0 )
3284 m_gal->DrawPolygon( shape );
3285}
3286
3287
3288void PCB_PAINTER::draw( const PCB_DIMENSION_BASE* aDimension, int aLayer )
3289{
3290 COLOR4D color = m_pcbSettings.GetColor( aDimension, aLayer );
3291
3292 if( aLayer == LAYER_LOCKED_ITEM_SHADOW || aLayer == LAYER_CONSTRAINT_SHADOW )
3293 {
3294 int margin = m_lockedShadowMargin;
3295
3296 if( aLayer == LAYER_CONSTRAINT_SHADOW
3297 && m_pcbSettings.GetHighlightedConstraintMembers().count( aDimension->m_Uuid ) )
3298 {
3299 color = color.Brightened( 0.5 );
3300 margin *= 2;
3301 }
3302
3303 m_gal->SetIsFill( true );
3304 m_gal->SetIsStroke( true );
3305 m_gal->SetFillColor( color );
3306 m_gal->SetStrokeColor( color );
3307 m_gal->SetLineWidth( margin );
3308
3309 for( const std::shared_ptr<SHAPE>& shape : aDimension->GetShapes() )
3310 {
3311 switch( shape->Type() )
3312 {
3313 case SH_SEGMENT:
3314 {
3315 const SEG& seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg();
3316 m_gal->DrawSegment( seg.A, seg.B, margin );
3317 break;
3318 }
3319
3320 case SH_CIRCLE:
3321 {
3322 int radius = static_cast<const SHAPE_CIRCLE*>( shape.get() )->GetRadius();
3323 m_gal->DrawCircle( shape->Centre(), radius );
3324 break;
3325 }
3326
3327 default: break;
3328 }
3329 }
3330
3331 SHAPE_POLY_SET poly;
3332 aDimension->PCB_TEXT::TransformShapeToPolygon( poly, aDimension->GetLayer(), 0, m_maxError, ERROR_OUTSIDE );
3333 m_gal->DrawPolygon( poly );
3334
3335 return;
3336 }
3337
3338 m_gal->SetStrokeColor( color );
3339 m_gal->SetFillColor( color );
3340 m_gal->SetIsFill( false );
3341 m_gal->SetIsStroke( true );
3342
3344
3345 if( outline_mode )
3346 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
3347 else
3348 m_gal->SetLineWidth( getLineThickness( aDimension->GetLineThickness() ) );
3349
3350 // Draw dimension shapes
3351 // TODO(JE) lift this out
3352 for( const std::shared_ptr<SHAPE>& shape : aDimension->GetShapes() )
3353 {
3354 switch( shape->Type() )
3355 {
3356 case SH_SEGMENT:
3357 {
3358 const SEG& seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg();
3359 m_gal->DrawLine( seg.A, seg.B );
3360 break;
3361 }
3362
3363 case SH_CIRCLE:
3364 {
3365 int radius = static_cast<const SHAPE_CIRCLE*>( shape.get() )->GetRadius();
3366 m_gal->DrawCircle( shape->Centre(), radius );
3367 break;
3368 }
3369
3370 default:
3371 break;
3372 }
3373 }
3374
3375 // Draw text
3376 wxString resolvedText = aDimension->GetShownText( true );
3377 TEXT_ATTRIBUTES attrs = aDimension->GetAttributes();
3378
3379 if( m_gal->IsFlippedX() && !aDimension->IsSideSpecific() )
3380 attrs.m_Mirrored = !attrs.m_Mirrored;
3381
3382 if( outline_mode )
3383 attrs.m_StrokeWidth = m_pcbSettings.m_outlineWidth;
3384 else
3386
3387 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
3388
3389 if( aDimension->GetFont() && aDimension->GetFont()->IsOutline() )
3390 cache = aDimension->GetRenderCache( aDimension->GetFont(), resolvedText );
3391
3392 if( cache )
3393 {
3394 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : *cache )
3395 m_gal->DrawGlyph( *glyph.get() );
3396 }
3397 else
3398 {
3399 strokeText( resolvedText, aDimension->GetTextPos(), attrs, aDimension->GetFontMetrics() );
3400 }
3401}
3402
3403
3404void PCB_PAINTER::draw( const PCB_TARGET* aTarget )
3405{
3406 const COLOR4D strokeColor = m_pcbSettings.GetColor( aTarget, aTarget->GetLayer() );
3407 VECTOR2D position( aTarget->GetPosition() );
3408 double size, radius;
3409
3410 m_gal->SetLineWidth( getLineThickness( aTarget->GetWidth() ) );
3411 m_gal->SetStrokeColor( strokeColor );
3412 m_gal->SetIsFill( false );
3413 m_gal->SetIsStroke( true );
3414
3415 m_gal->Save();
3416 m_gal->Translate( position );
3417
3418 if( aTarget->GetShape() )
3419 {
3420 // shape x
3421 m_gal->Rotate( M_PI / 4.0 );
3422 size = 2.0 * aTarget->GetSize() / 3.0;
3423 radius = aTarget->GetSize() / 2.0;
3424 }
3425 else
3426 {
3427 // shape +
3428 size = aTarget->GetSize() / 2.0;
3429 radius = aTarget->GetSize() / 3.0;
3430 }
3431
3432 m_gal->DrawLine( VECTOR2D( -size, 0.0 ), VECTOR2D( size, 0.0 ) );
3433 m_gal->DrawLine( VECTOR2D( 0.0, -size ), VECTOR2D( 0.0, size ) );
3434 m_gal->DrawCircle( VECTOR2D( 0.0, 0.0 ), radius );
3435
3436 m_gal->Restore();
3437}
3438
3439
3440void PCB_PAINTER::draw( const PCB_POINT* aPoint, int aLayer )
3441{
3442 // aLayer will be the virtual point layer (LAYER_POINT_START, ... in GAL_LAYER_ID).
3443 // This is used for draw ordering in the GAL.
3444 // The cross color comes from LAYER_POINTS and the ring color follows the point's board layer.
3445 // Visibility comes from the combination of that board layer and LAYER_POINTS.
3446
3447 double size = (double)aPoint->GetSize() / 2;
3448
3449 // Keep the width constant, not related to the scale because the anchor
3450 // is just a marker on screen, just draw in pixels
3451 double thickness = m_pcbSettings.m_outlineWidth;
3452
3453 // The general "points" colour
3454 COLOR4D crossColor = m_pcbSettings.GetColor( aPoint, LAYER_POINTS );
3455 // The colour for the ring around the point follows the "real" layer of the point
3456 COLOR4D ringColor = m_pcbSettings.GetColor( aPoint, aPoint->GetLayer() );
3457
3458 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
3459 {
3460 thickness += m_lockedShadowMargin;
3461 crossColor = m_pcbSettings.GetColor( aPoint, aLayer );
3462 ringColor = m_pcbSettings.GetColor( aPoint, aLayer );
3463 }
3464
3465 VECTOR2D position( aPoint->GetPosition() );
3466
3467 m_gal->SetLineWidth( thickness );
3468 m_gal->SetStrokeColor( crossColor );
3469 m_gal->SetIsFill( false );
3470 m_gal->SetIsStroke( true );
3471
3472 m_gal->Save();
3473 m_gal->Translate( position );
3474
3475 // Draw as X to make it clearer when overlaid on cursor or axes
3476 m_gal->DrawLine( VECTOR2D( -size, -size ), VECTOR2D( size, size ) );
3477 m_gal->DrawLine( VECTOR2D( size, -size ), VECTOR2D( -size, size ) );
3478
3479 // Draw the circle in the layer colour
3480 m_gal->SetStrokeColor( ringColor );
3481 m_gal->DrawCircle( VECTOR2D( 0.0, 0.0 ), size / 2 );
3482
3483 m_gal->Restore();
3484}
3485
3486
3487void PCB_PAINTER::draw( const PCB_MARKER* aMarker, int aLayer )
3488{
3489 // Don't paint invisible markers.
3490 // It would be nice to do this through layer dependencies but we can't do an "or" there today
3491 if( aMarker->GetBoard() && !aMarker->GetBoard()->IsElementVisible( aMarker->GetColorLayer() ) )
3492 return;
3493
3494 COLOR4D color = m_pcbSettings.GetColor( aMarker, aMarker->GetColorLayer() );
3495
3496 aMarker->SetZoom( 1.0 / sqrt( m_gal->GetZoomFactor() ) );
3497
3498 switch( aLayer )
3499 {
3501 case LAYER_DRC_ERROR:
3502 case LAYER_DRC_WARNING:
3505 {
3506 // The active marker is redrawn on LAYER_DRC_HIGHLIGHTED so it lands on top of any
3507 // neighbouring inactive markers
3508 if( aLayer == LAYER_DRC_HIGHLIGHTED && !aMarker->IsBrightened() && !aMarker->IsSelected() )
3509 return;
3510
3511 bool isShadow = aLayer == LAYER_MARKER_SHADOWS;
3512
3513 SHAPE_LINE_CHAIN polygon;
3514 aMarker->ShapeToPolygon( polygon );
3515
3516 m_gal->Save();
3517 m_gal->Translate( aMarker->GetPosition() );
3518
3519 if( isShadow )
3520 {
3521 m_gal->SetStrokeColor( m_pcbSettings.GetColor( aMarker, LAYER_MARKER_SHADOWS ) );
3522 m_gal->SetIsStroke( true );
3523 m_gal->SetLineWidth( (float) aMarker->MarkerScale() );
3524 }
3525 else
3526 {
3527 m_gal->SetFillColor( color );
3528 m_gal->SetIsFill( true );
3529 }
3530
3531 m_gal->DrawPolygon( polygon );
3532 m_gal->Restore();
3533 break;
3534 }
3535
3536 case LAYER_DRC_SHAPES:
3537 if( !aMarker->IsBrightened() && !aMarker->IsSelected() )
3538 return;
3539
3540 for( const PCB_SHAPE& shape : aMarker->GetShapes() )
3541 {
3542 if( shape.GetStroke().GetWidth() == 1.0 )
3543 {
3544 m_gal->SetIsFill( false );
3545 m_gal->SetIsStroke( true );
3546 m_gal->SetStrokeColor( color );
3547 m_gal->SetLineWidth( KiROUND( aMarker->MarkerScale() / 2.0 ) );
3548
3549 if( shape.GetShape() == SHAPE_T::SEGMENT )
3550 {
3551 m_gal->DrawLine( shape.GetStart(), shape.GetEnd() );
3552 }
3553 else if( shape.GetShape() == SHAPE_T::ARC )
3554 {
3555 EDA_ANGLE startAngle, endAngle;
3556 shape.CalcArcAngles( startAngle, endAngle );
3557
3558 m_gal->DrawArc( shape.GetCenter(), shape.GetRadius(), startAngle, shape.GetArcAngle() );
3559 }
3560 }
3561 else
3562 {
3563 m_gal->SetIsFill( true );
3564 m_gal->SetIsStroke( false );
3565 m_gal->SetFillColor( color.WithAlpha( 0.5 ) );
3566
3567 if( shape.GetShape() == SHAPE_T::SEGMENT )
3568 {
3569 m_gal->DrawSegment( shape.GetStart(), shape.GetEnd(), shape.GetWidth() );
3570 }
3571 else if( shape.GetShape() == SHAPE_T::ARC )
3572 {
3573 EDA_ANGLE startAngle, endAngle;
3574 shape.CalcArcAngles( startAngle, endAngle );
3575
3576 m_gal->DrawArcSegment( shape.GetCenter(), shape.GetRadius(), startAngle, shape.GetArcAngle(),
3577 shape.GetWidth(), ARC_HIGH_DEF );
3578 }
3579 }
3580 }
3581
3582 break;
3583 }
3584}
3585
3586
3587void PCB_PAINTER::draw( const PCB_BOARD_OUTLINE* aBoardOutline, int aLayer )
3588{
3589 if( !aBoardOutline->HasOutline() )
3590 return;
3591
3592 // aBoardOutline makes sense only for the board editor. for fp holder boards
3593 // there are no board outlines area.
3594 const BOARD* brd = aBoardOutline->GetBoard();
3595
3596 if( !brd || brd->GetBoardUse() == BOARD_USE::FPHOLDER )
3597 return;
3598
3600 m_gal->Save();
3601
3602 const COLOR4D& outlineColor = m_pcbSettings.GetColor( aBoardOutline, aLayer );
3603 m_gal->SetFillColor( outlineColor );
3604 m_gal->AdvanceDepth();
3605 m_gal->SetLineWidth( 0 );
3606 m_gal->SetIsFill( true );
3607 m_gal->SetIsStroke( false );
3608 m_gal->DrawPolygon( aBoardOutline->GetOutline() );
3609
3610 m_gal->Restore();
3611}
3612
3613
3615 int aDrillSize, PCB_LAYER_ID aStartLayer,
3616 PCB_LAYER_ID aEndLayer )
3617{
3618 double backdrillRadius = aDrillSize / 2.0;
3619 double lineWidth = std::max( backdrillRadius / 4.0, m_pcbSettings.m_outlineWidth * 2.0 );
3620
3622 m_gal->AdvanceDepth();
3623 m_gal->SetIsFill( false );
3624 m_gal->SetIsStroke( true );
3625 m_gal->SetLineWidth( lineWidth );
3626
3627 // Draw semi-circle in start layer color (top half, from 90° to 270°)
3628 m_gal->SetStrokeColor( m_pcbSettings.GetColor( aItem, aStartLayer ) );
3629 m_gal->DrawArc( aCenter, backdrillRadius, EDA_ANGLE( 90, DEGREES_T ),
3630 EDA_ANGLE( 180, DEGREES_T ) );
3631
3632 // Draw semi-circle in end layer color (bottom half, from 270° to 90°)
3633 m_gal->SetStrokeColor( m_pcbSettings.GetColor( aItem, aEndLayer ) );
3634 m_gal->DrawArc( aCenter, backdrillRadius, EDA_ANGLE( 270, DEGREES_T ),
3635 EDA_ANGLE( 180, DEGREES_T ) );
3636}
3637
3638
3640{
3641 int size = 0;
3642
3643 // Check to see if the pad or via has a post-machining operation on this layer
3644 if( const PAD* pad = dynamic_cast<const PAD*>( aItem ) )
3645 {
3646 size = pad->GetPostMachiningKnockout( aLayer );
3647 }
3648 else if( const PCB_VIA* via = dynamic_cast<const PCB_VIA*>( aItem ) )
3649 {
3650 size = via->GetPostMachiningKnockout( aLayer );
3651 }
3652
3653 if( size <= 0 )
3654 return;
3655
3657 m_gal->AdvanceDepth();
3658
3659 double pmRadius = size / 2.0;
3660 // Use a line width proportional to the radius for visibility
3661 double lineWidth = std::max( pmRadius / 8.0, m_pcbSettings.m_outlineWidth * 2.0 );
3662
3663 COLOR4D layerColor = m_pcbSettings.GetColor( aItem, aLayer );
3664
3665 m_gal->SetIsFill( false );
3666 m_gal->SetIsStroke( true );
3667 m_gal->SetStrokeColor( layerColor );
3668 m_gal->SetLineWidth( lineWidth );
3669
3670 // Draw dashed circle manually with fixed number of segments for consistent appearance
3671 constexpr int NUM_DASHES = 12; // Number of dashes around the circle
3672 EDA_ANGLE dashAngle = ANGLE_360 / ( NUM_DASHES * 2 ); // Dash and gap are equal size
3673
3674 for( int i = 0; i < NUM_DASHES; ++i )
3675 {
3676 EDA_ANGLE startAngle = dashAngle * ( i * 2 );
3677 m_gal->DrawArc( aCenter, pmRadius, startAngle, dashAngle );
3678 }
3679}
3680
3681
3682const double PCB_RENDER_SETTINGS::MAX_FONT_SIZE = pcbIUScale.mmToIU( 10.0 );
const char * name
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr int ARC_HIGH_DEF
Definition base_units.h:137
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
@ FPHOLDER
Definition board.h:365
@ NORMAL
Inactive layers are shown normally (no high-contrast mode)
@ HIDDEN
Inactive layers are hidden.
@ RATSNEST
Net/netclass colors are shown on ratsnest lines only.
@ ALL
Net/netclass colors are shown on all net copper.
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
BOX2< VECTOR2D > BOX2D
Definition box2.h:919
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
Bezier curves to polygon converter.
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMaxError=10)
Convert a Bezier curve to a polygon.
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
const wxString & GetDisplayNetname() const
virtual int GetOwnClearance(PCB_LAYER_ID aLayer, wxString *aSource=nullptr) const
Return an item's "own" clearance in internal units.
Container for design settings for a BOARD object.
int GetHolePlatingThickness() const
Pad & via drills are finish size.
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:83
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:295
virtual BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const
Create a copy of this BOARD_ITEM.
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:159
virtual bool IsKnockout() const
Definition board_item.h:382
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:315
virtual void TransformShapeToPolySet(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, KIGFX::RENDER_SETTINGS *aRenderSettings=nullptr) const
Convert the item shape to a polyset.
Definition board_item.h:511
const KIFONT::METRICS & GetFontMetrics() const
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:235
bool IsSideSpecific() const
virtual bool IsOnCopperLayer() const
Definition board_item.h:176
int GetMaxError() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:373
bool IsFootprintHolder() const
Find out if the board is being used to hold a single footprint for editing/viewing.
Definition board.h:403
BOARD_USE GetBoardUse() const
Get what the board use is.
Definition board.h:392
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition board.cpp:1109
const LSET & GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1057
int GetCopperLayerCount() const
Definition board.cpp:994
KIGFX::COLOR4D GetNetChainColor(const wxString &aChain) const
Definition board.h:1218
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1158
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1043
constexpr const Vec & GetPosition() const
Definition box2.h:207
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:554
constexpr const Vec GetEnd() const
Definition box2.h:208
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:233
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:142
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr Vec Centre() const
Definition box2.h:93
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:164
constexpr const Vec & GetOrigin() const
Definition box2.h:206
constexpr const SizeVec & GetSize() const
Definition box2.h:202
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:293
Color settings are a bit different than most of the settings objects in that there can be more than o...
COLOR4D GetColor(int aLayer) const
APPEARANCE m_Appearance
EDA_ANGLE Normalize90()
Definition eda_angle.h:257
double AsRadians() const
Definition eda_angle.h:120
wxString GetName() const
Definition eda_group.h:47
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:135
const KIID m_Uuid
Definition eda_item.h:531
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
bool IsEntered() const
Definition eda_item.h:133
bool IsSelected() const
Definition eda_item.h:132
bool IsBrightened() const
Definition eda_item.h:134
bool IsMoving() const
Definition eda_item.h:130
int GetEllipseMinorRadius() const
Definition eda_shape.h:310
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:283
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:292
virtual VECTOR2I GetTopLeft() const
Definition eda_shape.h:271
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:338
int GetEllipseMajorRadius() const
Definition eda_shape.h:301
int GetRectangleWidth() const
virtual std::vector< SHAPE * > MakeEffectiveShapes(bool aEdgeOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition eda_shape.h:462
void CalcArcAngles(EDA_ANGLE &aStartAngle, EDA_ANGLE &aEndAngle) const
Calc arc start and end angles such that aStartAngle < aEndAngle.
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:319
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:185
virtual VECTOR2I GetBotRight() const
Definition eda_shape.h:272
const std::vector< SEG > & GetHatchLines() const
bool IsHatchedFill() const
Definition eda_shape.h:140
virtual int GetHatchLineWidth() const
Definition eda_shape.h:175
bool IsSolidFill() const
Definition eda_shape.h:133
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:240
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
std::vector< VECTOR2I > GetRectCorners() const
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:329
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition eda_shape.h:404
const VECTOR2I & GetBezierC1() const
Definition eda_shape.h:280
int GetRectangleHeight() const
int GetCornerRadius() const
virtual bool IsVisible() const
Definition eda_text.h:208
KIFONT::FONT * GetFont() const
Definition eda_text.h:268
std::vector< std::unique_ptr< KIFONT::GLYPH > > * GetRenderCache(const KIFONT::FONT *aFont, const wxString &forResolvedText, const VECTOR2I &aOffset={ 0, 0 }) const
Definition eda_text.cpp:703
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:773
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:221
virtual KIFONT::FONT * GetDrawFont(const RENDER_SETTINGS *aSettings) const
Definition eda_text.cpp:667
const TEXT_ATTRIBUTES & GetAttributes() const
Definition eda_text.h:252
int GetEffectiveTextPenWidth(int aDefaultPenWidth=0) const
The EffectiveTextPenWidth uses the text thickness if > 1 or aDefaultPenWidth.
Definition eda_text.cpp:461
LSET GetPrivateLayers() const
Definition footprint.h:315
SHAPE_POLY_SET GetBoundingHull() const
Return a bounding polygon for the shapes and pads in the footprint.
bool IsConflicting() const
const SHAPE_POLY_SET & GetCourtyard(PCB_LAYER_ID aLayer) const
Used in DRC to test the courtyard area (a complex polygon).
VECTOR2I GetPosition() const override
Definition footprint.h:406
DRAWINGS & GraphicalItems()
Definition footprint.h:378
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
APP_SETTINGS_BASE * KifaceSettings() const
Definition kiface_base.h:91
FONT is an abstract base class for both outline and stroke fonts.
Definition font.h:94
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:143
virtual bool IsStroke() const
Definition font.h:101
void Draw(KIGFX::GAL *aGal, const wxString &aText, const VECTOR2I &aPosition, const VECTOR2I &aCursor, const TEXT_ATTRIBUTES &aAttributes, const METRICS &aFontMetrics, std::optional< VECTOR2I > aMousePos=std::nullopt, wxString *aActiveUrl=nullptr) const
Draw a string.
Definition font.cpp:246
virtual bool IsOutline() const
Definition font.h:102
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
COLOR4D WithAlpha(double aAlpha) const
Return a color with the same color, but the given alpha.
Definition color4d.h:308
static const COLOR4D CLEAR
Definition color4d.h:403
COLOR4D & Darken(double aFactor)
Makes the color darker by a given factor.
Definition color4d.h:223
COLOR4D Darkened(double aFactor) const
Return a color that is darker by a given factor, without modifying object.
Definition color4d.h:279
COLOR4D Inverted() const
Returns an inverted color, alpha remains the same.
Definition color4d.h:320
COLOR4D & Brighten(double aFactor)
Makes the color brighter by a given factor.
Definition color4d.h:206
static const COLOR4D WHITE
Definition color4d.h:401
double a
Alpha component.
Definition color4d.h:392
COLOR4D Brightened(double aFactor) const
Return a color that is brighter by a given factor, without modifying object.
Definition color4d.h:265
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:398
static const COLOR4D BLACK
Definition color4d.h:402
COLOR4D Mix(const COLOR4D &aColor, double aFactor) const
Return a color that is mixed with the input by a factor.
Definition color4d.h:292
Attribute save/restore for GAL attributes.
Abstract interface for drawing on a 2D-surface.
GAL * m_gal
Instance of graphic abstraction layer that gives an interface to call commands used to draw (eg.
Definition painter.h:98
PAINTER(GAL *aGal)
Initialize this object for painting on any of the polymorphic GRAPHICS_ABSTRACTION_LAYER* derivatives...
Definition painter.cpp:29
void drawPostMachiningIndicator(const BOARD_ITEM *aItem, const VECTOR2D &aCenter, PCB_LAYER_ID aLayer)
Draw post-machining indicator (dashed circle) at the given center point.
virtual SHAPE_SEGMENT getPadHoleShape(const PAD *aPad) const
Return hole shape for a pad (internal units).
PCB_PAINTER(GAL *aGal, FRAME_T aFrameType)
int getLineThickness(int aActualThickness) const
Get the thickness to draw for a line (e.g.
void renderNetNameForSegment(const SHAPE_SEGMENT &aSeg, const COLOR4D &aColor, const wxString &aNetName) const
PCB_VIEWERS_SETTINGS_BASE * viewer_settings()
void draw(const PCB_TRACK *aTrack, int aLayer)
virtual PAD_DRILL_SHAPE getDrillShape(const PAD *aPad) const
Return drill shape of a pad.
PCB_RENDER_SETTINGS m_pcbSettings
virtual int getViaDrillSize(const PCB_VIA *aVia) const
Return drill diameter for a via (internal units).
void strokeText(const wxString &aText, const VECTOR2I &aPosition, const TEXT_ATTRIBUTES &aAttrs, const KIFONT::METRICS &aFontMetrics)
void drawBackdrillIndicator(const BOARD_ITEM *aItem, const VECTOR2D &aCenter, int aDrillSize, PCB_LAYER_ID aStartLayer, PCB_LAYER_ID aEndLayer)
Draw backdrill indicator (two semi-circles) at the given center point.
virtual bool Draw(const VIEW_ITEM *aItem, int aLayer) override
Takes an instance of VIEW_ITEM and passes it to a function that knows how to draw the item.
double m_zoneOpacity
Opacity override for filled zones.
double m_trackOpacity
Opacity override for all tracks.
double m_imageOpacity
Opacity override for user images.
double m_viaOpacity
Opacity override for all types of via.
ZONE_DISPLAY_MODE m_ZoneDisplayMode
void LoadColors(const COLOR_SETTINGS *aSettings) override
double m_padOpacity
Opacity override for SMD pads and PTHs.
void SetBackgroundColor(const COLOR4D &aColor) override
Set the background color.
COLOR4D GetColor(const VIEW_ITEM *aItem, int aLayer) const override
Returns the color that should be used to draw the specific VIEW_ITEM on the specific layer using curr...
HIGH_CONTRAST_MODE m_ContrastModeDisplay
std::map< int, KIGFX::COLOR4D > m_netColors
Set of net codes that should not have their ratsnest displayed.
NET_COLOR_MODE m_netColorMode
Overrides for specific netclass colors.
static const double MAX_FONT_SIZE
< Maximum font size for netnames (and other dynamically shown strings)
double m_filledShapeOpacity
Opacity override for graphic shapes.
bool GetShowPageLimits() const override
void LoadDisplayOptions(const PCB_DISPLAY_OPTIONS &aOptions)
Load settings related to display options (high-contrast mode, full or outline modes for vias/pads/tra...
PCB_LAYER_ID GetPrimaryHighContrastLayer() const
Return the board layer which is in high-contrast mode.
void SetGapLengthRatio(double aRatio)
PCB_LAYER_ID GetActiveLayer() const
std::map< int, COLOR4D > m_layerColorsHi
virtual void update()
Precalculates extra colors for layers (e.g.
void SetDashLengthRatio(double aRatio)
std::set< int > m_highlightNetcodes
std::map< int, COLOR4D > m_layerColorsDark
std::map< int, COLOR4D > m_layerColorsSel
std::set< int > m_highContrastLayers
std::map< int, COLOR4D > m_layerColors
bool m_hiContrastEnabled
Parameters for display modes.
An abstract base class for deriving all objects that can be added to a VIEW.
Definition view_item.h:82
bool IsBOARD_ITEM() const
Definition view_item.h:98
double GetForcedTransparency() const
Definition view_item.h:167
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
PCB_LAYER_ID ExtractLayer() const
Find the first set PCB_LAYER_ID.
Definition lset.cpp:538
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition lset.cpp:309
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition lset.cpp:595
static const LSET & PhysicalLayersMask()
Return a mask holding all layers which are physically realized.
Definition lset.cpp:693
int MarkerScale() const
The scaling factor to convert polygonal shape coordinates to internal units.
Definition marker_base.h:65
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...
COLOR4D GetPcbColor(bool aIsForSave=false) const
Definition netclass.h:195
bool HasPcbColor() const
Definition netclass.h:194
Handle the data for a net.
Definition netinfo.h:46
const wxString & GetNetChain() const
Definition netinfo.h:112
PAD * GetTerminalPad(int aIndex) const
Definition netinfo.h:115
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:256
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:171
Definition pad.h:61
int GetOwnClearance(PCB_LAYER_ID aLayer, wxString *aSource=nullptr) const override
Return the pad's "own" clearance in internal units.
Definition pad.cpp:1944
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition pad.h:552
const std::vector< std::shared_ptr< PCB_SHAPE > > & GetPrimitives(PCB_LAYER_ID aLayer) const
Accessor to the basic shape list for custom-shaped pads.
Definition pad.h:370
int GetSizeX() const
Definition pad.cpp:311
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
Definition pad.cpp:650
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer, FLASHING flashPTHPads=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition pad.cpp:1208
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
Definition pad.cpp:1599
PAD_ATTRIB GetAttribute() const
Definition pad.h:555
const wxString & GetPinFunction() const
Definition pad.h:154
const wxString & GetNumber() const
Definition pad.h:143
VECTOR2I GetPosition() const override
Definition pad.cpp:245
PCB_LAYER_ID GetTertiaryDrillEndLayer() const
Definition pad.h:537
VECTOR2I GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.cpp:796
PCB_LAYER_ID GetTertiaryDrillStartLayer() const
Definition pad.h:535
bool IsNoConnectPad() const
Definition pad.cpp:567
int GetDrillSizeX() const
Definition pad.h:317
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:202
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE, bool ignoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
Definition pad.cpp:2945
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1951
VECTOR2I GetSize(PCB_LAYER_ID aLayer) const
Definition pad.cpp:287
bool IsFreePad() const
Definition pad.cpp:573
const VECTOR2I & GetSecondaryDrillSize() const
Definition pad.h:506
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.cpp:1723
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:429
int GetSizeY() const
Definition pad.cpp:331
int GetPostMachiningKnockout(PCB_LAYER_ID aLayer) const
Get the knockout diameter for a layer affected by post-machining.
Definition pad.cpp:908
VECTOR2I GetSolderPasteMargin(PCB_LAYER_ID aLayer) const
Usually < 0 (mask shape smaller than pad)because the margin can be dependent on the pad size,...
Definition pad.cpp:2014
PCB_LAYER_ID GetSecondaryDrillStartLayer() const
Definition pad.h:518
const VECTOR2I & GetTertiaryDrillSize() const
Definition pad.h:523
VECTOR2I ShapePos(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1831
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Return a SHAPE_SEGMENT object representing the pad's hole.
Definition pad.cpp:1312
PCB_LAYER_ID GetSecondaryDrillEndLayer() const
Definition pad.h:520
DISPLAY_OPTIONS m_Display
EDA_ANGLE GetArcAngleStart() const
const VECTOR2I GetFocusPosition() const override
Similar to GetPosition() but allows items to return their visual center rather than their anchor.
Definition pcb_track.h:291
double GetRadius() const
EDA_ANGLE GetAngle() const
const VECTOR2I & GetMid() const
Definition pcb_track.h:286
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_track.h:293
void GetBoundingHull(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
bool HasOutline() const
const SHAPE_POLY_SET & GetOutline() const
Abstract dimension API.
int GetLineThickness() const
const std::vector< std::shared_ptr< SHAPE > > & GetShapes() const
double m_TrackOpacity
Opacity override for all tracks.
double m_FilledShapeOpacity
Opacity override for graphic shapes.
double m_ZoneOpacity
Opacity override for filled zone areas.
double m_ImageOpacity
Opacity override for user images.
double m_PadOpacity
Opacity override for SMD pads and PTHs.
double m_ViaOpacity
Opacity override for all types of via.
HIGH_CONTRAST_MODE m_ContrastModeDisplay
How inactive layers are displayed.
NET_COLOR_MODE m_NetColorMode
How to use color overrides on specific nets and netclasses.
ZONE_DISPLAY_MODE m_ZoneDisplayMode
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:51
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
void SetZoom(double aZoomFactor) const
std::vector< PCB_SHAPE > GetShapes() const
GAL_LAYER_ID GetColorLayer() const
VECTOR2I GetPosition() const override
Definition pcb_marker.h:60
PCB_VIEWERS_SETTINGS_BASE * viewer_settings()
A PCB_POINT is a 0-dimensional point that is used to mark a position on a PCB, or more usually a foot...
Definition pcb_point.h:39
int GetSize() const
Definition pcb_point.h:69
VECTOR2I GetPosition() const override
Definition pcb_point.h:60
Object to handle a bitmap image that can be inserted in a PCB.
REFERENCE_IMAGE & GetReferenceImage()
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:78
int GetWidth() const override
bool HasSolderMask() const
Definition pcb_shape.h:329
int GetSolderMaskExpansion() const
bool IsProxyItem() const override
Definition pcb_shape.h:146
STROKE_PARAMS GetStroke() const override
void UpdateHatching() const override
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition pcb_shape.h:68
int GetRowSpan() const
int GetColSpan() const
std::vector< PCB_TABLECELL * > GetCells() const
Definition pcb_table.h:156
void DrawBorders(const std::function< void(const VECTOR2I &aPt1, const VECTOR2I &aPt2, const STROKE_PARAMS &aStroke)> &aCallback) const
int GetShape() const
Definition pcb_target.h:54
int GetWidth() const
Definition pcb_target.h:60
int GetSize() const
Definition pcb_target.h:57
VECTOR2I GetPosition() const override
Definition pcb_target.h:51
bool IsBorderEnabled() const
Disables the border, this is done by changing the stroke internally.
void TransformTextToPolySet(SHAPE_POLY_SET &aBuffer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc) const
Function TransformTextToPolySet Convert the text to a polygonSet describing the actual character stro...
VECTOR2I GetTextSize() const override
VECTOR2I GetDrawPos() const override
wxString GetShownText(bool aAllowExtraText, int aDepth=0) const override
Return the string actually shown after processing of the base text.
std::vector< VECTOR2I > GetCorners() const override
Return 4 corners for a rectangle or rotated rectangle (stored as a poly).
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc, bool aIgnoreLineWidth=false) const override
Convert the item shape to a closed polygon.
Definition pcb_text.cpp:835
const SHAPE_POLY_SET & GetKnockoutCache(const KIFONT::FONT *aFont, const wxString &forResolvedText, int aMaxError) const
Definition pcb_text.cpp:707
wxString GetShownText(bool aAllowExtraText, int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition pcb_text.cpp:162
VECTOR2I GetTextPos() const override
Definition pcb_text.cpp:445
int GetTextThickness() const override
Definition pcb_text.cpp:481
EDA_ANGLE GetDrawRotation() const override
Definition pcb_text.cpp:204
VECTOR2I GetTextSize() const override
Definition pcb_text.cpp:454
int GetSolderMaskExpansion() const
const VECTOR2I & GetStart() const
Definition pcb_track.h:93
const VECTOR2I & GetEnd() const
Definition pcb_track.h:90
virtual int GetWidth() const
Definition pcb_track.h:87
PCB_LAYER_ID BottomLayer() const
PCB_LAYER_ID GetTertiaryDrillEndLayer() const
Definition pcb_track.h:802
std::optional< int > GetTertiaryDrillSize() const
bool FlashLayer(int aLayer) const
Check to see whether the via should have a pad on the specific layer.
std::optional< int > GetSecondaryDrillSize() const
PCB_LAYER_ID GetSecondaryDrillEndLayer() const
Definition pcb_track.h:788
int GetWidth() const override
PCB_LAYER_ID GetTertiaryDrillStartLayer() const
Definition pcb_track.h:799
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
PCB_LAYER_ID TopLayer() const
int GetDrillValue() const
Calculate the drill value for vias (m_drill if > 0, or default drill value for the board).
VIATYPE GetViaType() const
Definition pcb_track.h:402
PCB_LAYER_ID GetSecondaryDrillStartLayer() const
Definition pcb_track.h:785
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Return the 2 layers used by the via (the via actually uses all layers between these 2 layers)
VIEWERS_DISPLAY_OPTIONS m_ViewersDisplay
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:562
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition pgm_base.h:124
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
VECTOR2I GetPosition() const
VECTOR2I GetSize() const
const BITMAP_BASE & GetImage() const
Get the underlying image.
double GetImageScale() const
A round rectangle shape, based on a rectangle and a radius.
Definition roundrect.h:32
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aMaxError) const
Get the polygonal representation of the roundrect.
Definition roundrect.cpp:79
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
VECTOR2I::extended_type ecoord
Definition seg.h:40
VECTOR2I B
Definition seg.h:46
int Length() const
Return the length (this).
Definition seg.h:339
ecoord SquaredLength() const
Definition seg.h:344
T * GetAppSettings(const char *aFilename)
Return a handle to the a given settings by type.
VECTOR2I GetEnd() const override
Definition shape_arc.h:204
const SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError=DefaultAccuracyForPCB(), int *aActualError=nullptr) const
Construct a SHAPE_LINE_CHAIN of segments from a given arc.
VECTOR2I GetStart() const override
Definition shape_arc.h:203
const VECTOR2I & GetCenter() const
int GetRadius() const
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int PointCount() const
Return the number of points (vertices) in this line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
Represent a set of closed polygons.
bool IsTriangulationUpToDate() const
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
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)
virtual void CacheTriangulation(bool aSimplify=false, const TASK_SUBMITTER &aSubmitter={})
Build a polygon triangulation, needed to draw a polygon on OpenGL and in some other calculations.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
const SHAPE_LINE_CHAIN & CHole(int aOutline, int aHole) const
int OutlineCount() const
Return the number of outlines in the set.
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
int GetWidth() const override
Definition shape_rect.h:181
const VECTOR2I & GetPosition() const
Definition shape_rect.h:165
const VECTOR2I GetSize() const
Definition shape_rect.h:173
int GetHeight() const
Definition shape_rect.h:189
const SEG & GetSeg() const
int GetWidth() const override
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
const SHAPE_LINE_CHAIN & Vertices() const
Return the list of vertices defining this simple polygon.
virtual const SEG GetSegment(int aIndex) const override
const VECTOR2I & CPoint(int aIndex) const
Return a const reference to a given point in the polygon.
int PointCount() const
Return the number of points (vertices) in this polygon.
virtual size_t GetSegmentCount() const override
An abstract shape on 2D plane.
Definition shape.h:124
Simple container to manage line stroke parameters.
int GetWidth() const
LINE_STYLE GetLineStyle() 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)
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
KIFONT::FONT * m_Font
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:381
Handle a list of polygons defining a copper zone.
Definition zone.h:70
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:813
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:697
SHAPE_POLY_SET GetBoardOutline() const
Definition zone.cpp:874
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:750
bool IsTeardropArea() const
Definition zone.h:788
ZONE_BORDER_DISPLAY_STYLE GetHatchStyle() const
Definition zone.h:685
bool IsConflicting() const
For rule areas which exclude footprints (and therefore participate in courtyard conflicts during move...
Definition zone.cpp:537
std::vector< SEG > GetHatchLines() const
Definition zone.cpp:1560
@ MAGENTA
Definition color4d.h:56
@ CYAN
Definition color4d.h:54
void TransformArcToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arc to multiple straight segments.
@ CHAMFER_ALL_CORNERS
All angles are chamfered.
@ ROUND_ALL_CORNERS
All angles are rounded.
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ DEGREES_T
Definition eda_angle.h:31
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_360
Definition eda_angle.h:417
#define IGNORE_PARENT_GROUP
Definition eda_item.h:53
@ UNDEFINED
Definition eda_shape.h:45
@ ELLIPSE
Definition eda_shape.h:52
@ SEGMENT
Definition eda_shape.h:46
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
@ ELLIPSE_ARC
Definition eda_shape.h:53
FRAME_T
The set of EDA_BASE_FRAME derivatives, typically stored in EDA_BASE_FRAME::m_Ident.
Definition frame_type.h:29
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
@ FRAME_CVPCB_DISPLAY
Definition frame_type.h:49
@ FRAME_FOOTPRINT_VIEWER
Definition frame_type.h:41
@ FRAME_FOOTPRINT_WIZARD
Definition frame_type.h:42
@ FRAME_FOOTPRINT_PREVIEW
Definition frame_type.h:44
@ FRAME_FOOTPRINT_CHOOSER
Definition frame_type.h:40
@ FRAME_FOOTPRINT_EDITOR
Definition frame_type.h:39
@ FRAME_PCB_DISPLAY3D
Definition frame_type.h:43
@ FRAME_CVPCB
Definition frame_type.h:48
a few functions useful in geometry calculations.
int GetPenSizeForNormal(int aTextSize)
Definition gr_text.cpp:57
double m_HoleWallPaintingMultiplier
What factor to use when painting via and PTH pad hole walls, so that the painted hole wall can be ove...
bool IsSolderMaskLayer(int aLayer)
Definition layer_ids.h:754
@ LAYER_PAD_FR_NETNAMES
Additional netnames layers (not associated with a PCB layer).
Definition layer_ids.h:196
@ LAYER_PAD_BK_NETNAMES
Definition layer_ids.h:197
@ LAYER_PAD_NETNAMES
Definition layer_ids.h:198
@ NETNAMES_LAYER_ID_START
Definition layer_ids.h:190
bool IsPcbLayer(int aLayer)
Test whether a layer is a valid layer for Pcbnew.
Definition layer_ids.h:672
bool IsPadCopperLayer(int aLayer)
Definition layer_ids.h:887
bool IsPointsLayer(int aLayer)
Definition layer_ids.h:905
int GetNetnameLayer(int aLayer)
Return a netname layer corresponding to the given layer.
Definition layer_ids.h:860
bool IsClearanceLayer(int aLayer)
Definition layer_ids.h:899
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:683
@ LAYER_POINTS
PCB reference/manual snap points visibility.
Definition layer_ids.h:317
@ GAL_LAYER_ID_START
Definition layer_ids.h:225
@ LAYER_LOCKED_ITEM_SHADOW
Shadow layer for locked items.
Definition layer_ids.h:303
@ LAYER_PAD_COPPER_START
Virtual layers for pad copper on a given copper layer.
Definition layer_ids.h:343
@ LAYER_VIA_HOLEWALLS
Definition layer_ids.h:294
@ LAYER_CONFLICTS_SHADOW
Shadow layer for items flagged conflicting.
Definition layer_ids.h:306
@ LAYER_NON_PLATEDHOLES
Draw usual through hole vias.
Definition layer_ids.h:235
@ LAYER_DRC_EXCLUSION
Layer for DRC markers which have been individually excluded.
Definition layer_ids.h:300
@ LAYER_PCB_BACKGROUND
PCB background color.
Definition layer_ids.h:277
@ LAYER_DRC_HIGHLIGHTED
Color for highlighted DRC markers.
Definition layer_ids.h:331
@ LAYER_DRC_SHAPES
Custom shapes for DRC markers.
Definition layer_ids.h:311
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
Definition layer_ids.h:288
@ LAYER_DRC_WARNING
Layer for DRC markers with #SEVERITY_WARNING.
Definition layer_ids.h:297
@ LAYER_PAD_PLATEDHOLES
to draw pad holes (plated)
Definition layer_ids.h:267
@ GAL_LAYER_ID_END
Definition layer_ids.h:366
@ LAYER_VIA_COPPER_START
Virtual layers for via copper on a given copper layer.
Definition layer_ids.h:347
@ LAYER_CONSTRAINT_SHADOW
Shadow layer for items bound to a constraint.
Definition layer_ids.h:320
@ LAYER_CLEARANCE_START
Virtual layers for pad/via/track clearance outlines for a given copper layer.
Definition layer_ids.h:351
@ LAYER_ZONE_START
Virtual layers for stacking zones and tracks on a given copper layer.
Definition layer_ids.h:339
@ LAYER_ANCHOR
Anchor of items having an anchor point (texts, footprints).
Definition layer_ids.h:244
@ LAYER_VIA_BURIED
Draw blind vias.
Definition layer_ids.h:231
@ LAYER_MARKER_SHADOWS
Shadows for DRC markers.
Definition layer_ids.h:301
@ LAYER_VIA_HOLES
Draw via holes (pad holes do not use this layer).
Definition layer_ids.h:270
@ LAYER_VIA_BLIND
Draw micro vias.
Definition layer_ids.h:230
@ LAYER_VIA_MICROVIA
Definition layer_ids.h:229
@ LAYER_VIA_THROUGH
Draw buried vias.
Definition layer_ids.h:232
@ LAYER_DRC_ERROR
Layer for DRC markers with #SEVERITY_ERROR.
Definition layer_ids.h:273
@ LAYER_PAD_HOLEWALLS
Definition layer_ids.h:293
bool IsViaCopperLayer(int aLayer)
Definition layer_ids.h:893
bool IsNetnameLayer(int aLayer)
Test whether a layer is a netname layer.
Definition layer_ids.h:875
bool IsHoleLayer(int aLayer)
Definition layer_ids.h:745
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:694
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ F_CrtYd
Definition layer_ids.h:112
@ Edge_Cuts
Definition layer_ids.h:108
@ F_Paste
Definition layer_ids.h:100
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ B_Paste
Definition layer_ids.h:101
@ F_SilkS
Definition layer_ids.h:96
@ B_CrtYd
Definition layer_ids.h:111
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ PCB_LAYER_ID_COUNT
Definition layer_ids.h:167
@ F_Cu
Definition layer_ids.h:60
bool IsZoneFillLayer(int aLayer)
Definition layer_ids.h:881
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
MATRIX3x3< double > MATRIX3x3D
Definition matrix3x3.h:469
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:29
bool ZoneOutlineDrawnOnLayer(bool aOutlineOnly, int aLayer)
Decide which GAL draw pass paints a zone's outline.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
PAD_DRILL_SHAPE
The set of pad drill shapes, used with PAD::{Set,Get}DrillShape()
Definition padstack.h:69
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:103
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ ROUNDRECT
Definition padstack.h:57
BARCODE class definition.
Class to handle a set of BOARD_ITEMs.
PCBNEW_SETTINGS * pcbconfig()
@ SHOW_WITH_VIA_ALWAYS
PGM_BASE & Pgm()
The global program "get" accessor.
PGM_BASE * PgmOrNull()
Return a reference that can be nullptr when running a shared lib from a script, not from a kicad app.
see class PGM_BASE
@ SH_RECT
axis-aligned rectangle
Definition shape.h:43
@ SH_CIRCLE
circle
Definition shape.h:46
@ SH_SIMPLE
simple polygon
Definition shape.h:47
@ SH_SEGMENT
line segment
Definition shape.h:44
wxString UnescapeString(const wxString &aSource)
int PrintableCharCount(const wxString &aString)
Return the number of printable (ie: non-formatting) chars.
LINE_STYLE
Dashed line types.
VECTOR2I center
int radius
VECTOR2I end
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
int clearance
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_CENTER
#define M_PI
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:225
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:81
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:99
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:96
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:90
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:104
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:86
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:85
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:82
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:83
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition typeinfo.h:92
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:94
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:100
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:88
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:95
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_BOARD_OUTLINE_T
class PCB_BOARD_OUTLINE_T, a pcb board outline item
Definition typeinfo.h:105
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:87
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:106
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:98
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682