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_drill_chart.h>
29#include <pcb_drill_map.h>
30#include <pcb_track.h>
31#include <pcb_group.h>
32#include <footprint.h>
33#include <pad.h>
34#include <pcb_shape.h>
35#include <string_utils.h>
36#include <zone.h>
37#include <pcb_reference_image.h>
38#include <pcb_text.h>
39#include <pcb_textbox.h>
44#include <pcb_table.h>
45#include <pcb_tablecell.h>
46#include <pcb_marker.h>
47#include <pcb_dimension.h>
48#include <pcb_point.h>
49#include <pcb_barcode.h>
50#include <pcb_target.h>
51#include <pcb_board_outline.h>
52#include <pcb_grid_item.h>
53
54#include <layer_ids.h>
55#include <lset.h>
56#include <pcb_painter.h>
57#include <pcb_display_options.h>
63#include <pcbnew_settings.h>
65
68#include <callback_gal.h>
71#include <geometry/shape_rect.h>
73#include <geometry/roundrect.h>
77#include <geometry/shape_arc.h>
78#include <stroke_params.h>
79#include <bezier_curves.h>
80#include <kiface_base.h>
81#include <gr_text.h>
82#include <pgm_base.h>
83
84using namespace KIGFX;
85
86
88{
89 return dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
90}
91
92// Helpers for display options existing in Cvpcb and Pcbnew
93// Note, when running Cvpcb, pcbconfig() returns nullptr and viewer_settings()
94// returns the viewer options existing to Cvpcb and Pcbnew
116
117
119{
120 m_backgroundColor = COLOR4D( 0.0, 0.0, 0.0, 1.0 );
124
125 m_trackOpacity = 1.0;
126 m_viaOpacity = 1.0;
127 m_padOpacity = 1.0;
128 m_zoneOpacity = 1.0;
129 m_imageOpacity = 1.0;
131
133
134 m_PadEditModePad = nullptr;
135
136 SetDashLengthRatio( 12 ); // From ISO 128-2
137 SetGapLengthRatio( 3 ); // From ISO 128-2
138
140
141 update();
142}
143
144
146{
148
149 // Init board layers colors:
150 for( int i = 0; i < PCB_LAYER_ID_COUNT; i++ )
151 {
152 m_layerColors[i] = aSettings->GetColor( i );
153
154 // Guard: if the alpha channel is too small, the layer is not visible.
155 if( m_layerColors[i].a < 0.2 )
156 m_layerColors[i].a = 0.2;
157 }
158
159 // Init specific graphic layers colors:
160 for( int i = GAL_LAYER_ID_START; i < GAL_LAYER_ID_END; i++ )
161 m_layerColors[i] = aSettings->GetColor( i );
162
163 // A per-board-layer GAL layer is unknown to every theme and resolves to UNSPECIFIED,
164 // which is transparent, so take the colour of the documentation layer hosting the map
165 for( int i = 0; i < PCB_LAYER_ID_COUNT; i++ )
167
168 // Colors for layers that aren't theme-able
171
172 // Netnames for copper layers
173 const COLOR4D lightLabel = aSettings->GetColor( NETNAMES_LAYER_ID_START );
174 const COLOR4D darkLabel = lightLabel.Inverted();
175
176 for( PCB_LAYER_ID layer : LSET::AllCuMask().CuStack() )
177 {
178 if( m_layerColors[layer].GetBrightness() > 0.5 )
179 m_layerColors[GetNetnameLayer( layer )] = darkLabel;
180 else
181 m_layerColors[GetNetnameLayer( layer )] = lightLabel;
182 }
183
184 if( PgmOrNull() ) // can be null if used without project (i.e. from python script)
186 else
187 m_hiContrastFactor = 1.0f - 0.8f; // default value
188
189 update();
190}
191
192
207
208
209COLOR4D PCB_RENDER_SETTINGS::GetColor( const VIEW_ITEM* aItem, int aLayer ) const
210{
211 return GetColor( dynamic_cast<const BOARD_ITEM*>( aItem ), aLayer );
212}
213
214
216{
217 if( aLayer < 0 || aLayer >= MAX_LAYERS_FOR_VIEW || !m_canvasFadedLayers.test( aLayer ) )
218 return false;
219
220 // These through-hole pads follow the via layer's colour and activity, not their own
221 if( IsPadCopperLayer( aLayer ) && pcbconfig() && pcbconfig()->m_Display.m_UseViaColorForNormalTHPadstacks )
222 return false;
223
224 return true;
225}
226
227
228bool PCB_RENDER_SETTINGS::GetLayerDim( int aLayer, COLOR4D& aToward, double& aKeep ) const
229{
230 if( !IsCanvasFadedLayer( aLayer ) )
231 return false;
232
233 auto it = m_layerColors.find( LAYER_PCB_BACKGROUND );
234 const COLOR4D background = it == m_layerColors.end() ? COLOR4D::BLACK : it->second;
235
236 // Net highlight fades every item it does not highlight towards black
237 aToward = COLOR4D::BLACK;
239
240 // Mirrors GetColor in dimmed high contrast, where zones, pad and via copper and clearance
241 // outlines follow their copper layer
242 int base = aLayer;
243
244 if( IsZoneFillLayer( aLayer ) )
245 base = aLayer - LAYER_ZONE_START;
246 else if( IsPadCopperLayer( aLayer ) )
247 base = aLayer - LAYER_PAD_COPPER_START;
248 else if( IsViaCopperLayer( aLayer ) )
249 base = aLayer - LAYER_VIA_COPPER_START;
250 else if( IsClearanceLayer( aLayer ) )
251 base = aLayer - LAYER_CLEARANCE_START;
252
254 && !m_highContrastLayers.count( base ) )
255 {
256 const double contrast = base == Edge_Cuts ? std::max( m_hiContrastFactor, 0.3f ) : m_hiContrastFactor;
257
258 // GetColor mixes c * keep with the background, which equals one fade keeping keep * contrast
259 // of c towards a colour with the same background share
260 const double keep = aKeep * contrast;
261 const double share = keep < 1.0 ? ( 1.0 - contrast ) / ( 1.0 - keep ) : 0.0;
262
263 aToward = COLOR4D( background.r * share, background.g * share, background.b * share, 1.0 );
264 aKeep = keep;
265 }
266
267 return true;
268}
269
270
272{
273 const BOARD_ITEM* item = dynamic_cast<const BOARD_ITEM*>( aItem );
274
275 if( !item )
276 return false;
277
278 // Must match the items GetColor leaves out of high contrast dimming
279 if( item->IsBrightened() || item->IsSelected() )
280 return true;
281
282 if( m_highlightEnabled && item->IsConnected() && item->IsOnCopperLayer() )
283 return m_highlightNetcodes.count( static_cast<const BOARD_CONNECTED_ITEM*>( item )->GetNetCode() ) > 0;
284
285 return false;
286}
287
288
289COLOR4D PCB_RENDER_SETTINGS::GetColor( const BOARD_ITEM* aItem, int aLayer ) const
290{
291 int netCode = -1;
292 int originalLayer = aLayer;
293
294 if( aLayer == LAYER_MARKER_SHADOWS )
295 return m_backgroundColor.WithAlpha( 0.5 );
296
297 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
298 return m_layerColors.at( aLayer );
299
300 if( aLayer == LAYER_CONSTRAINT_SHADOW )
301 return m_layerColors.at( aLayer );
302
303 // SMD pads use the copper netname layer
304 if( aLayer == LAYER_PAD_FR_NETNAMES )
305 aLayer = GetNetnameLayer( F_Cu );
306 else if( aLayer == LAYER_PAD_BK_NETNAMES )
307 aLayer = GetNetnameLayer( B_Cu );
308
309 if( IsHoleLayer( aLayer ) && m_isPrinting )
310 {
311 // Careful that we don't end up with the same colour for the annular ring and the hole
312 // when printing in B&W.
313 const PAD* pad = dynamic_cast<const PAD*>( aItem );
314 const PCB_VIA* via = dynamic_cast<const PCB_VIA*>( aItem );
315 int holeLayer = aLayer;
316 int annularRingLayer = UNDEFINED_LAYER;
317
318 if( pad && pad->GetAttribute() == PAD_ATTRIB::PTH )
319 {
320 LSET copperLayers = pad->GetLayerSet() & LSET::AllCuMask();
321
322 if( !copperLayers.empty() )
323 annularRingLayer = copperLayers.Seq().front();
324 }
325 else if( via )
326 {
327 annularRingLayer = F_Cu;
328 }
329
330 if( annularRingLayer != UNDEFINED_LAYER )
331 {
332 auto it = m_layerColors.find( holeLayer );
333 auto it2 = m_layerColors.find( annularRingLayer );
334
335 if( it != m_layerColors.end() && it2 != m_layerColors.end() && it->second == it2->second )
336 aLayer = LAYER_PCB_BACKGROUND;
337 }
338 }
339
340 // Zones should pull from the copper layer
341 if( aItem && aItem->Type() == PCB_ZONE_T )
342 {
343 if( IsZoneFillLayer( aLayer ) )
344 aLayer = aLayer - LAYER_ZONE_START;
345 }
346
347 // Points use the LAYER_POINTS color for their virtual per-layer layers
348 if( IsPointsLayer( aLayer ) )
349 aLayer = LAYER_POINTS;
350
351 // Pad and via copper and clearance outlines take their color from the copper layer
352 if( IsPadCopperLayer( aLayer ) )
353 {
354 if( pcbconfig() && aItem && aItem->Type() == PCB_PAD_T )
355 {
356 const PAD* pad = static_cast<const PAD*>( aItem );
357
358 // Old-skool display for people who struggle with change
359 if( pcbconfig()->m_Display.m_UseViaColorForNormalTHPadstacks
360 && pad->GetAttribute() == PAD_ATTRIB::PTH
361 && pad->Padstack().Mode() == PADSTACK::MODE::NORMAL )
362 {
363 aLayer = LAYER_VIA_HOLES;
364 }
365 else
366 {
367 aLayer = aLayer - LAYER_PAD_COPPER_START;
368 }
369 }
370 else
371 {
372 aLayer = aLayer - LAYER_PAD_COPPER_START;
373 }
374 }
375 else if( IsViaCopperLayer( aLayer ) )
376 aLayer = aLayer - LAYER_VIA_COPPER_START;
377 else if( IsClearanceLayer( aLayer ) )
378 aLayer = aLayer - LAYER_CLEARANCE_START;
379
380 // Use via "golden copper" hole color for pad hole walls for contrast
381 else if( aLayer == LAYER_PAD_HOLEWALLS )
382 aLayer = LAYER_VIA_HOLES;
383
384 // Show via mask layers if appropriate
385 if( aLayer == LAYER_VIA_THROUGH && !m_isPrinting )
386 {
387 if( aItem && aItem->GetBoard() )
388 {
389 LSET visibleLayers = aItem->GetBoard()->GetVisibleLayers()
390 & aItem->GetBoard()->GetEnabledLayers()
391 & aItem->GetLayerSet();
392
393 if( GetActiveLayer() == F_Mask && visibleLayers.test( F_Mask ) )
394 {
395 aLayer = F_Mask;
396 }
397 else if( GetActiveLayer() == B_Mask && visibleLayers.test( B_Mask ) )
398 {
399 aLayer = B_Mask;
400 }
401 else if( ( visibleLayers & LSET::AllCuMask() ).none() )
402 {
403 if( visibleLayers.any() )
404 aLayer = visibleLayers.Seq().back();
405 }
406 }
407 }
408
409 // Normal path: get the layer base color
410 auto it = m_layerColors.find( aLayer );
411 COLOR4D color = it == m_layerColors.end() ? COLOR4D::WHITE : it->second;
412
413 if( !aItem )
414 return color;
415
416 // Selection disambiguation
417 if( aItem->IsBrightened() || ( aItem->Type() == PCB_MARKER_T && aItem->IsSelected() ) )
418 {
419 if( aItem->Type() == PCB_MARKER_T )
420 {
421 auto itemLayerIter = m_layerColors.find( LAYER_DRC_HIGHLIGHTED );
422
423 if( itemLayerIter != m_layerColors.end() )
424 return itemLayerIter->second;
425 }
426
427 return color.Brightened( m_selectFactor ).WithAlpha( 0.8 );
428 }
429
430 // Normal selection
431 if( aItem->IsSelected() )
432 {
433 // Selection for tables is done with a background wash, so pass in nullptr to GetColor()
434 // so we just get the "normal" (un-selected/un-brightened) color for the borders.
435 if( BaseType( aItem->Type() ) != PCB_TABLE_T && aItem->Type() != PCB_TABLECELL_T )
436 {
437 auto it_selected = m_layerColorsSel.find( aLayer );
438 color = it_selected == m_layerColorsSel.end() ? color.Brightened( 0.8 ) : it_selected->second;
439 }
440 }
441
442 // Some graphic objects are BOARD_CONNECTED_ITEM, but they are seen here as
443 // actually board connected objects only if on a copper layer
444 const BOARD_CONNECTED_ITEM* conItem = nullptr;
445
446 if( aItem->IsConnected() && aItem->IsOnCopperLayer() )
447 conItem = static_cast<const BOARD_CONNECTED_ITEM*>( aItem );
448
449 // Try to obtain the netcode for the aItem
450 if( conItem )
451 netCode = conItem->GetNetCode();
452
453 bool highlighted = m_highlightEnabled && m_highlightNetcodes.count( netCode );
454 bool selected = aItem->IsSelected();
455
456 // Apply net color overrides
457 if( conItem && m_netColorMode == NET_COLOR_MODE::ALL && IsCopperLayer( aLayer ) )
458 {
459 COLOR4D netColor = COLOR4D::UNSPECIFIED;
460
461 auto ii = m_netColors.find( netCode );
462
463 if( ii != m_netColors.end() )
464 netColor = ii->second;
465
466 if( netColor == COLOR4D::UNSPECIFIED )
467 {
468 const NETCLASS* nc = conItem->GetEffectiveNetClass();
469
470 if( nc->HasPcbColor() )
471 netColor = nc->GetPcbColor();
472 }
473
474 if( netColor == COLOR4D::UNSPECIFIED )
475 netColor = color;
476
477 if( selected )
478 {
479 // Selection brightening overrides highlighting
480 netColor.Brighten( m_selectFactor );
481 }
482 else if( m_highlightEnabled )
483 {
484 // Highlight brightens objects on all layers and darkens everything else. The canvas
485 // darkens layers it fades itself, see GetLayerDim.
486 if( highlighted )
487 netColor.Brighten( m_highlightFactor );
488 else if( !IsCanvasFadedLayer( originalLayer ) )
489 netColor.Darken( 1.0 - m_highlightFactor );
490 }
491
492 color = netColor;
493 }
494 else if( !selected && m_highlightEnabled )
495 {
496 // Single net highlight mode
497 if( m_highlightNetcodes.contains( netCode ) )
498 {
499 auto it_hi = m_layerColorsHi.find( aLayer );
500 color = it_hi == m_layerColorsHi.end() ? color.Brightened( m_highlightFactor ) : it_hi->second;
501 }
502 else if( !IsCanvasFadedLayer( originalLayer ) )
503 {
504 auto it_dark = m_layerColorsDark.find( aLayer );
505 color = it_dark == m_layerColorsDark.end() ? color.Darkened( 1.0 - m_highlightFactor ) : it_dark->second;
506 }
507 }
508
509 // Apply high-contrast dimming
510 if( m_hiContrastEnabled && m_highContrastLayers.size() && !highlighted && !selected )
511 {
513 bool isActive = m_highContrastLayers.count( aLayer );
514 bool hide = false;
515
516 switch( originalLayer )
517 {
518 case LAYER_PADS:
519 {
520 const PAD* pad = static_cast<const PAD*>( aItem );
521
522 if( pad->IsOnLayer( primary ) && !pad->FlashLayer( primary ) )
523 {
524 isActive = false;
525
526 if( IsCopperLayer( primary ) )
527 hide = true;
528 }
529
531 isActive = false;
532
533 break;
534 }
535
536 case LAYER_VIA_BLIND:
537 case LAYER_VIA_BURIED:
539 {
540 const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem );
541
542 // Target graphic is active if the via crosses the primary layer
543 if( via->GetLayerSet().test( primary ) == 0 )
544 {
545 isActive = false;
546 hide = true;
547 }
548
549 break;
550 }
551
553 {
554 const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem );
555
556 if( !via->FlashLayer( primary ) )
557 {
558 isActive = false;
559
560 if( IsCopperLayer( primary ) )
561 hide = true;
562 }
563
564 break;
565 }
566
570 // Pad holes are active is any physical layer is active
571 if( LSET::PhysicalLayersMask().test( primary ) == 0 )
572 isActive = false;
573
574 break;
575
576 case LAYER_VIA_HOLES:
578 {
579 const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem );
580
581 if( via->GetViaType() == VIATYPE::THROUGH )
582 {
583 // A through via's hole is active if any physical layer is active
584 if( LSET::PhysicalLayersMask().test( primary ) == 0 )
585 isActive = false;
586 }
587 else
588 {
589 // A blind/buried or micro via's hole is active if it crosses the primary layer
590 if( via->GetLayerSet().test( primary ) == 0 )
591 isActive = false;
592 }
593
594 break;
595 }
596
597 case LAYER_DRC_ERROR:
600 isActive = true;
601 break;
602
603 default:
604 break;
605 }
606
607 if( !isActive )
608 {
609 // Graphics on Edge_Cuts layer are not fully dimmed or hidden because they are
610 // useful when working on another layer
611 // We could use a dim factor = m_hiContrastFactor, but to have a sufficient
612 // contrast whenever m_hiContrastFactor value, we clamp the factor to 0.3f
613 // (arbitray choice after tests)
614 float dim_factor_Edge_Cuts = std::max( m_hiContrastFactor, 0.3f );
615
617 || IsNetnameLayer( aLayer )
618 || hide )
619 {
620 if( originalLayer == Edge_Cuts )
621 {
623
624 if( it != m_layerColors.end() )
625 color = color.Mix( it->second, dim_factor_Edge_Cuts );
626 else
627 color = color.Mix( COLOR4D::BLACK, dim_factor_Edge_Cuts );
628 }
629 else
630 color = COLOR4D::CLEAR;
631 }
632 else if( !IsCanvasFadedLayer( originalLayer ) )
633 {
634 // The canvas fades the other layers as a whole, see GetLayerDim
636 COLOR4D backgroundColor = it == m_layerColors.end() ? COLOR4D::BLACK : it->second;
637
638 if( originalLayer == Edge_Cuts )
639 color = color.Mix( backgroundColor, dim_factor_Edge_Cuts );
640 else
641 color = color.Mix( backgroundColor, m_hiContrastFactor );
642
643 // Reference images can't have their color mixed so just reduce the opacity a bit
644 // so they show through less
645 if( aItem->Type() == PCB_REFERENCE_IMAGE_T )
646 color.a *= m_hiContrastFactor;
647 }
648 }
649 }
650 else if( originalLayer == LAYER_VIA_BLIND
651 || originalLayer == LAYER_VIA_BURIED
652 || originalLayer == LAYER_VIA_MICROVIA )
653 {
654 const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem );
655 const BOARD* board = via->GetBoard();
656 LSET visibleLayers = LSET::AllLayersMask();
657
658 if( board )
659 visibleLayers = board->GetVisibleLayers() & board->GetEnabledLayers();
660
661 // Target graphic is visible if the via crosses a visible layer
662 if( ( via->GetLayerSet() & visibleLayers ).none() )
663 color = COLOR4D::CLEAR;
664 }
665
666 // Apply per-type opacity overrides
667 if( aItem->Type() == PCB_TRACE_T || aItem->Type() == PCB_ARC_T )
668 color.a *= m_trackOpacity;
669 else if( aItem->Type() == PCB_VIA_T )
670 color.a *= m_viaOpacity;
671 else if( aItem->Type() == PCB_PAD_T )
672 color.a *= m_padOpacity;
673 else if( aItem->Type() == PCB_ZONE_T && static_cast<const ZONE*>( aItem )->IsTeardropArea() )
674 color.a *= m_trackOpacity;
675 else if( aItem->Type() == PCB_ZONE_T )
676 color.a *= m_zoneOpacity;
677 else if( aItem->Type() == PCB_REFERENCE_IMAGE_T )
678 color.a *= m_imageOpacity;
679 else if( aItem->Type() == PCB_SHAPE_T && static_cast<const PCB_SHAPE*>( aItem )->IsAnyFill() )
680 color.a *= m_filledShapeOpacity;
681 else if( aItem->Type() == PCB_SHAPE_T && aItem->IsOnCopperLayer() )
682 color.a *= m_trackOpacity;
683
684 if( aItem->GetForcedTransparency() > 0.0 )
685 color = color.WithAlpha( color.a * ( 1.0 - aItem->GetForcedTransparency() ) );
686
687 // No special modifiers enabled
688 return color;
689}
690
691
696
697
699 PAINTER( aGal ),
700 m_frameType( aFrameType ),
704{
705}
706
707
708int PCB_PAINTER::getLineThickness( int aActualThickness ) const
709{
710 // if items have 0 thickness, draw them with the outline
711 // width, otherwise respect the set value (which, no matter
712 // how small will produce something)
713 if( aActualThickness == 0 )
714 return KiROUND( m_pcbSettings.m_outlineWidth );
715
716 return aActualThickness;
717}
718
719
721{
722 return aPad->GetDrillShape();
723}
724
725
727{
728 SHAPE_SEGMENT segm = *aPad->GetEffectiveHoleShape();
729 return segm;
730}
731
732
733int PCB_PAINTER::getViaDrillSize( const PCB_VIA* aVia ) const
734{
735 return aVia->GetDrillValue();
736}
737
738
739bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
740{
741 if( !aItem->IsBOARD_ITEM() )
742 return false;
743
744 const BOARD_ITEM* item = static_cast<const BOARD_ITEM*>( aItem );
745
746 if( const BOARD* board = item->GetBoard() )
747 {
748 const BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
752
753 if( item->GetParentFootprint() && !board->IsFootprintHolder() )
754 {
755 FOOTPRINT* parentFP = item->GetParentFootprint();
756
757 // Never draw footprint reference images on board
758 if( item->Type() == PCB_REFERENCE_IMAGE_T )
759 {
760 return false;
761 }
762 else if( item->GetLayerSet().count() > 1 )
763 {
764 // For multi-layer objects, exclude only those layers that are private
765 if( IsPcbLayer( aLayer ) && parentFP->GetPrivateLayers().test( aLayer ) )
766 return false;
767 }
768 else if( item->GetLayerSet().count() == 1 )
769 {
770 // For single-layer objects, exclude all layers including ancillary layers
771 // such as holes, netnames, etc.
772 PCB_LAYER_ID singleLayer = item->GetLayerSet().ExtractLayer();
773
774 if( parentFP->GetPrivateLayers().test( singleLayer ) )
775 return false;
776 }
777 }
778 }
779 else
780 {
783 }
784
785 // the "cast" applied in here clarifies which overloaded draw() is called
786 switch( item->Type() )
787 {
788 case PCB_TRACE_T:
789 draw( static_cast<const PCB_TRACK*>( item ), aLayer );
790 break;
791
792 case PCB_ARC_T:
793 draw( static_cast<const PCB_ARC*>( item ), aLayer );
794 break;
795
796 case PCB_VIA_T:
797 if( IsDrillSymbolLayer( aLayer ) )
798 drawDrillSymbol( item, aLayer );
799 else
800 draw( static_cast<const PCB_VIA*>( item ), aLayer );
801
802 break;
803
804 case PCB_PAD_T:
805 if( IsDrillSymbolLayer( aLayer ) )
806 drawDrillSymbol( item, aLayer );
807 else
808 draw( static_cast<const PAD*>( item ), aLayer );
809
810 break;
811
812 case PCB_SHAPE_T:
813 draw( static_cast<const PCB_SHAPE*>( item ), aLayer );
814 break;
815
817 draw( static_cast<const PCB_REFERENCE_IMAGE*>( item ), aLayer );
818 break;
819
820 case PCB_FIELD_T:
821 draw( static_cast<const PCB_FIELD*>( item ), aLayer );
822 break;
823
824 case PCB_TEXT_T:
825 draw( static_cast<const PCB_TEXT*>( item ), aLayer );
826 break;
827
828 case PCB_TEXTBOX_T:
829 draw( static_cast<const PCB_TEXTBOX*>( item ), aLayer );
830 break;
831
832 case PCB_TABLE_T:
833 draw( static_cast<const PCB_TABLE*>( item ), aLayer );
834 break;
835
837 {
838 const PCB_DRILL_CHART* chart = static_cast<const PCB_DRILL_CHART*>( item );
839
840 // Before the table, whose selection wash is drawn last and would otherwise bury the
841 // marks the way it does not bury the cell text
842 drawChartSymbols( chart, aLayer );
843 draw( static_cast<const PCB_TABLE*>( item ), aLayer );
844 break;
845 }
846
847 case PCB_DRILL_MAP_T:
848 draw( static_cast<const PCB_DRILL_MAP*>( item ), aLayer );
849 break;
850
851 case PCB_FOOTPRINT_T:
852 draw( static_cast<const FOOTPRINT*>( item ), aLayer );
853 break;
854
855 case PCB_GROUP_T:
856 draw( static_cast<const PCB_GROUP*>( item ), aLayer );
857 break;
858
859 case PCB_ZONE_T:
860 draw( static_cast<const ZONE*>( item ), aLayer );
861 break;
862
864 case PCB_DIM_CENTER_T:
865 case PCB_DIM_RADIAL_T:
867 case PCB_DIM_LEADER_T:
868 draw( static_cast<const PCB_DIMENSION_BASE*>( item ), aLayer );
869 break;
870
871 case PCB_BARCODE_T:
872 draw( static_cast<const PCB_BARCODE*>( item ), aLayer );
873 break;
874
875 case PCB_TARGET_T:
876 draw( static_cast<const PCB_TARGET*>( item ) );
877 break;
878
879 case PCB_POINT_T:
880 draw( static_cast<const PCB_POINT*>( item ), aLayer );
881 break;
882
883 case PCB_GRID_ITEM_T:
884 draw( static_cast<const PCB_GRID_ITEM*>( item ), aLayer );
885 break;
886
887 case PCB_MARKER_T:
888 draw( static_cast<const PCB_MARKER*>( item ), aLayer );
889 break;
890
892 draw( static_cast<const PCB_BOARD_OUTLINE*>( item ), aLayer );
893 break;
894
895 default:
896 // Painter does not know how to draw the object
897 return false;
898 }
899
900 // Draw bounding boxes after drawing objects so they can be seen.
901 if( m_pcbSettings.GetDrawBoundingBoxes() )
902 {
903 // Show bounding boxes of painted objects for debugging.
904 BOX2I box = item->GetBoundingBox();
905
906 m_gal->SetIsFill( false );
907 m_gal->SetIsStroke( true );
908
909 if( item->Type() == PCB_FOOTPRINT_T )
910 m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) : COLOR4D( MAGENTA ) );
911 else
912 m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) : COLOR4D( 0.4, 0.4, 0.4, 1 ) );
913
914 m_gal->SetLineWidth( 1 );
915 m_gal->DrawRectangle( box.GetOrigin(), box.GetEnd() );
916
917 if( item->Type() == PCB_FOOTPRINT_T )
918 {
919 m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) : COLOR4D( CYAN ) );
920
921 const FOOTPRINT* fp = static_cast<const FOOTPRINT*>( item );
922
923 if( fp )
924 {
925 const SHAPE_POLY_SET& convex = fp->GetBoundingHull();
926
927 m_gal->DrawPolyline( convex.COutline( 0 ) );
928 }
929 }
930 }
931
932 return true;
933}
934
935
936void PCB_PAINTER::draw( const PCB_TRACK* aTrack, int aLayer )
937{
938 const VECTOR2I& start = aTrack->GetStart();
939 const VECTOR2I& end = aTrack->GetEnd();
940 int track_width = aTrack->GetWidth();
941 COLOR4D color = m_pcbSettings.GetColor( aTrack, aLayer );
942
943 // If a chain highlight is active and the track belongs to the highlighted chain, and the chain
944 // has a colour override configured on the board, prefer that colour. Only do this when we're
945 // drawing the actual copper (not netname labels, clearance outlines, etc.).
946 if( IsCopperLayer( aLayer ) && !m_pcbSettings.m_highlightedNetChain.IsEmpty() )
947 {
948 if( NETINFO_ITEM* netinfo = aTrack->GetNet() )
949 {
950 if( netinfo->GetNetChain() == m_pcbSettings.m_highlightedNetChain )
951 {
952 if( const BOARD* board = aTrack->GetBoard() )
953 {
954 COLOR4D chainColor = board->GetNetChainColor( m_pcbSettings.m_highlightedNetChain );
955
956 if( chainColor != COLOR4D::UNSPECIFIED )
957 color = chainColor.WithAlpha( color.a );
958 }
959 }
960 }
961 }
962
963 if( IsNetnameLayer( aLayer ) )
964 {
965 if( !pcbconfig() || pcbconfig()->m_Display.m_NetNames < 2 )
966 return;
967
968 if( aTrack->GetNetCode() <= NETINFO_LIST::UNCONNECTED )
969 return;
970
971 SHAPE_SEGMENT trackShape( { aTrack->GetStart(), aTrack->GetEnd() }, aTrack->GetWidth() );
972 renderNetNameForSegment( trackShape, color, aTrack->GetDisplayNetname() );
973 return;
974 }
975 else if( IsCopperLayer( aLayer ) || IsSolderMaskLayer( aLayer ) || aLayer == LAYER_LOCKED_ITEM_SHADOW )
976 {
977 bool outline_mode = pcbconfig()
979 && aLayer != LAYER_LOCKED_ITEM_SHADOW;
980
981 if( IsSolderMaskLayer( aLayer ) )
982 track_width = track_width + aTrack->GetSolderMaskExpansion() * 2;
983
984 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
985 track_width = track_width + m_lockedShadowMargin;
986
987 if( !outline_mode )
988 {
989 // Tracks are the most common primitive, so they bypass the generic fill and stroke path
990 m_gal->DrawTrack( VECTOR2D( start ), VECTOR2D( end ), track_width, color );
991 }
992 else
993 {
994 m_gal->SetStrokeColor( color );
995 m_gal->SetIsStroke( true );
996 m_gal->SetIsFill( false );
997 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
998 m_gal->DrawSegment( start, end, track_width );
999 }
1000 }
1001
1002 // Clearance lines
1003 if( IsClearanceLayer( aLayer )
1004 && pcbconfig()
1005 && pcbconfig()->m_Display.m_TrackClearance == SHOW_WITH_VIA_ALWAYS
1006 && !m_pcbSettings.m_isPrinting )
1007 {
1008 const PCB_LAYER_ID copperLayerForClearance = ToLAYER_ID( aLayer - LAYER_CLEARANCE_START );
1009
1010 int clearance = aTrack->GetOwnClearance( copperLayerForClearance );
1011
1012 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1013 m_gal->SetIsFill( false );
1014 m_gal->SetIsStroke( true );
1015 m_gal->SetStrokeColor( color );
1016 m_gal->DrawSegment( start, end, track_width + clearance * 2 );
1017 }
1018}
1019
1020
1022 const wxString& aNetName ) const
1023{
1024 // When drawing netnames, clip the track to the viewport
1025 BOX2D viewport;
1026 VECTOR2D screenSize = m_gal->GetScreenPixelSize();
1027 const MATRIX3x3D& matrix = m_gal->GetScreenWorldMatrix();
1028
1029 viewport.SetOrigin( VECTOR2D( matrix * VECTOR2D( 0, 0 ) ) );
1030 viewport.SetEnd( VECTOR2D( matrix * screenSize ) );
1031 viewport.Normalize();
1032
1033 // Calc the approximate size of the netname (assume square chars)
1034 int nameSize = (int) aNetName.size() * aSeg.GetWidth();
1035
1036 // Check if the track is long enough to have a netname displayed
1037 if( aSeg.GetSeg().SquaredLength() < SEG::Square( nameSize ) )
1038 return;
1039
1040 double textSize = aSeg.GetWidth();
1041 double penWidth = textSize / 12.0;
1042 EDA_ANGLE textOrientation;
1043 int num_names = 1;
1044
1045 VECTOR2I start = aSeg.GetSeg().A;
1046 VECTOR2I end = aSeg.GetSeg().B;
1047 VECTOR2D segV = end - start;
1048
1049 if( end.y == start.y ) // horizontal
1050 {
1051 textOrientation = ANGLE_HORIZONTAL;
1052 num_names = std::max( num_names, KiROUND( aSeg.GetSeg().Length() / viewport.GetWidth() ) );
1053 }
1054 else if( end.x == start.x ) // vertical
1055 {
1056 textOrientation = ANGLE_VERTICAL;
1057 num_names = std::max( num_names, KiROUND( aSeg.GetSeg().Length() / viewport.GetHeight() ) );
1058 }
1059 else
1060 {
1061 textOrientation = -EDA_ANGLE( segV );
1062 textOrientation.Normalize90();
1063
1064 double min_size = std::min( viewport.GetWidth(), viewport.GetHeight() );
1065 num_names = std::max( num_names, KiROUND( aSeg.GetSeg().Length() / ( M_SQRT2 * min_size ) ) );
1066 }
1067
1068 m_gal->SetIsStroke( true );
1069 m_gal->SetIsFill( false );
1070 m_gal->SetStrokeColor( aColor );
1071 m_gal->SetLineWidth( penWidth );
1072 m_gal->SetFontBold( false );
1073 m_gal->SetFontItalic( false );
1074 m_gal->SetFontUnderlined( false );
1075 m_gal->SetTextMirrored( false );
1076 m_gal->SetGlyphSize( VECTOR2D( textSize * 0.55, textSize * 0.55 ) );
1077 m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER );
1078 m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER );
1079
1080 int divisions = num_names + 1;
1081
1082 for( int ii = 1; ii < divisions; ++ii )
1083 {
1084 VECTOR2I textPosition = start + segV * ( (double) ii / divisions );
1085
1086 if( viewport.Contains( textPosition ) )
1087 m_gal->BitmapText( aNetName, textPosition, textOrientation );
1088 }
1089}
1090
1091
1092void PCB_PAINTER::draw( const PCB_ARC* aArc, int aLayer )
1093{
1094 COLOR4D color = m_pcbSettings.GetColor( aArc, aLayer );
1095
1096 // If a chain highlight is active and the track belongs to the highlighted chain, and the chain
1097 // has a colour override configured on the board, prefer that colour. Only do this when we're
1098 // drawing the actual copper (not netname labels, clearance outlines, etc.).
1099 if( IsCopperLayer( aLayer ) && !m_pcbSettings.m_highlightedNetChain.IsEmpty() )
1100 {
1101 if( NETINFO_ITEM* netinfo = aArc->GetNet() )
1102 {
1103 if( netinfo->GetNetChain() == m_pcbSettings.m_highlightedNetChain )
1104 {
1105 if( const BOARD* board = aArc->GetBoard() )
1106 {
1107 COLOR4D chainColor = board->GetNetChainColor( m_pcbSettings.m_highlightedNetChain );
1108
1109 if( chainColor != COLOR4D::UNSPECIFIED )
1110 color = chainColor.WithAlpha( color.a );
1111 }
1112 }
1113 }
1114 }
1115
1116 VECTOR2D center( aArc->GetCenter() );
1117 int width = aArc->GetWidth();
1118 double radius = aArc->GetRadius();
1119 EDA_ANGLE start_angle = aArc->GetArcAngleStart();
1120 EDA_ANGLE angle = aArc->GetAngle();
1121
1122 // GetRadius() clamps a runaway centre but GetCenter() does not, thus the two disagree and
1123 // the copper draws far from the track. The plotter substitutes the chord for this reason.
1124 bool degenerate = aArc->IsDegenerated( 10 /* in IU */ );
1125
1126 if( IsNetnameLayer( aLayer ) )
1127 {
1128 if( !pcbconfig() || pcbconfig()->m_Display.m_NetNames < 2 )
1129 return;
1130
1131 if( aArc->GetNetCode() <= NETINFO_LIST::UNCONNECTED )
1132 return;
1133
1134 const wxString& netname = aArc->GetDisplayNetname();
1135
1136 if( netname.IsEmpty() )
1137 return;
1138
1139 // Radius and centre disagree here, thus the arc length and the tangent are meaningless
1140 if( degenerate )
1141 {
1142 const SHAPE_SEGMENT chord( { aArc->GetStart(), aArc->GetEnd() }, width );
1143 renderNetNameForSegment( chord, color, netname );
1144 return;
1145 }
1146
1147 // Calc the approximate size of the netname (assume square chars)
1148 int nameSize = (int) netname.size() * width;
1149
1150 // Arc length must accommodate the label length.
1151 if( aArc->GetLength() < nameSize )
1152 return;
1153
1154 // Tangent at the arc midpoint is perpendicular to the radius there.
1155 VECTOR2I midPt = aArc->GetMid();
1156 VECTOR2D radial = VECTOR2D( midPt ) - center;
1157 EDA_ANGLE textOrientation( VECTOR2D( -radial.y, radial.x ) );
1158 textOrientation = -textOrientation;
1159 textOrientation.Normalize90();
1160
1161 double textSize = width;
1162 double penWidth = textSize / 12.0;
1163
1164 m_gal->SetIsStroke( true );
1165 m_gal->SetIsFill( false );
1166 m_gal->SetStrokeColor( color );
1167 m_gal->SetLineWidth( penWidth );
1168 m_gal->SetFontBold( false );
1169 m_gal->SetFontItalic( false );
1170 m_gal->SetFontUnderlined( false );
1171 m_gal->SetTextMirrored( false );
1172 m_gal->SetGlyphSize( VECTOR2D( textSize * 0.55, textSize * 0.55 ) );
1173 m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER );
1174 m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER );
1175
1176 m_gal->BitmapText( netname, midPt, textOrientation );
1177 return;
1178 }
1179 else if( IsCopperLayer( aLayer ) || IsSolderMaskLayer( aLayer ) || aLayer == LAYER_LOCKED_ITEM_SHADOW )
1180 {
1181 // Draw a regular track
1182 bool outline_mode = pcbconfig()
1184 && aLayer != LAYER_LOCKED_ITEM_SHADOW;
1185 m_gal->SetStrokeColor( color );
1186 m_gal->SetFillColor( color );
1187 m_gal->SetIsStroke( outline_mode );
1188 m_gal->SetIsFill( not outline_mode );
1189 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1190
1191 if( IsSolderMaskLayer( aLayer ) )
1192 width = width + aArc->GetSolderMaskExpansion() * 2;
1193
1194 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
1195 width = width + m_lockedShadowMargin;
1196
1197 if( degenerate )
1198 m_gal->DrawSegment( aArc->GetStart(), aArc->GetEnd(), width );
1199 else
1200 m_gal->DrawArcSegment( center, radius, start_angle, angle, width, m_maxError );
1201 }
1202
1203 // Clearance lines
1204 if( IsClearanceLayer( aLayer )
1205 && pcbconfig()
1206 && pcbconfig()->m_Display.m_TrackClearance == SHOW_WITH_VIA_ALWAYS
1207 && !m_pcbSettings.m_isPrinting )
1208 {
1209 const PCB_LAYER_ID copperLayerForClearance = ToLAYER_ID( aLayer - LAYER_CLEARANCE_START );
1210
1211 int clearance = aArc->GetOwnClearance( copperLayerForClearance );
1212
1213 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1214 m_gal->SetIsFill( false );
1215 m_gal->SetIsStroke( true );
1216 m_gal->SetStrokeColor( color );
1217
1218 if( degenerate )
1219 m_gal->DrawSegment( aArc->GetStart(), aArc->GetEnd(), width + clearance * 2 );
1220 else
1221 m_gal->DrawArcSegment( center, radius, start_angle, angle, width + clearance * 2, m_maxError );
1222 }
1223
1224#if 0
1225 // Debug only: enable this code only to test the TransformArcToPolygon function and display the polygon
1226 // outline created by it.
1227 // arcs on F_Cu are approximated with ERROR_INSIDE, others with ERROR_OUTSIDE
1228 SHAPE_POLY_SET cornerBuffer;
1230 TransformArcToPolygon( cornerBuffer, aArc->GetStart(), aArc->GetMid(), aArc->GetEnd(), width,
1231 m_maxError, errorloc );
1232 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1233 m_gal->SetIsFill( false );
1234 m_gal->SetIsStroke( true );
1235 m_gal->SetStrokeColor( COLOR4D( 0, 0, 1.0, 1.0 ) );
1236 m_gal->DrawPolygon( cornerBuffer );
1237#endif
1238
1239#if 0
1240 // Debug only: enable this code only to test the arc geometry.
1241 // Draw 3 lines from arc center to arc start, arc middle, arc end to show how the arc is defined
1242 SHAPE_ARC arc( aArc->GetStart(), aArc->GetMid(), aArc->GetEnd(), m_pcbSettings.m_outlineWidth );
1243 m_gal->SetIsFill( false );
1244 m_gal->SetIsStroke( true );
1245 m_gal->SetStrokeColor( COLOR4D( 0, 0, 1.0, 1.0 ) );
1246 m_gal->DrawSegment( arc.GetStart(), arc.GetCenter(), m_pcbSettings.m_outlineWidth );
1247 m_gal->DrawSegment( aArc->GetFocusPosition(), arc.GetCenter(), m_pcbSettings.m_outlineWidth );
1248 m_gal->DrawSegment( arc.GetEnd(), arc.GetCenter(), m_pcbSettings.m_outlineWidth );
1249#endif
1250
1251#if 0
1252 // Debug only: enable this code only to test the SHAPE_ARC::ConvertToPolyline function and display the
1253 // polyline created by it.
1254 SHAPE_ARC arc( aArc->GetCenter(), aArc->GetStart(), aArc->GetAngle(), aArc->GetWidth() );
1256 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1257 m_gal->SetIsFill( false );
1258 m_gal->SetIsStroke( true );
1259 m_gal->SetStrokeColor( COLOR4D( 0.3, 0.2, 0.5, 1.0 ) );
1260
1261 for( int idx = 1; idx < arcSpine.PointCount(); idx++ )
1262 m_gal->DrawSegment( arcSpine.CPoint( idx-1 ), arcSpine.CPoint( idx ), aArc->GetWidth() );
1263#endif
1264}
1265
1266
1267static bool viaHoleShowsLayerPair( const PCB_VIA* aVia )
1268{
1269 PCB_LAYER_ID layerTop, layerBottom;
1270 aVia->LayerPair( &layerTop, &layerBottom );
1271
1272 return aVia->GetViaType() == VIATYPE::BLIND || aVia->GetViaType() == VIATYPE::BURIED
1273 || ( aVia->GetViaType() == VIATYPE::MICROVIA && ( layerTop != F_Cu || layerBottom != B_Cu ) );
1274}
1275
1276
1277bool PCB_PAINTER::HasUniformColor( const VIEW_ITEM* aItem, int aLayer ) const
1278{
1279 if( !aItem->IsBOARD_ITEM() )
1280 return true;
1281
1282 const bool holeLayer = aLayer == LAYER_VIA_HOLES || aLayer == LAYER_VIA_HOLEWALLS;
1283
1284 switch( static_cast<const BOARD_ITEM*>( aItem )->Type() )
1285 {
1286 case PCB_PAD_T:
1287 {
1288 const PAD* pad = static_cast<const PAD*>( aItem );
1289 const NETINFO_ITEM* net = pad->GetNet();
1290
1291 // A net's terminal pads gain and lose their emphasis box with the net highlight
1292 if( net && ( net->GetTerminalPad( 0 ) == pad || net->GetTerminalPad( 1 ) == pad ) )
1293 return false;
1294
1295 if( aLayer != LAYER_PAD_HOLEWALLS )
1296 return true;
1297
1298 return pad->GetDrillSize().x <= 0 || ( pad->GetSecondaryDrillSizeX() <= 0 && pad->GetTertiaryDrillSizeX() <= 0 );
1299 }
1300
1301 case PCB_VIA_T:
1302 {
1303 const PCB_VIA* via = static_cast<const PCB_VIA*>( aItem );
1304
1305 if( !holeLayer )
1306 return true;
1307
1308 if( aLayer == LAYER_VIA_HOLES )
1309 return !viaHoleShowsLayerPair( via );
1310
1311 return via->GetSecondaryDrillSize().value_or( 0 ) <= 0 && via->GetTertiaryDrillSize().value_or( 0 ) <= 0;
1312 }
1313
1314 // A cached image takes its opacity from the settings, which only a repaint can apply
1316 return false;
1317
1318 // A zone's outline and hatch are drawn opaque whatever the zone opacity, so its outline does not
1319 // take the zone's colour
1320 case PCB_ZONE_T:
1321 return !ZoneOutlineDrawnOnLayer( zoneOutlineOnly( static_cast<const ZONE*>( aItem ) ), aLayer );
1322
1323 default:
1324 return true;
1325 }
1326}
1327
1328
1329bool PCB_PAINTER::zoneOutlineOnly( const ZONE* aZone ) const
1330{
1331 return aZone->GetIsRuleArea()
1332 || ( !aZone->IsTeardropArea() && m_pcbSettings.m_ZoneDisplayMode == ZONE_DISPLAY_MODE::SHOW_ZONE_OUTLINE );
1333}
1334
1335
1336void PCB_PAINTER::draw( const PCB_VIA* aVia, int aLayer )
1337{
1338 const BOARD* board = aVia->GetBoard();
1339 COLOR4D color = m_pcbSettings.GetColor( aVia, aLayer );
1340 VECTOR2D center( aVia->GetStart() );
1341
1342 // draw hidden vias transparent not skipped so a recolour restores them without re-tessellating
1343
1344 // Chain highlight colour override for copper/hole layers.
1345 if( board && !m_pcbSettings.m_highlightedNetChain.IsEmpty()
1346 && ( IsCopperLayer( aLayer ) || IsViaCopperLayer( aLayer )
1347 || aLayer == LAYER_VIA_HOLES ) )
1348 {
1349 if( NETINFO_ITEM* netinfo = aVia->GetNet() )
1350 {
1351 if( netinfo->GetNetChain() == m_pcbSettings.m_highlightedNetChain )
1352 {
1353 COLOR4D chainColor = board->GetNetChainColor( m_pcbSettings.m_highlightedNetChain );
1354
1355 if( chainColor != COLOR4D::UNSPECIFIED )
1356 color = chainColor.WithAlpha( color.a );
1357 }
1358 }
1359 }
1360
1361 const int copperLayer = IsViaCopperLayer( aLayer ) ? aLayer - LAYER_VIA_COPPER_START : aLayer;
1362
1363 PCB_LAYER_ID currentLayer = ToLAYER_ID( copperLayer );
1364 PCB_LAYER_ID layerTop, layerBottom;
1365 aVia->LayerPair( &layerTop, &layerBottom );
1366
1367 // Blind/buried vias (and microvias) will use different hole and label rendering
1368 bool isBlindBuried = viaHoleShowsLayerPair( aVia );
1369
1370 // Draw description layer
1371 if( IsNetnameLayer( aLayer ) )
1372 {
1373 VECTOR2D position( center );
1374
1375 // Is anything that we can display enabled (netname and/or layers ids)?
1376 bool showNets = pcbconfig() && pcbconfig()->m_Display.m_NetNames != 0
1377 && !aVia->GetNetname().empty();
1378 bool showLayers = aVia->GetViaType() != VIATYPE::THROUGH;
1379
1380 if( !showNets && !showLayers )
1381 return;
1382
1383 double maxSize = PCB_RENDER_SETTINGS::MAX_FONT_SIZE;
1384 double size = aVia->GetWidth( currentLayer );
1385
1386 // Font size limits
1387 if( size > maxSize )
1388 size = maxSize;
1389
1390 m_gal->Save();
1391 m_gal->Translate( position );
1392
1393 // Default font settings
1394 m_gal->ResetTextAttributes();
1395 m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER );
1396 m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER );
1397 m_gal->SetFontBold( false );
1398 m_gal->SetFontItalic( false );
1399 m_gal->SetFontUnderlined( false );
1400 m_gal->SetTextMirrored( false );
1401 m_gal->SetStrokeColor( m_pcbSettings.GetColor( aVia, aLayer ) );
1402 m_gal->SetIsStroke( true );
1403 m_gal->SetIsFill( false );
1404
1405 // Set the text position via position. if only one text, it is on the via position
1406 // For 2 lines, the netname is slightly below the center, and the layer IDs above
1407 // the netname
1408 VECTOR2D textpos( 0.0, 0.0 );
1409
1410 const wxString& netname = aVia->GetDisplayNetname();
1411
1412 PCB_LAYER_ID topLayerId = aVia->TopLayer();
1413 PCB_LAYER_ID bottomLayerId = aVia->BottomLayer();
1414 int topLayer; // The via top layer number (from 1 to copper layer count)
1415 int bottomLayer; // The via bottom layer number (from 1 to copper layer count)
1416
1417 switch( topLayerId )
1418 {
1419 case F_Cu: topLayer = 1; break;
1420 case B_Cu: topLayer = aVia->BoardCopperLayerCount(); break;
1421 default: topLayer = (topLayerId - B_Cu)/2 + 1; break;
1422 }
1423
1424 switch( bottomLayerId )
1425 {
1426 case F_Cu: bottomLayer = 1; break;
1427 case B_Cu: bottomLayer = aVia->BoardCopperLayerCount(); break;
1428 default: bottomLayer = (bottomLayerId - B_Cu)/2 + 1; break;
1429 }
1430
1431 wxString layerIds;
1432#if wxUSE_UNICODE_WCHAR
1433 layerIds << std::to_wstring( topLayer ) << L'-' << std::to_wstring( bottomLayer );
1434#else
1435 layerIds << std::to_string( topLayer ) << '-' << std::to_string( bottomLayer );
1436#endif
1437
1438 // a good size is set room for at least 6 chars, to be able to print 2 lines of text,
1439 // or at least 3 chars for only the netname
1440 // (The layerIds string has 5 chars max)
1441 int minCharCnt = showLayers ? 6 : 3;
1442
1443 // approximate the size of netname and layerIds text:
1444 double tsize = 1.5 * size / std::max( PrintableCharCount( netname ), minCharCnt );
1445 tsize = std::min( tsize, size );
1446
1447 // Use a smaller text size to handle interline, pen size..
1448 tsize *= 0.75;
1449 VECTOR2D namesize( tsize, tsize );
1450
1451 // For 2 lines, adjust the text pos (move it a small amount to the bottom)
1452 if( showLayers && showNets )
1453 textpos.y += ( tsize * 1.3 )/ 2;
1454
1455 m_gal->SetGlyphSize( namesize );
1456 m_gal->SetLineWidth( namesize.x / 10.0 );
1457
1458 if( showNets )
1459 m_gal->BitmapText( netname, textpos, ANGLE_HORIZONTAL );
1460
1461 if( showLayers )
1462 {
1463 if( showNets )
1464 textpos.y -= tsize * 1.3;
1465
1466 m_gal->BitmapText( layerIds, textpos, ANGLE_HORIZONTAL );
1467 }
1468
1469 m_gal->Restore();
1470
1471 return;
1472 }
1473
1474 bool outline_mode = pcbconfig() && !pcbconfig()->m_Display.m_DisplayViaFill;
1475
1476 m_gal->SetStrokeColor( color );
1477 m_gal->SetFillColor( color );
1478 m_gal->SetIsStroke( true );
1479 m_gal->SetIsFill( false );
1480
1481 if( outline_mode )
1482 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1483
1484 if( aLayer == LAYER_VIA_HOLEWALLS )
1485 {
1486 double thickness =
1488 double drillRadius = getViaDrillSize( aVia ) / 2.0;
1489 double maxRadius = aVia->GetWidth( layerTop ) / 2.0;
1490 double radius = drillRadius + thickness;
1491
1492 // Clamp the hole wall so it doesn't extend beyond the via's copper
1493 if( radius > maxRadius )
1494 {
1495 radius = maxRadius;
1496 thickness = radius - drillRadius;
1497 }
1498
1499 if( thickness <= 0 )
1500 return;
1501
1502 if( !outline_mode )
1503 {
1504 m_gal->SetLineWidth( thickness );
1505 radius -= thickness / 2.0;
1506 }
1507
1508 // Underpaint the hole so that there aren't artifacts at its edge
1509 m_gal->SetIsFill( true );
1510
1511 m_gal->DrawCircle( center, radius );
1512
1513 // Draw backdrill indicators (semi-circles extending into the hole) on top of the
1514 // hole wall so they remain visible regardless of layer rendering order
1515 if( !m_pcbSettings.IsPrinting() )
1516 {
1517 std::optional<int> secDrill = aVia->GetSecondaryDrillSize();
1518 std::optional<int> terDrill = aVia->GetTertiaryDrillSize();
1519
1520 if( secDrill.value_or( 0 ) > 0 )
1521 {
1523 aVia->GetSecondaryDrillEndLayer() );
1524 }
1525
1526 if( terDrill.value_or( 0 ) > 0 )
1527 {
1528 drawBackdrillIndicator( aVia, center, *terDrill, aVia->GetTertiaryDrillStartLayer(),
1529 aVia->GetTertiaryDrillEndLayer() );
1530 }
1531 }
1532 }
1533 else if( aLayer == LAYER_VIA_HOLES )
1534 {
1535 double radius = getViaDrillSize( aVia ) / 2.0;
1536
1537 m_gal->SetIsStroke( false );
1538 m_gal->SetIsFill( true );
1539
1540 if( isBlindBuried && !m_pcbSettings.IsPrinting() )
1541 {
1542 m_gal->SetIsStroke( false );
1543 m_gal->SetIsFill( true );
1544
1545 m_gal->SetFillColor( m_pcbSettings.GetColor( aVia, layerTop ) );
1546 m_gal->DrawArc( center, radius, EDA_ANGLE( 180, DEGREES_T ), EDA_ANGLE( 180, DEGREES_T ) );
1547
1548 m_gal->SetFillColor( m_pcbSettings.GetColor( aVia, layerBottom ) );
1549 m_gal->DrawArc( center, radius, EDA_ANGLE( 0, DEGREES_T ), EDA_ANGLE( 180, DEGREES_T ) );
1550 }
1551 else
1552 {
1553 m_gal->DrawCircle( center, radius );
1554 }
1555
1556 }
1557 else if( ( aLayer == F_Mask && aVia->IsOnLayer( F_Mask ) )
1558 || ( aLayer == B_Mask && aVia->IsOnLayer( B_Mask ) ) )
1559 {
1560 int margin = board ? board->GetDesignSettings().m_SolderMaskExpansion : 0;
1561
1562 m_gal->SetIsFill( true );
1563 m_gal->SetIsStroke( false );
1564
1565 m_gal->SetLineWidth( margin );
1566 m_gal->DrawCircle( center, aVia->GetWidth( currentLayer ) / 2.0 + margin );
1567 }
1568 else if( m_pcbSettings.IsPrinting() || IsCopperLayer( currentLayer ) )
1569 {
1570 int annular_width = KiROUND( ( aVia->GetWidth( currentLayer ) - getViaDrillSize( aVia ) ) / 2.0 );
1571 double radius = aVia->GetWidth( currentLayer ) / 2.0;
1572 bool draw = false;
1573
1574 if( m_pcbSettings.IsPrinting() )
1575 {
1576 draw = aVia->FlashLayer( m_pcbSettings.GetPrintLayers() );
1577 }
1578 else if( aVia->IsSelected() )
1579 {
1580 draw = true;
1581 }
1582 else if( !board || aVia->FlashLayer( board->GetVisibleLayers() & board->GetEnabledLayers() ) )
1583 {
1584 draw = true;
1585 }
1586
1587 if( !aVia->FlashLayer( currentLayer ) )
1588 draw = false;
1589
1590 if( !outline_mode )
1591 {
1592 m_gal->SetLineWidth( annular_width );
1593 radius -= annular_width / 2.0;
1594 }
1595
1596 if( draw )
1597 m_gal->DrawCircle( center, radius );
1598
1599 // Draw post-machining indicator if this layer is post-machined
1600 if( !m_pcbSettings.IsPrinting() && draw )
1601 {
1602 drawPostMachiningIndicator( aVia, center, currentLayer );
1603 }
1604 }
1605 else if( aLayer == LAYER_LOCKED_ITEM_SHADOW ) // draw a ring around the via
1606 {
1607 m_gal->SetLineWidth( m_lockedShadowMargin );
1608
1609 m_gal->DrawCircle( center, ( aVia->GetWidth( currentLayer ) + m_lockedShadowMargin ) / 2.0 );
1610 }
1611
1612 // Clearance lines
1613 if( IsClearanceLayer( aLayer )
1614 && pcbconfig()
1615 && pcbconfig()->m_Display.m_TrackClearance == SHOW_WITH_VIA_ALWAYS
1616 && !m_pcbSettings.m_isPrinting )
1617 {
1618 const PCB_LAYER_ID copperLayerForClearance = ToLAYER_ID( aLayer - LAYER_CLEARANCE_START );
1619
1620 double radius;
1621
1622 if( aVia->FlashLayer( copperLayerForClearance ) )
1623 radius = aVia->GetWidth( copperLayerForClearance ) / 2.0;
1624 else
1626
1627 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1628 m_gal->SetIsFill( false );
1629 m_gal->SetIsStroke( true );
1630 m_gal->SetStrokeColor( color );
1631 m_gal->DrawCircle( center, radius + aVia->GetOwnClearance( copperLayerForClearance ) );
1632 }
1633}
1634
1635
1636// A shape mark has no text, so the cells are empty and the symbol column would otherwise
1637// print blank on every chart using the default policy
1638void PCB_PAINTER::drawChartSymbols( const PCB_DRILL_CHART* aChart, int aLayer )
1639{
1640 if( aChart->GetSymbolColumn() < 0 || aChart->RowShapes().empty() )
1641 return;
1642
1643 const BOARD* board = aChart->GetBoard();
1644
1645 if( !board )
1646 return;
1647
1649
1650 m_gal->SetIsFill( false );
1651 m_gal->SetIsStroke( true );
1652 m_gal->SetStrokeColor( m_pcbSettings.GetColor( aChart, aLayer ) );
1653 m_gal->SetLineWidth( profile.GetSymbolWidth() );
1654
1655 for( const auto& [row, shapeIndex] : aChart->RowShapes() )
1656 {
1657 const PCB_TABLECELL* cell = aChart->GetCell( row, aChart->GetSymbolColumn() );
1658
1659 if( !cell )
1660 continue;
1661
1662 const BOX2I box = cell->GetBoundingBox();
1663 const VECTOR2I centre = box.GetCenter();
1664
1665 // Fitted to the cell so a tall symbol size cannot spill into the neighbouring row
1666 const int radius = std::min<int>( profile.GetSymbolSize() / 2,
1667 std::min( box.GetWidth(), box.GetHeight() ) / 3 );
1668
1669 if( radius <= 0 )
1670 continue;
1671
1672 const unsigned shape = DRILL_MARKERS::CuratedShape( shapeIndex );
1673
1674 for( const DRILL_MARKERS::MARKER_PART& part :
1675 DRILL_MARKERS::BuildMarker( centre, radius, shape ) )
1676 {
1677 switch( part.m_Type )
1678 {
1680 m_gal->DrawLine( part.m_Points.front(), part.m_Points.back() );
1681 break;
1682
1684 for( size_t ii = 0; ii + 1 < part.m_Points.size(); ++ii )
1685 m_gal->DrawLine( part.m_Points[ii], part.m_Points[ii + 1] );
1686
1687 break;
1688
1690 m_gal->DrawCircle( centre, part.m_Radius );
1691 break;
1692 }
1693 }
1694 }
1695}
1696
1697
1698// The holes draw the marks, so without the outline and anchor drawn here there would be
1699// nothing on screen to click and a placed map could not be selected or deleted
1700void PCB_PAINTER::draw( const PCB_DRILL_MAP* aMap, int aLayer )
1701{
1702 const BOARD* board = aMap->GetBoard();
1703
1704 // The plotted width, not the sketch width, because the outline is artwork the map really
1705 // does emit rather than an editing decoration
1706 const int outlineWidth =
1707 board ? std::max<int>( board->GetDesignSettings().GetDrillSymbolProfile().GetSymbolWidth(), 1 ) : 1;
1708
1709 m_gal->SetIsFill( false );
1710 m_gal->SetIsStroke( true );
1711 m_gal->SetStrokeColor( m_pcbSettings.GetColor( aMap, aLayer ) );
1712 m_gal->SetLineWidth( outlineWidth );
1713
1714 const std::shared_ptr<const SHAPE_POLY_SET> outlines = aMap->GetBoardOutlines();
1715
1716 for( int ii = 0; ii < outlines->OutlineCount(); ++ii )
1717 {
1718 m_gal->DrawSegmentChain( outlines->COutline( ii ), outlineWidth );
1719
1720 for( int jj = 0; jj < outlines->HoleCount( ii ); ++jj )
1721 m_gal->DrawSegmentChain( outlines->CHole( ii, jj ), outlineWidth );
1722 }
1723
1724 // Every hole's view bounds were computed for the offset the map had when the drag began,
1725 // so the holes cannot draw the marks where they are now without being culled
1726 if( aMap->IsMoving() && board )
1727 {
1728 const std::shared_ptr<const DRILL_SYMBOL_CACHE> cache = board->DrillSymbolCache();
1729 const COLOR4D markColor = m_pcbSettings.GetColor( aMap, aLayer );
1730
1731 for( const auto& [itemId, entries] : cache->m_ByItem )
1732 drawDrillMarks( aMap, entries, markColor, aMap->GetFontMetrics() );
1733 }
1734
1735 if( !aMap->IsSelected() && !aMap->IsBrightened() )
1736 return;
1737
1738 const int thickness = std::max<int>( m_pcbSettings.m_outlineWidth, 1 );
1739
1740 m_gal->SetLineWidth( thickness );
1741
1742 const BOX2I box = aMap->GetBoundingBox();
1743
1744 if( box.GetWidth() <= 0 || box.GetHeight() <= 0 )
1745 return;
1746
1747 SHAPE_RECT rect( box );
1748
1750 [&]( const VECTOR2I& a, const VECTOR2I& b )
1751 {
1752 m_gal->DrawSegment( a, b, thickness );
1753 } );
1754}
1755
1756
1757// The mark comes from the board's shared symbol profile, so a symbol on screen means the
1758// same hole as that symbol in a plotted chart
1759void PCB_PAINTER::drawDrillSymbol( const BOARD_ITEM* aItem, int aLayer )
1760{
1761 const BOARD* board = aItem->GetBoard();
1762
1763 if( !board )
1764 return;
1765
1766 const std::vector<const PCB_DRILL_MAP*> maps =
1768
1769 if( maps.empty() )
1770 return;
1771
1772 // Held for the duration, because another thread may publish a new snapshot mid-draw
1773 const std::shared_ptr<const DRILL_SYMBOL_CACHE> cache = board->DrillSymbolCache();
1774
1775 const auto it = cache->m_ByItem.find( aItem->m_Uuid );
1776
1777 if( it == cache->m_ByItem.end() )
1778 {
1779 return;
1780 }
1781
1782 const COLOR4D color = m_pcbSettings.GetColor( aItem, aLayer );
1783
1784 // Several maps can share a layer. The UI prevents it but a parsed, pasted or
1785 // API-created board need not
1786 for( const PCB_DRILL_MAP* map : maps )
1787 {
1788 // A map being dragged draws its own marks. The hole's view bounds still describe
1789 // where they were, so drawing them from here would have them culled mid-drag.
1790 if( map->IsMoving() )
1791 continue;
1792
1793 drawDrillMarks( map, it->second, color, aItem->GetFontMetrics() );
1794 }
1795}
1796
1797
1798void PCB_PAINTER::drawDrillMarks( const PCB_DRILL_MAP* aMap, const std::vector<DRILL_SYMBOL_ENTRY>& aEntries,
1799 const COLOR4D& aColor, const KIFONT::METRICS& aFontMetrics )
1800{
1801 const BOARD* board = aMap->GetBoard();
1802
1803 if( !board )
1804 return;
1805
1807
1808 const int symbolSize = aMap->GetSymbolSize();
1809 const int radius = symbolSize / 2;
1810
1811 for( const DRILL_SYMBOL_ENTRY& entry : aEntries )
1812 {
1813 if( !aMap->GetAllSpans() && !( entry.m_Span == aMap->GetSpan() ) )
1814 continue;
1815
1816 // The mark is displaced from the hole it reports. The hole itself never moves
1817 const VECTOR2I pos = entry.m_Position + aMap->GetOffset();
1818
1819 m_gal->SetIsFill( false );
1820 m_gal->SetIsStroke( true );
1821 m_gal->SetStrokeColor( aColor );
1822 m_gal->SetLineWidth( profile.GetSymbolWidth() );
1823
1824 if( entry.m_Symbol.m_MarkMode == DRILL_MARK_MODE::SHAPE )
1825 {
1826 const unsigned shape = DRILL_MARKERS::CuratedShape( entry.m_Symbol.m_ShapeIndex );
1827
1828 for( const DRILL_MARKERS::MARKER_PART& part :
1829 DRILL_MARKERS::BuildMarker( pos, radius, shape ) )
1830 {
1831 switch( part.m_Type )
1832 {
1834 m_gal->DrawLine( part.m_Points.front(), part.m_Points.back() );
1835 break;
1836
1838 for( size_t ii = 0; ii + 1 < part.m_Points.size(); ++ii )
1839 m_gal->DrawLine( part.m_Points[ii], part.m_Points[ii + 1] );
1840
1841 break;
1842
1844 m_gal->DrawCircle( pos, part.m_Radius );
1845 break;
1846 }
1847 }
1848 }
1849 else
1850 {
1851 // The hole's own diameter, not the glyph size, or every hole would be labelled
1852 // with the same number and the screen would disagree with the plot
1853 const wxString text =
1854 entry.m_Symbol.m_MarkMode == DRILL_MARK_MODE::LETTER
1855 ? entry.m_Symbol.m_Letter
1856 : wxString::Format( wxT( "%.2f" ),
1857 pcbIUScale.IUTomm( entry.m_Diameter ) );
1858
1859 TEXT_ATTRIBUTES attrs;
1860 attrs.m_Size = VECTOR2I( symbolSize, symbolSize );
1861 attrs.m_StrokeWidth = profile.GetSymbolWidth();
1864
1865 m_gal->SetIsFill( true );
1866 m_gal->SetFillColor( aColor );
1867 strokeText( text, pos, attrs, aFontMetrics );
1868 }
1869
1870 if( aMap->GetGuideCross() )
1871 {
1872 // Reach matches the plotter exactly rather than 2 * radius, which loses an IU
1873 // for an odd symbol size and makes screen and plot disagree
1874 const int reach = symbolSize;
1875
1876 m_gal->SetIsFill( false );
1877 m_gal->SetIsStroke( true );
1878 m_gal->SetStrokeColor( aColor );
1879 m_gal->SetLineWidth( profile.GetSymbolWidth() );
1880 m_gal->DrawLine( pos - VECTOR2I( reach, 0 ), pos + VECTOR2I( reach, 0 ) );
1881 m_gal->DrawLine( pos - VECTOR2I( 0, reach ), pos + VECTOR2I( 0, reach ) );
1882 }
1883
1884 // A slot's true extent is the cost driver, so it is outlined as well as marked
1885 if( aMap->GetOutlineSlots() && entry.m_IsSlot )
1886 {
1887 // Built the same way the plotter builds its oval, so a rotated or tall slot is
1888 // not drawn as a horizontal one on screen
1889 VECTOR2I size = entry.m_SizeXY;
1890 EDA_ANGLE orientation = entry.m_Orientation;
1891
1892 if( size.x > size.y )
1893 {
1894 std::swap( size.x, size.y );
1895 orientation += ANGLE_90;
1896 }
1897
1898 const int half = ( size.y - size.x ) / 2;
1899 const VECTOR2I offset = VECTOR2I( 0, half );
1900 VECTOR2I start = offset;
1901 VECTOR2I end = VECTOR2I( 0, -half );
1902
1903 RotatePoint( start, orientation );
1904 RotatePoint( end, orientation );
1905
1906 m_gal->SetIsFill( false );
1907 m_gal->SetIsStroke( true );
1908 m_gal->SetStrokeColor( aColor );
1909 m_gal->SetLineWidth( profile.GetSymbolWidth() );
1910 m_gal->DrawSegment( pos + start, pos + end, size.x );
1911 }
1912 }
1913}
1914
1915
1916void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
1917{
1918 COLOR4D color = m_pcbSettings.GetColor( aPad, aLayer );
1919 const int copperLayer = IsPadCopperLayer( aLayer ) ? aLayer - LAYER_PAD_COPPER_START : aLayer;
1920 PCB_LAYER_ID pcbLayer = static_cast<PCB_LAYER_ID>( copperLayer );
1921
1922 if( IsNetnameLayer( aLayer ) )
1923 {
1924 PCBNEW_SETTINGS::DISPLAY_OPTIONS* displayOpts = pcbconfig() ? &pcbconfig()->m_Display : nullptr;
1925 wxString netname;
1926 wxString padNumber;
1927
1928 if( viewer_settings()->m_ViewersDisplay.m_DisplayPadNumbers )
1929 {
1930 padNumber = UnescapeString( aPad->GetNumber() );
1931
1932 if( dynamic_cast<CVPCB_SETTINGS*>( viewer_settings() ) )
1933 netname = aPad->GetPinFunction();
1934 }
1935
1936 if( displayOpts && !dynamic_cast<CVPCB_SETTINGS*>( viewer_settings() ) )
1937 {
1938 if( displayOpts->m_NetNames == 1 || displayOpts->m_NetNames == 3 )
1939 netname = aPad->GetDisplayNetname();
1940
1941 if( aPad->IsNoConnectPad() )
1942 netname = wxT( "x" );
1943 else if( aPad->IsFreePad() )
1944 netname = wxT( "*" );
1945 }
1946
1947 if( netname.IsEmpty() && padNumber.IsEmpty() )
1948 return;
1949
1950 BOX2I padBBox = aPad->GetBoundingBox();
1951 VECTOR2D position = padBBox.Centre();
1952 VECTOR2D padsize = VECTOR2D( padBBox.GetSize() );
1953
1954 if( aPad->IsEntered() )
1955 {
1956 FOOTPRINT* fp = aPad->GetParentFootprint();
1957
1958 // Find the number box
1959 for( const BOARD_ITEM* aItem : fp->GraphicalItems() )
1960 {
1961 if( aItem->Type() == PCB_SHAPE_T )
1962 {
1963 const PCB_SHAPE* shape = static_cast<const PCB_SHAPE*>( aItem );
1964
1965 if( shape->IsProxyItem() && shape->GetShape() == SHAPE_T::RECTANGLE )
1966 {
1967 position = shape->GetCenter();
1968 padsize = shape->GetBotRight() - shape->GetTopLeft();
1969
1970 // We normally draw a bit outside the pad, but this will be somewhat
1971 // unexpected when the user has drawn a box.
1972 padsize *= 0.9;
1973
1974 break;
1975 }
1976 }
1977 }
1978 }
1979 else if( aPad->GetShape( pcbLayer ) == PAD_SHAPE::CUSTOM )
1980 {
1981 // See if we have a number box
1982 for( const std::shared_ptr<PCB_SHAPE>& primitive : aPad->GetPrimitives( pcbLayer ) )
1983 {
1984 if( primitive->IsProxyItem() && primitive->GetShape() == SHAPE_T::RECTANGLE )
1985 {
1986 position = primitive->GetCenter();
1987 RotatePoint( position, aPad->GetOrientation() );
1988 position += aPad->ShapePos( pcbLayer );
1989
1990 padsize.x = abs( primitive->GetBotRight().x - primitive->GetTopLeft().x );
1991 padsize.y = abs( primitive->GetBotRight().y - primitive->GetTopLeft().y );
1992
1993 // We normally draw a bit outside the pad, but this will be somewhat
1994 // unexpected when the user has drawn a box.
1995 padsize *= 0.9;
1996
1997 break;
1998 }
1999 }
2000 }
2001
2002 if( aPad->GetShape( pcbLayer ) != PAD_SHAPE::CUSTOM )
2003 {
2004 // Don't allow a 45° rotation to bloat a pad's bounding box unnecessarily
2005 double limit = std::min( aPad->GetSize( pcbLayer ).x, aPad->GetSize( pcbLayer ).y ) * 1.1;
2006
2007 if( padsize.x > limit && padsize.y > limit )
2008 {
2009 padsize.x = limit;
2010 padsize.y = limit;
2011 }
2012 }
2013
2014 double maxSize = PCB_RENDER_SETTINGS::MAX_FONT_SIZE;
2015 double size = padsize.y;
2016
2017 m_gal->Save();
2018 m_gal->Translate( position );
2019
2020 // Keep the size ratio for the font, but make it smaller
2021 if( padsize.x < ( padsize.y * 0.95 ) )
2022 {
2023 m_gal->Rotate( -ANGLE_90.AsRadians() );
2024 size = padsize.x;
2025 std::swap( padsize.x, padsize.y );
2026 }
2027
2028 // Font size limits
2029 if( size > maxSize )
2030 size = maxSize;
2031
2032 // Default font settings
2033 m_gal->ResetTextAttributes();
2034 m_gal->SetHorizontalJustify( GR_TEXT_H_ALIGN_CENTER );
2035 m_gal->SetVerticalJustify( GR_TEXT_V_ALIGN_CENTER );
2036 m_gal->SetFontBold( false );
2037 m_gal->SetFontItalic( false );
2038 m_gal->SetFontUnderlined( false );
2039 m_gal->SetTextMirrored( false );
2040 m_gal->SetStrokeColor( m_pcbSettings.GetColor( aPad, aLayer ) );
2041 m_gal->SetIsStroke( true );
2042 m_gal->SetIsFill( false );
2043
2044 // We have already translated the GAL to be centered at the center of the pad's
2045 // bounding box
2046 VECTOR2I textpos( 0, 0 );
2047
2048 // Divide the space, to display both pad numbers and netnames and set the Y text
2049 // offset position to display 2 lines
2050 int Y_offset_numpad = 0;
2051 int Y_offset_netname = 0;
2052
2053 if( !netname.IsEmpty() && !padNumber.IsEmpty() )
2054 {
2055 // The magic numbers are defined experimentally for a better look.
2056 size = size / 2.5;
2057 Y_offset_netname = size / 1.4; // netname size is usually smaller than num pad
2058 // so the offset can be smaller
2059 Y_offset_numpad = size / 1.7;
2060 }
2061
2062 // We are using different fonts to display names, depending on the graphic
2063 // engine (OpenGL or Cairo).
2064 // Xscale_for_stroked_font adjust the text X size for cairo (stroke fonts) engine
2065 const double Xscale_for_stroked_font = 0.9;
2066
2067 if( !netname.IsEmpty() )
2068 {
2069 // approximate the size of net name text:
2070 // We use a size for at least 5 chars, to give a good look even for short names
2071 // (like VCC, GND...)
2072 double tsize = 1.5 * padsize.x / std::max( PrintableCharCount( netname )+1, 5 );
2073 tsize = std::min( tsize, size );
2074
2075 // Use a smaller text size to handle interline, pen size...
2076 tsize *= 0.85;
2077
2078 // Round and oval pads have less room to display the net name than other
2079 // (i.e RECT) shapes, so reduce the text size for these shapes
2080 if( aPad->GetShape( pcbLayer ) == PAD_SHAPE::CIRCLE
2081 || aPad->GetShape( pcbLayer ) == PAD_SHAPE::OVAL )
2082 {
2083 tsize *= 0.9;
2084 }
2085
2086 VECTOR2D namesize( tsize*Xscale_for_stroked_font, tsize );
2087 textpos.y = KiROUND( std::min( tsize * 1.4, double( Y_offset_netname ) ) );
2088
2089 m_gal->SetGlyphSize( namesize );
2090 m_gal->SetLineWidth( namesize.x / 6.0 );
2091 m_gal->SetFontBold( true );
2092 m_gal->BitmapText( netname, textpos, ANGLE_HORIZONTAL );
2093 }
2094
2095 if( !padNumber.IsEmpty() )
2096 {
2097 // approximate the size of the pad number text:
2098 // We use a size for at least 3 chars, to give a good look even for short numbers
2099 double tsize = 1.5 * padsize.x / std::max( PrintableCharCount( padNumber ), 3 );
2100 tsize = std::min( tsize, size );
2101
2102 // Use a smaller text size to handle interline, pen size...
2103 tsize *= 0.85;
2104 tsize = std::min( tsize, size );
2105 VECTOR2D numsize( tsize*Xscale_for_stroked_font, tsize );
2106 textpos.y = -Y_offset_numpad;
2107
2108 m_gal->SetGlyphSize( numsize );
2109 m_gal->SetLineWidth( numsize.x / 6.0 );
2110 m_gal->SetFontBold( true );
2111 m_gal->BitmapText( padNumber, textpos, ANGLE_HORIZONTAL );
2112 }
2113
2114 m_gal->Restore();
2115
2116 return;
2117 }
2118 else if( aLayer == LAYER_PAD_HOLEWALLS )
2119 {
2120 m_gal->SetIsFill( true );
2121 m_gal->SetIsStroke( false );
2123 double lineWidth = widthFactor * m_holePlatingThickness;
2124 lineWidth = std::min( lineWidth, aPad->GetSize( PADSTACK::ALL_LAYERS ).x / 2.0 );
2125 lineWidth = std::min( lineWidth, aPad->GetSize( PADSTACK::ALL_LAYERS ).y / 2.0 );
2126
2127 m_gal->SetFillColor( color );
2128 m_gal->SetMinLineWidth( lineWidth );
2129
2130 std::shared_ptr<SHAPE_SEGMENT> slot = aPad->GetEffectiveHoleShape();
2131
2132 if( slot->GetSeg().A == slot->GetSeg().B ) // Circular hole
2133 {
2134 double holeRadius = slot->GetWidth() / 2.0;
2135 m_gal->DrawHoleWall( slot->GetSeg().A, holeRadius, lineWidth );
2136 }
2137 else
2138 {
2139 int holeSize = KiROUND( slot->GetWidth() + ( 2 * lineWidth ) );
2140 m_gal->DrawSegment( slot->GetSeg().A, slot->GetSeg().B, holeSize );
2141 }
2142
2143 m_gal->SetMinLineWidth( 1.0 );
2144
2145 // Draw backdrill indicators on top of the hole wall so they remain visible
2146 // regardless of layer rendering order
2147 if( !m_pcbSettings.IsPrinting() && aPad->GetDrillSize().x > 0 )
2148 {
2149 const VECTOR2I& holePos = slot->GetSeg().A;
2150 const VECTOR2I& secDrill = aPad->GetSecondaryDrillSize();
2151 const VECTOR2I& terDrill = aPad->GetTertiaryDrillSize();
2152
2153 if( secDrill.x > 0 )
2154 {
2155 drawBackdrillIndicator( aPad, holePos, secDrill.x, aPad->GetSecondaryDrillStartLayer(),
2156 aPad->GetSecondaryDrillEndLayer() );
2157 }
2158
2159 if( terDrill.x > 0 )
2160 {
2161 drawBackdrillIndicator( aPad, holePos, terDrill.x, aPad->GetTertiaryDrillStartLayer(),
2162 aPad->GetTertiaryDrillEndLayer() );
2163 }
2164 }
2165
2166 return;
2167 }
2168
2169 bool outline_mode = !viewer_settings()->m_ViewersDisplay.m_DisplayPadFill;
2170
2171 if( m_pcbSettings.m_ForcePadSketchModeOn )
2172 outline_mode = true;
2173
2174 bool drawShape = false;
2175
2176 if( m_pcbSettings.IsPrinting() )
2177 {
2178 drawShape = aPad->FlashLayer( m_pcbSettings.GetPrintLayers() );
2179 }
2180 else if( ( aLayer < PCB_LAYER_ID_COUNT || IsPadCopperLayer( aLayer ) )
2181 && aPad->FlashLayer( pcbLayer ) )
2182 {
2183 drawShape = true;
2184 }
2185 else if( aPad->IsSelected() )
2186 {
2187 drawShape = true;
2188 outline_mode = true;
2189 }
2190 else if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
2191 {
2192 drawShape = true;
2193 outline_mode = false;
2194 }
2195
2196 // Plated holes are always filled as they use a solid BG fill to
2197 // draw the "hole" over the hole-wall segment/circle.
2198 if( outline_mode && aLayer != LAYER_PAD_PLATEDHOLES )
2199 {
2200 // Outline mode
2201 m_gal->SetIsFill( false );
2202 m_gal->SetIsStroke( true );
2203 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2204 m_gal->SetStrokeColor( color );
2205 }
2206 else
2207 {
2208 // Filled mode
2209 m_gal->SetIsFill( true );
2210 m_gal->SetIsStroke( false );
2211 m_gal->SetFillColor( color );
2212 }
2213
2214 if( aLayer == LAYER_PAD_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES )
2215 {
2216 SHAPE_SEGMENT slot = getPadHoleShape( aPad );
2217 VECTOR2I center = slot.GetSeg().A;
2218
2219 if( slot.GetSeg().A == slot.GetSeg().B ) // Circular hole
2220 m_gal->DrawCircle( center, slot.GetWidth() / 2.0 );
2221 else
2222 m_gal->DrawSegment( slot.GetSeg().A, slot.GetSeg().B, slot.GetWidth() );
2223 }
2224 else if( drawShape )
2225 {
2226 // Simple pad shapes skip GetEffectiveShape() and tessellation. Chamfered and trapezoid pads
2227 // stay generic since the typed encoding has only one corner parameter.
2228 PAD_SHAPE padShape = aPad->GetShape( pcbLayer );
2229
2230 if( m_gal->SupportsTypedPads()
2231 && !outline_mode
2232 && aLayer != LAYER_LOCKED_ITEM_SHADOW
2233 && aLayer != F_Mask && aLayer != B_Mask
2234 && aLayer != F_Paste && aLayer != B_Paste
2235 && padShape != PAD_SHAPE::CUSTOM
2236 && padShape != PAD_SHAPE::CHAMFERED_RECT
2237 && padShape != PAD_SHAPE::TRAPEZOID
2238 && ( !IsCopperLayer( pcbLayer )
2239 || aPad->GetPostMachiningKnockout( pcbLayer ) == 0 ) )
2240 {
2241 VECTOR2I padSize = aPad->GetSize( pcbLayer );
2242 double halfWidth = padSize.x / 2.0;
2243 double halfHeight = padSize.y / 2.0;
2244 VECTOR2D center( aPad->ShapePos( pcbLayer ) );
2245 double rotation = aPad->GetOrientation().AsRadians();
2246 float shapeType;
2247
2248 switch( padShape )
2249 {
2250 case PAD_SHAPE::CIRCLE: shapeType = 0.0f; break;
2251 case PAD_SHAPE::RECTANGLE: shapeType = 1.0f; break;
2252 case PAD_SHAPE::ROUNDRECT: shapeType = 2.0f; break;
2253 case PAD_SHAPE::OVAL: shapeType = 4.0f; break;
2254 default: shapeType = 0.0f; break;
2255 }
2256
2257 double cornerRadius = 0.0;
2258
2259 if( padShape == PAD_SHAPE::ROUNDRECT )
2260 cornerRadius = aPad->GetRoundRectCornerRadius( pcbLayer );
2261
2262 m_gal->DrawPad( center, halfWidth, halfHeight, cornerRadius, shapeType,
2263 rotation, color );
2264 }
2265 else
2266 {
2267 VECTOR2I pad_size = aPad->GetSize( pcbLayer );
2268 VECTOR2I margin;
2269
2270 auto getExpansion =
2271 [&]( PCB_LAYER_ID layer )
2272 {
2273 VECTOR2I expansion;
2274
2275 switch( aLayer )
2276 {
2277 case F_Mask:
2278 case B_Mask:
2279 expansion.x = expansion.y = aPad->GetSolderMaskExpansion( layer );
2280 break;
2281
2282 case F_Paste:
2283 case B_Paste:
2284 expansion = aPad->GetSolderPasteMargin( layer );
2285 break;
2286
2287 default:
2288 expansion.x = expansion.y = 0;
2289 break;
2290 }
2291
2292 return expansion;
2293 };
2294
2295 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
2296 {
2297 LSET visibleLayers = aPad->GetLayerSet();
2298
2299 if( aPad->GetBoard() )
2300 {
2301 visibleLayers &= aPad->GetBoard()->GetVisibleLayers();
2302 visibleLayers &= aPad->GetBoard()->GetEnabledLayers();
2303 }
2304
2305 for( PCB_LAYER_ID layer : visibleLayers )
2306 {
2307 VECTOR2I expansion = getExpansion( layer );
2308
2309 if( expansion.SquaredEuclideanNorm() > margin.SquaredEuclideanNorm() )
2310 margin = expansion;
2311 }
2312
2313 margin.x += m_lockedShadowMargin / 2;
2314 margin.y += m_lockedShadowMargin / 2;
2315 }
2316 else
2317 {
2318 margin = getExpansion( pcbLayer );
2319 }
2320
2321 std::unique_ptr<PAD> dummyPad;
2322 std::shared_ptr<SHAPE_COMPOUND> shapes;
2323
2324 // Drawing components of compound shapes in outline mode produces a mess.
2325 bool simpleShapes = !outline_mode;
2326
2327 if( simpleShapes )
2328 {
2329 if( ( margin.x != margin.y && aPad->GetShape( pcbLayer ) != PAD_SHAPE::CUSTOM )
2330 || ( aPad->GetShape( pcbLayer ) == PAD_SHAPE::ROUNDRECT
2331 && ( margin.x < 0 || margin.y < 0 ) ) )
2332 {
2333 // Our algorithms below (polygon inflation in particular) can't handle differential
2334 // inflation along separate axes. So for those cases we build a dummy pad instead,
2335 // and inflate it.
2336
2337 // Margin is added to both sides. If the total margin is larger than the pad
2338 // then don't display this layer
2339 if( pad_size.x + 2 * margin.x <= 0 || pad_size.y + 2 * margin.y <= 0 )
2340 return;
2341
2342 dummyPad.reset( static_cast<PAD*>( aPad->Duplicate( IGNORE_PARENT_GROUP ) ) );
2343
2344 int initial_radius = dummyPad->GetRoundRectCornerRadius( pcbLayer );
2345
2346 dummyPad->SetSize( pcbLayer, pad_size + margin + margin );
2347
2348 if( dummyPad->GetShape( pcbLayer ) == PAD_SHAPE::ROUNDRECT )
2349 {
2350 // To keep the right margin around the corners, we need to modify the corner radius.
2351 // We must have only one radius correction, so use the smallest absolute margin.
2352 int radius_margin = std::max( margin.x, margin.y ); // radius_margin is < 0
2353 dummyPad->SetRoundRectCornerRadius( pcbLayer, std::max( initial_radius + radius_margin, 0 ) );
2354 }
2355
2356 shapes = std::dynamic_pointer_cast<SHAPE_COMPOUND>( dummyPad->GetEffectiveShape( pcbLayer ) );
2357 margin.x = margin.y = 0;
2358 }
2359 else
2360 {
2361 shapes = std::dynamic_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape( pcbLayer ) );
2362 }
2363
2364 // The dynamic cast above will fail if the pad returned the hole shape or a null shape
2365 // instead of a SHAPE_COMPOUND, which happens if we're on a copper layer and the pad has
2366 // no shape on that layer.
2367 if( !shapes )
2368 return;
2369
2370 if( aPad->GetShape( pcbLayer ) == PAD_SHAPE::CUSTOM && ( margin.x || margin.y ) )
2371 {
2372 // We can't draw as shapes because we don't know which edges are internal and which
2373 // are external (so we don't know when to apply the margin and when not to).
2374 simpleShapes = false;
2375 }
2376
2377 for( const SHAPE* shape : shapes->Shapes() )
2378 {
2379 if( !simpleShapes )
2380 break;
2381
2382 switch( shape->Type() )
2383 {
2384 case SH_SEGMENT:
2385 case SH_CIRCLE:
2386 case SH_RECT:
2387 case SH_SIMPLE:
2388 // OK so far
2389 break;
2390
2391 default:
2392 // Not OK
2393 simpleShapes = false;
2394 break;
2395 }
2396 }
2397 }
2398
2399 const auto drawOneSimpleShape =
2400 [&]( const SHAPE& aShape )
2401 {
2402 switch( aShape.Type() )
2403 {
2404 case SH_SEGMENT:
2405 {
2406 const SHAPE_SEGMENT& seg = (const SHAPE_SEGMENT&) aShape;
2407 int effectiveWidth = seg.GetWidth() + 2 * margin.x;
2408
2409 if( effectiveWidth > 0 )
2410 m_gal->DrawSegment( seg.GetSeg().A, seg.GetSeg().B, effectiveWidth );
2411
2412 break;
2413 }
2414
2415 case SH_CIRCLE:
2416 {
2417 const SHAPE_CIRCLE& circle = (const SHAPE_CIRCLE&) aShape;
2418 int effectiveRadius = circle.GetRadius() + margin.x;
2419
2420 if( effectiveRadius > 0 )
2421 m_gal->DrawCircle( circle.GetCenter(), effectiveRadius );
2422
2423 break;
2424 }
2425
2426 case SH_RECT:
2427 {
2428 const SHAPE_RECT& r = (const SHAPE_RECT&) aShape;
2429 VECTOR2I pos = r.GetPosition();
2430 VECTOR2I effectiveMargin = margin;
2431
2432 if( effectiveMargin.x < 0 )
2433 {
2434 // A negative margin just produces a smaller rect.
2435 VECTOR2I effectiveSize = r.GetSize() + effectiveMargin;
2436
2437 if( effectiveSize.x > 0 && effectiveSize.y > 0 )
2438 m_gal->DrawRectangle( pos - effectiveMargin, pos + effectiveSize );
2439 }
2440 else if( effectiveMargin.x > 0 )
2441 {
2442 // A positive margin produces a larger rect, but with rounded corners
2443 m_gal->DrawRectangle( r.GetPosition(), r.GetPosition() + r.GetSize() );
2444
2445 // Use segments to produce the margin with rounded corners
2446 m_gal->DrawSegment( pos,
2447 pos + VECTOR2I( r.GetWidth(), 0 ),
2448 effectiveMargin.x * 2 );
2449 m_gal->DrawSegment( pos + VECTOR2I( r.GetWidth(), 0 ),
2450 pos + r.GetSize(),
2451 effectiveMargin.x * 2 );
2452 m_gal->DrawSegment( pos + r.GetSize(),
2453 pos + VECTOR2I( 0, r.GetHeight() ),
2454 effectiveMargin.x * 2 );
2455 m_gal->DrawSegment( pos + VECTOR2I( 0, r.GetHeight() ),
2456 pos,
2457 effectiveMargin.x * 2 );
2458 }
2459 else
2460 {
2461 m_gal->DrawRectangle( r.GetPosition(), r.GetPosition() + r.GetSize() );
2462 }
2463
2464 break;
2465 }
2466
2467 case SH_SIMPLE:
2468 {
2469 const SHAPE_SIMPLE& poly = static_cast<const SHAPE_SIMPLE&>( aShape );
2470
2471 if( poly.PointCount() < 2 ) // Careful of empty pads
2472 break;
2473
2474 if( margin.x < 0 ) // The poly shape must be deflated
2475 {
2476 SHAPE_POLY_SET outline;
2477 outline.NewOutline();
2478
2479 for( int ii = 0; ii < poly.PointCount(); ++ii )
2480 outline.Append( poly.CPoint( ii ) );
2481
2483
2484 m_gal->DrawPolygon( outline );
2485 }
2486 else
2487 {
2488 m_gal->DrawPolygon( poly.Vertices() );
2489 }
2490
2491 // Now add on a rounded margin (using segments) if the margin > 0
2492 if( margin.x > 0 )
2493 {
2494 for( int ii = 0; ii < (int) poly.GetSegmentCount(); ++ii )
2495 {
2496 SEG seg = poly.GetSegment( ii );
2497 m_gal->DrawSegment( seg.A, seg.B, margin.x * 2 );
2498 }
2499 }
2500
2501 break;
2502 }
2503
2504 default:
2505 // Better not get here; we already pre-flighted the shapes...
2506 break;
2507 }
2508 };
2509
2510 if( simpleShapes )
2511 {
2512 for( const SHAPE* shape : shapes->Shapes() )
2513 drawOneSimpleShape( *shape );
2514 }
2515 else
2516 {
2517 // This is expensive. Avoid if possible.
2518 SHAPE_POLY_SET polySet;
2519 aPad->TransformShapeToPolygon( polySet, ToLAYER_ID( aLayer ), margin.x, m_maxError, ERROR_INSIDE );
2520 m_gal->DrawPolygon( polySet );
2521 }
2522 }
2523 }
2524
2525 if( !m_pcbSettings.IsPrinting() && IsCopperLayer( pcbLayer ) && aPad->GetDrillSize().x > 0 )
2526 {
2527 VECTOR2D holePos = aPad->GetPosition() + aPad->GetOffset( pcbLayer );
2528 drawPostMachiningIndicator( aPad, holePos, pcbLayer );
2529 }
2530
2531 if( IsClearanceLayer( aLayer )
2532 && ( ( pcbconfig() && pcbconfig()->m_Display.m_PadClearance ) || !pcbconfig() )
2533 && !m_pcbSettings.m_isPrinting )
2534 {
2535 const PCB_LAYER_ID copperLayerForClearance = ToLAYER_ID( aLayer - LAYER_CLEARANCE_START );
2536
2537 if( aPad->GetAttribute() == PAD_ATTRIB::NPTH )
2538 color = m_pcbSettings.GetLayerColor( LAYER_NON_PLATEDHOLES );
2539
2540 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2541 m_gal->SetIsStroke( true );
2542 m_gal->SetIsFill( false );
2543 m_gal->SetStrokeColor( color );
2544
2545 const int clearance = aPad->GetOwnClearance( copperLayerForClearance );
2546
2547 if( aPad->FlashLayer( copperLayerForClearance ) && clearance > 0 )
2548 {
2549 auto shape = std::dynamic_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape( pcbLayer ) );
2550
2551 if( shape && shape->Size() == 1 && shape->Shapes()[0]->Type() == SH_SEGMENT )
2552 {
2553 const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shape->Shapes()[0];
2554 m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() + 2 * clearance );
2555 }
2556 else if( shape && shape->Size() == 1 && shape->Shapes()[0]->Type() == SH_CIRCLE )
2557 {
2558 const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shape->Shapes()[0];
2559 m_gal->DrawCircle( circle->GetCenter(), circle->GetRadius() + clearance );
2560 }
2561 else
2562 {
2563 SHAPE_POLY_SET polySet;
2564
2565 // Use ERROR_INSIDE because it avoids Clipper and is therefore much faster.
2566 aPad->TransformShapeToPolygon( polySet, copperLayerForClearance, clearance, m_maxError, ERROR_INSIDE );
2567
2568 if( polySet.Outline( 0 ).PointCount() > 2 ) // Careful of empty pads
2569 m_gal->DrawPolygon( polySet );
2570 }
2571 }
2572 else if( aPad->GetEffectiveHoleShape() && clearance > 0 )
2573 {
2574 std::shared_ptr<SHAPE_SEGMENT> slot = aPad->GetEffectiveHoleShape();
2575 m_gal->DrawSegment( slot->GetSeg().A, slot->GetSeg().B, slot->GetWidth() + 2 * clearance );
2576 }
2577 }
2578
2579 if( m_pcbSettings.IsHighlightEnabled()
2580 && m_pcbSettings.GetHighlightNetCodes().contains( aPad->GetNetCode() ) )
2581 {
2582 NETINFO_ITEM* net = aPad->GetNet();
2583 if( net && ( net->GetTerminalPad( 0 ) == aPad || net->GetTerminalPad( 1 ) == aPad ) )
2584 {
2585 BOX2I box = aPad->GetBoundingBox();
2586 m_gal->SetIsFill( false );
2587 m_gal->SetIsStroke( true );
2588
2589 // Base emphasis (net highlight)
2590 COLOR4D termColor = color.Brightened( 0.2 );
2591 int baseWidth = m_pcbSettings.m_outlineWidth * 2;
2592
2593 // If a grouped chain highlight is active and this pad belongs to that chain,
2594 // make the emphasis stronger (brighter + thicker + inset second rectangle).
2595 if( !m_pcbSettings.m_highlightedNetChain.IsEmpty()
2596 && net && net->GetNetChain() == m_pcbSettings.m_highlightedNetChain )
2597 {
2598 // Prefer the chain's own colour override if the board has one.
2599 if( const BOARD* board = aPad->GetBoard() )
2600 {
2601 COLOR4D chainColor =
2602 board->GetNetChainColor( m_pcbSettings.m_highlightedNetChain );
2603
2604 if( chainColor != COLOR4D::UNSPECIFIED )
2605 termColor = chainColor;
2606 else
2607 termColor = termColor.Brightened( 0.25 );
2608 }
2609 else
2610 {
2611 termColor = termColor.Brightened( 0.25 );
2612 }
2613
2614 baseWidth = m_pcbSettings.m_outlineWidth * 3;
2615 }
2616
2617 m_gal->SetStrokeColor( termColor );
2618 m_gal->SetLineWidth( baseWidth );
2619 m_gal->DrawRectangle( box.GetOrigin(), box.GetEnd() );
2620
2621 if( !m_pcbSettings.m_highlightedNetChain.IsEmpty()
2622 && net && net->GetNetChain() == m_pcbSettings.m_highlightedNetChain )
2623 {
2624 // Draw an inner rectangle for additional visual distinction.
2625 // Shrink by one outline width equivalent to avoid excessive size.
2626 int inset = baseWidth * 2; // screen-space approx; acceptable heuristic
2627 BOX2I inner = box;
2628 inner.Inflate( -inset, -inset );
2629 if( inner.GetWidth() > 0 && inner.GetHeight() > 0 )
2630 {
2631 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2632 m_gal->DrawRectangle( inner.GetOrigin(), inner.GetEnd() );
2633 }
2634 }
2635 }
2636 }
2637}
2638
2639
2640void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
2641{
2642 COLOR4D color = m_pcbSettings.GetColor( aShape, aLayer );
2644 int thickness = getLineThickness( aShape->GetWidth() );
2645 LINE_STYLE lineStyle = aShape->GetStroke().GetLineStyle();
2646 bool isSolidFill = aShape->IsSolidFill();
2647 bool isHatchedFill = aShape->IsHatchedFill();
2648
2649 if( lineStyle == LINE_STYLE::DEFAULT )
2650 lineStyle = LINE_STYLE::SOLID;
2651
2652 if( IsSolderMaskLayer( aLayer )
2653 && aShape->HasSolderMask()
2654 && IsExternalCopperLayer( aShape->GetLayer() ) )
2655 {
2656 lineStyle = LINE_STYLE::SOLID;
2657 thickness += aShape->GetSolderMaskExpansion() * 2;
2658
2659 if( isHatchedFill )
2660 {
2661 isSolidFill = true;
2662 isHatchedFill = false;
2663 }
2664 }
2665
2666 if( IsNetnameLayer( aLayer ) )
2667 {
2668 // Net names are shown only in board editor:
2670 return;
2671
2672 if( !pcbconfig() || pcbconfig()->m_Display.m_NetNames < 2 )
2673 return;
2674
2675 if( aShape->GetNetCode() <= NETINFO_LIST::UNCONNECTED )
2676 return;
2677
2678 const wxString& netname = aShape->GetDisplayNetname();
2679
2680 if( netname.IsEmpty() )
2681 return;
2682
2683 if( aShape->GetShape() == SHAPE_T::SEGMENT )
2684 {
2685 SHAPE_SEGMENT seg( { aShape->GetStart(), aShape->GetEnd() }, aShape->GetWidth() );
2686 renderNetNameForSegment( seg, color, netname );
2687 return;
2688 }
2689
2690 // TODO: Maybe use some of the pad code?
2691
2692 return;
2693 }
2694
2695 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
2696 {
2697 color = m_pcbSettings.GetColor( aShape, aLayer );
2698 thickness = thickness + m_lockedShadowMargin;
2699
2700 // Note: on LAYER_LOCKED_ITEM_SHADOW always draw shadow shapes as continuous lines
2701 // otherwise the look is very strange and ugly
2702 lineStyle = LINE_STYLE::SOLID;
2703 }
2704
2705 if( aLayer == LAYER_CONSTRAINT_SHADOW )
2706 {
2707 color = m_pcbSettings.GetColor( aShape, aLayer );
2708 int margin = m_lockedShadowMargin;
2709
2710 // Selected constraint members drawn brighter and thicker
2711 if( m_pcbSettings.GetHighlightedConstraintMembers().count( aShape->m_Uuid ) )
2712 {
2713 color = color.Brightened( 0.5 );
2714 margin *= 2;
2715 }
2716
2717 thickness = thickness + margin;
2718 lineStyle = LINE_STYLE::SOLID;
2719 }
2720
2721 if( outline_mode )
2722 {
2723 m_gal->SetIsFill( false );
2724 m_gal->SetIsStroke( true );
2725 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2726 }
2727
2728 m_gal->SetFillColor( color );
2729 m_gal->SetStrokeColor( color );
2730
2731 if( lineStyle == LINE_STYLE::SOLID || aShape->IsSolidFill() )
2732 {
2733 switch( aShape->GetShape() )
2734 {
2735 case SHAPE_T::SEGMENT:
2736 {
2737 VECTOR2I segStart = aShape->GetStart();
2738 VECTOR2I segEnd = aShape->GetEnd();
2739
2740 if( EDA_SHAPE::ShortenSegmentForEndings( segStart, segEnd, aShape->GetStartEnding(),
2741 aShape->GetEndEnding(), thickness ) )
2742 {
2743 if( aShape->IsProxyItem() )
2744 {
2745 std::vector<VECTOR2I> pts;
2746 VECTOR2I offset = ( segEnd - segStart ).Perpendicular();
2747 offset = offset.Resize( thickness / 2 );
2748
2749 pts.push_back( segStart + offset );
2750 pts.push_back( segStart - offset );
2751 pts.push_back( segEnd - offset );
2752 pts.push_back( segEnd + offset );
2753
2754 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2755 m_gal->DrawLine( pts[0], pts[1] );
2756 m_gal->DrawLine( pts[1], pts[2] );
2757 m_gal->DrawLine( pts[2], pts[3] );
2758 m_gal->DrawLine( pts[3], pts[0] );
2759 m_gal->DrawLine( ( pts[0] + pts[1] ) / 2, ( pts[1] + pts[2] ) / 2 );
2760 m_gal->DrawLine( ( pts[1] + pts[2] ) / 2, ( pts[2] + pts[3] ) / 2 );
2761 m_gal->DrawLine( ( pts[2] + pts[3] ) / 2, ( pts[3] + pts[0] ) / 2 );
2762 m_gal->DrawLine( ( pts[3] + pts[0] ) / 2, ( pts[0] + pts[1] ) / 2 );
2763 }
2764 else if( outline_mode )
2765 {
2766 m_gal->DrawSegment( segStart, segEnd, thickness );
2767 }
2768 else if( lineStyle == LINE_STYLE::SOLID )
2769 {
2770 m_gal->SetIsFill( true );
2771 m_gal->SetIsStroke( false );
2772
2773 m_gal->DrawSegment( segStart, segEnd, thickness );
2774 }
2775 }
2776
2777 break;
2778 }
2779
2780 case SHAPE_T::RECTANGLE:
2781 {
2782 if( aShape->GetCornerRadius() > 0 )
2783 {
2784 // Creates a normalized ROUNDRECT item
2785 // (GetRectangleWidth() and GetRectangleHeight() can be < 0 with transforms
2786 ROUNDRECT rr( SHAPE_RECT( aShape->GetStart(), aShape->GetRectangleWidth(),
2787 aShape->GetRectangleHeight() ),
2788 aShape->GetCornerRadius(), true /* normalize */ );
2789 SHAPE_POLY_SET poly;
2790 rr.TransformToPolygon( poly, aShape->GetMaxError() );
2791 SHAPE_LINE_CHAIN outline = poly.Outline( 0 );
2792
2793 if( aShape->IsProxyItem() )
2794 {
2795 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2796 m_gal->DrawPolygon( outline );
2797 }
2798 else if( outline_mode )
2799 {
2800 m_gal->DrawSegmentChain( outline, thickness );
2801 }
2802 else
2803 {
2804 m_gal->SetIsFill( true );
2805 m_gal->SetIsStroke( false );
2806
2807 if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
2808 {
2809 m_gal->DrawSegmentChain( outline, thickness );
2810 }
2811
2812 if( isSolidFill )
2813 {
2814 if( thickness < 0 )
2815 {
2816 SHAPE_POLY_SET deflated_shape = outline;
2817 deflated_shape.Inflate( thickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, m_maxError );
2818 m_gal->DrawPolygon( deflated_shape );
2819 }
2820 else
2821 {
2822 m_gal->DrawPolygon( outline );
2823 }
2824 }
2825 }
2826 }
2827 else
2828 {
2829 std::vector<VECTOR2I> pts = aShape->GetRectCorners();
2830
2831 if( aShape->IsProxyItem() )
2832 {
2833 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
2834 m_gal->DrawLine( pts[0], pts[1] );
2835 m_gal->DrawLine( pts[1], pts[2] );
2836 m_gal->DrawLine( pts[2], pts[3] );
2837 m_gal->DrawLine( pts[3], pts[0] );
2838 m_gal->DrawLine( pts[0], pts[2] );
2839 m_gal->DrawLine( pts[1], pts[3] );
2840 }
2841 else if( outline_mode )
2842 {
2843 m_gal->DrawSegment( pts[0], pts[1], thickness );
2844 m_gal->DrawSegment( pts[1], pts[2], thickness );
2845 m_gal->DrawSegment( pts[2], pts[3], thickness );
2846 m_gal->DrawSegment( pts[3], pts[0], thickness );
2847 }
2848 else
2849 {
2850 m_gal->SetIsFill( true );
2851 m_gal->SetIsStroke( false );
2852
2853 if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
2854 {
2855 m_gal->DrawSegment( pts[0], pts[1], thickness );
2856 m_gal->DrawSegment( pts[1], pts[2], thickness );
2857 m_gal->DrawSegment( pts[2], pts[3], thickness );
2858 m_gal->DrawSegment( pts[3], pts[0], thickness );
2859 }
2860
2861 if( isSolidFill )
2862 {
2863 SHAPE_POLY_SET poly;
2864 poly.NewOutline();
2865
2866 for( const VECTOR2I& pt : pts )
2867 poly.Append( pt );
2868
2869 if( thickness < 0 )
2871
2872 m_gal->DrawPolygon( poly );
2873 }
2874 }
2875 }
2876
2877 break;
2878 }
2879
2880 case SHAPE_T::ARC:
2881 {
2882 EDA_ANGLE startAngle;
2883 EDA_ANGLE endAngle;
2884 aShape->CalcArcAngles( startAngle, endAngle );
2885
2886 EDA_ANGLE arcAngle = endAngle - startAngle;
2887
2888 if( aShape->ShortenArcForEndings( startAngle, arcAngle, aShape->GetRadius(), thickness ) )
2889 {
2890 if( outline_mode )
2891 {
2892 m_gal->DrawArcSegment( aShape->GetCenter(), aShape->GetRadius(), startAngle, arcAngle, thickness,
2893 m_maxError );
2894 }
2895 else if( lineStyle == LINE_STYLE::SOLID )
2896 {
2897 m_gal->SetIsFill( true );
2898 m_gal->SetIsStroke( false );
2899
2900 m_gal->DrawArcSegment( aShape->GetCenter(), aShape->GetRadius(), startAngle, arcAngle, thickness,
2901 m_maxError );
2902 }
2903 }
2904
2905 break;
2906 }
2907
2908 case SHAPE_T::CIRCLE:
2909 if( outline_mode )
2910 {
2911 m_gal->DrawCircle( aShape->GetStart(), aShape->GetRadius() - thickness / 2 );
2912 m_gal->DrawCircle( aShape->GetStart(), aShape->GetRadius() + thickness / 2 );
2913 }
2914 else
2915 {
2916 m_gal->SetIsFill( aShape->IsSolidFill() );
2917 m_gal->SetIsStroke( lineStyle == LINE_STYLE::SOLID && thickness > 0 );
2918 m_gal->SetLineWidth( thickness );
2919
2920 int radius = aShape->GetRadius();
2921
2922 if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
2923 {
2924 m_gal->DrawCircle( aShape->GetStart(), radius );
2925 }
2926 else if( isSolidFill )
2927 {
2928 if( thickness < 0 )
2929 {
2930 radius += thickness / 2;
2931 radius = std::max( radius, 0 );
2932 }
2933
2934 m_gal->DrawCircle( aShape->GetStart(), radius );
2935 }
2936 }
2937 break;
2938
2939 case SHAPE_T::POLY:
2940 {
2941 SHAPE_POLY_SET& shape = const_cast<PCB_SHAPE*>( aShape )->GetPolyShape();
2942
2943 auto drawOutlineBody =
2944 [&]( const SHAPE_LINE_CHAIN& aOutline, int aOutlineIdx )
2945 {
2946 if( aOutline.PointCount() < 2 )
2947 return;
2948
2949 if( aShape->GetStartEnding().GetShortenDepth( thickness ) > 0
2950 || aShape->GetEndEnding().GetShortenDepth( thickness ) > 0 )
2951 {
2952 std::vector<VECTOR2I> pts;
2953
2954 if( !aShape->GetShortenedBodyPolyPoints( aOutline, aOutlineIdx, pts, thickness ) )
2955 return;
2956
2957 SHAPE_LINE_CHAIN shortened;
2958
2959 for( const VECTOR2I& pt : pts )
2960 shortened.Append( pt );
2961
2962 shortened.SetClosed( aOutline.IsClosed() );
2963 m_gal->DrawSegmentChain( shortened, thickness );
2964 }
2965 else
2966 {
2967 m_gal->DrawSegmentChain( aOutline, thickness );
2968 }
2969 };
2970
2971 if( shape.OutlineCount() == 0 )
2972 break;
2973
2974 if( outline_mode )
2975 {
2976 for( int ii = 0; ii < shape.OutlineCount(); ++ii )
2977 drawOutlineBody( shape.COutline( ii ), ii );
2978 }
2979 else
2980 {
2981 m_gal->SetIsFill( true );
2982 m_gal->SetIsStroke( false );
2983
2984 if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
2985 {
2986 for( int ii = 0; ii < shape.OutlineCount(); ++ii )
2987 drawOutlineBody( shape.COutline( ii ), ii );
2988 }
2989
2990 if( isSolidFill )
2991 {
2992 if( thickness < 0 )
2993 {
2994 SHAPE_POLY_SET deflated_shape = shape;
2995 deflated_shape.Inflate( thickness / 2, CORNER_STRATEGY::ROUND_ALL_CORNERS, m_maxError );
2996 m_gal->DrawPolygon( deflated_shape );
2997 }
2998 else
2999 {
3000 // On Opengl, a not convex filled polygon is usually drawn by using
3001 // triangles as primitives. CacheTriangulation() can create basic triangle
3002 // primitives to draw the polygon solid shape on Opengl. GLU tessellation
3003 // is much slower, so currently we are using our tessellation.
3004 if( m_gal->UsesPolygonTriangulationCache()
3005 && !shape.IsTriangulationUpToDate() )
3006 shape.CacheTriangulation( true );
3007
3008 m_gal->DrawPolygon( shape );
3009 }
3010 }
3011 }
3012
3013 break;
3014 }
3015
3016 case SHAPE_T::BEZIER:
3017 if( std::optional<BEZIER<double>> curve = aShape->ShortenedBezierCurve( thickness ) )
3018 {
3019 if( outline_mode )
3020 {
3021 std::vector<VECTOR2D> output;
3022 BEZIER_POLY converter( std::vector<VECTOR2D>{ curve->Start, curve->C1, curve->C2, curve->End } );
3023
3024 converter.GetPoly( output, m_maxError );
3025 m_gal->DrawSegmentChain( output, thickness );
3026 }
3027 else
3028 {
3029 m_gal->SetIsFill( aShape->IsSolidFill() );
3030 m_gal->SetIsStroke( lineStyle == LINE_STYLE::SOLID && thickness > 0 );
3031 m_gal->SetLineWidth( thickness );
3032 m_gal->DrawCurve( curve->Start, curve->C1, curve->C2, curve->End, m_maxError );
3033 }
3034 }
3035
3036 break;
3037
3038 case SHAPE_T::ELLIPSE:
3039 {
3040 const VECTOR2D center( aShape->GetEllipseCenter() );
3041 const int majorR = aShape->GetEllipseMajorRadius();
3042 const int minorR = aShape->GetEllipseMinorRadius();
3043 const EDA_ANGLE& rot = aShape->GetEllipseRotation();
3044
3045 if( outline_mode )
3046 {
3047 m_gal->DrawEllipse( center, majorR - thickness / 2, minorR - thickness / 2, rot );
3048 m_gal->DrawEllipse( center, majorR + thickness / 2, minorR + thickness / 2, rot );
3049 }
3050 else
3051 {
3052 m_gal->SetIsFill( aShape->IsSolidFill() );
3053 m_gal->SetIsStroke( lineStyle == LINE_STYLE::SOLID && thickness > 0 );
3054 m_gal->SetLineWidth( thickness );
3055
3056 if( lineStyle == LINE_STYLE::SOLID && thickness > 0 )
3057 m_gal->DrawEllipse( center, majorR, minorR, rot );
3058 else if( isSolidFill )
3059 m_gal->DrawEllipse( center, majorR, minorR, rot );
3060 }
3061
3062 break;
3063 }
3064
3066 {
3067 const VECTOR2D center( aShape->GetEllipseCenter() );
3068 const int majorR = aShape->GetEllipseMajorRadius();
3069 const int minorR = aShape->GetEllipseMinorRadius();
3070 const EDA_ANGLE& rot = aShape->GetEllipseRotation();
3071 const EDA_ANGLE& start = aShape->GetEllipseStartAngle();
3072 const EDA_ANGLE& end = aShape->GetEllipseEndAngle();
3073 SHAPE_ELLIPSE ellipse( center, majorR, minorR, rot, start, end );
3074 SHAPE_LINE_CHAIN chain = ellipse.ConvertToPolyline( aShape->GetMaxError() );
3075 std::vector<VECTOR2I> pts;
3076
3077 if( aShape->GetShortenedBodyPolyPoints( chain, 0, pts, thickness ) )
3078 {
3079 chain.Clear();
3080
3081 for( const VECTOR2I& pt : pts )
3082 chain.Append( pt );
3083
3084 chain.SetClosed( false );
3085
3086 if( outline_mode )
3087 m_gal->DrawSegmentChain( chain, thickness );
3088 else if( lineStyle == LINE_STYLE::SOLID )
3089 m_gal->DrawSegmentChain( chain, thickness );
3090 }
3091
3092 break;
3093 }
3094
3095 case SHAPE_T::UNDEFINED:
3096 break;
3097 }
3098 }
3099
3100 if( lineStyle != LINE_STYLE::SOLID )
3101 {
3102 if( !outline_mode )
3103 {
3104 m_gal->SetIsFill( true );
3105 m_gal->SetIsStroke( false );
3106 }
3107
3108 std::vector<SHAPE*> shapes = aShape->MakeEffectiveShapesForStroking( thickness );
3109
3110 for( SHAPE* shape : shapes )
3111 {
3112 STROKE_PARAMS::Stroke( shape, lineStyle, getLineThickness( aShape->GetWidth() ),
3114 [&]( const VECTOR2I& a, const VECTOR2I& b )
3115 {
3116 m_gal->DrawSegment( a, b, thickness );
3117 } );
3118 }
3119
3120 for( SHAPE* shape : shapes )
3121 delete shape;
3122 }
3123
3124 if( isHatchedFill )
3125 {
3126 aShape->UpdateHatching();
3127 m_gal->SetIsFill( false );
3128 m_gal->SetIsStroke( true );
3129 m_gal->SetLineWidth( aShape->GetHatchLineWidth() );
3130
3131 for( const SEG& seg : aShape->GetHatchLines() )
3132 m_gal->DrawLine( seg.A, seg.B );
3133 }
3134
3135 // Line endings
3138 {
3139 EDA_ANGLE startTangent, endTangent;
3140 aShape->GetEndingTangents( startTangent, endTangent, thickness );
3141
3142 VECTOR2I startPt, endPt;
3143
3144 if( aShape->GetLineEndingEndpoints( startPt, endPt ) )
3145 {
3146 aShape->GetStartEnding().Draw( *m_gal, startPt, startTangent, thickness, color );
3147 aShape->GetEndEnding().Draw( *m_gal, endPt, endTangent, thickness, color );
3148 }
3149 }
3150}
3151
3152
3153void PCB_PAINTER::strokeText( const wxString& aText, const VECTOR2I& aPosition,
3154 const TEXT_ATTRIBUTES& aAttrs, const KIFONT::METRICS& aFontMetrics )
3155{
3156 KIFONT::FONT* font = aAttrs.m_Font;
3157
3158 if( !font )
3159 font = KIFONT::FONT::GetFont( wxEmptyString, aAttrs.m_Bold, aAttrs.m_Italic );
3160
3161 m_gal->SetIsFill( font->IsOutline() );
3162 m_gal->SetIsStroke( font->IsStroke() );
3163
3164 VECTOR2I pos( aPosition );
3165 VECTOR2I fudge( KiROUND( 0.16 * aAttrs.m_StrokeWidth ), 0 );
3166
3167 RotatePoint( fudge, aAttrs.m_Angle.GetAngle() );
3168
3169 if( ( aAttrs.m_Halign == GR_TEXT_H_ALIGN_LEFT && !aAttrs.m_Mirrored )
3170 || ( aAttrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT && aAttrs.m_Mirrored ) )
3171 {
3172 pos -= fudge;
3173 }
3174 else if( ( aAttrs.m_Halign == GR_TEXT_H_ALIGN_RIGHT && !aAttrs.m_Mirrored )
3175 || ( aAttrs.m_Halign == GR_TEXT_H_ALIGN_LEFT && aAttrs.m_Mirrored ) )
3176 {
3177 pos += fudge;
3178 }
3179
3180 font->Draw( m_gal, aText, pos, aAttrs, aFontMetrics );
3181}
3182
3183
3184void PCB_PAINTER::draw( const PCB_REFERENCE_IMAGE* aBitmap, int aLayer )
3185{
3186 m_gal->Save();
3187
3188 const REFERENCE_IMAGE& refImg = aBitmap->GetReferenceImage();
3189 m_gal->Translate( refImg.GetPosition() );
3190
3191 m_gal->Rotate( -refImg.GetOrientation().AsRadians() );
3192
3193 // When the image scale factor is not 1.0, we need to modify the actual as the image scale
3194 // factor is similar to a local zoom
3195 const double img_scale = refImg.GetImageScale();
3196
3197 if( img_scale != 1.0 )
3198 m_gal->Scale( VECTOR2D( img_scale, img_scale ) );
3199
3200 double imgAlpha = refImg.GetOpacity();
3201
3202 if( aBitmap->IsSelected() || aBitmap->IsBrightened() )
3203 {
3204 COLOR4D color = m_pcbSettings.GetColor( aBitmap, LAYER_ANCHOR );
3205 m_gal->SetIsStroke( true );
3206 m_gal->SetStrokeColor( color );
3207 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth * 2.0f );
3208 m_gal->SetIsFill( false );
3209
3210 // Draws a bounding box.
3211 VECTOR2D bm_size( refImg.GetSize() );
3212 // bm_size is the actual image size in UI.
3213 // but m_canvas scale was previously set to img_scale
3214 // so recalculate size relative to this image size.
3215 bm_size.x /= img_scale;
3216 bm_size.y /= img_scale;
3217 VECTOR2D origin( -bm_size.x / 2.0, -bm_size.y / 2.0 );
3218 VECTOR2D end = origin + bm_size;
3219
3220 m_gal->DrawRectangle( origin, end );
3221
3222 // Give it a floor when selected so you can't lose it too easily
3223 imgAlpha = std::max( imgAlpha, 0.1 );
3224 }
3225 else
3226 {
3227 imgAlpha *= m_pcbSettings.GetColor( aBitmap, aBitmap->GetLayer() ).a;
3228 }
3229
3230 m_gal->DrawBitmap( refImg.GetImage(), imgAlpha );
3231
3232 m_gal->Restore();
3233}
3234
3235
3236void PCB_PAINTER::draw( const PCB_FIELD* aField, int aLayer )
3237{
3238 if( aField->IsVisible() )
3239 draw( static_cast<const PCB_TEXT*>( aField ), aLayer );
3240}
3241
3242
3243void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer )
3244{
3245 wxString resolvedText( aText->GetShownText( FOR_CANVAS ) );
3246
3247 if( resolvedText.Length() == 0 )
3248 return;
3249
3250 if( aLayer == LAYER_LOCKED_ITEM_SHADOW ) // happens only if locked
3251 {
3252 const COLOR4D color = m_pcbSettings.GetColor( aText, aLayer );
3253
3254 m_gal->SetIsFill( true );
3255 m_gal->SetIsStroke( true );
3256 m_gal->SetFillColor( color );
3257 m_gal->SetStrokeColor( color );
3258 m_gal->SetLineWidth( m_lockedShadowMargin );
3259
3260 SHAPE_POLY_SET poly;
3261 aText->TransformShapeToPolygon( poly, aText->GetLayer(), 0, m_maxError, ERROR_OUTSIDE );
3262 m_gal->DrawPolygon( poly );
3263
3264 return;
3265 }
3266
3267 const KIFONT::METRICS& metrics = aText->GetFontMetrics();
3268 TEXT_ATTRIBUTES attrs = aText->GetAttributes();
3269 // Raw attrs are lib frame for FP children, pull scaled values for render.
3270 attrs.m_Size = aText->GetTextSize();
3271 attrs.m_StrokeWidth = aText->GetTextThickness();
3272 const COLOR4D& color = m_pcbSettings.GetColor( aText, aLayer );
3273 bool outline_mode = !viewer_settings()->m_ViewersDisplay.m_DisplayTextFill;
3274
3275 KIFONT::FONT* font = aText->GetDrawFont( &m_pcbSettings );
3276
3277 m_gal->SetStrokeColor( color );
3278 m_gal->SetFillColor( color );
3279 attrs.m_Angle = aText->GetDrawRotation();
3280
3281 if( aText->IsKnockout() )
3282 {
3283 const SHAPE_POLY_SET& finalPoly = aText->GetKnockoutCache( font, resolvedText, m_maxError );
3284
3285 m_gal->SetIsStroke( false );
3286 m_gal->SetIsFill( true );
3287 m_gal->DrawPolygon( finalPoly );
3288 }
3289 else
3290 {
3291 if( outline_mode )
3292 attrs.m_StrokeWidth = m_pcbSettings.m_outlineWidth;
3293 else
3295
3296 if( m_gal->IsFlippedX() && !aText->IsSideSpecific() )
3297 {
3298 // We do not want to change the mirroring for this kind of text
3299 // on the mirrored canvas
3300 // (not mirrored is draw not mirrored and mirrored is draw mirrored)
3301 // So we need to recalculate the text position to keep it at the same position
3302 // on the canvas
3303 VECTOR2I textPos = aText->GetTextPos();
3304 VECTOR2I textWidth = VECTOR2I( aText->GetTextBox( &m_pcbSettings ).GetWidth(), 0 );
3305
3306 if( aText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
3307 textWidth.x = -textWidth.x;
3308 else if( aText->GetHorizJustify() == GR_TEXT_H_ALIGN_CENTER )
3309 textWidth.x = 0;
3310
3311 RotatePoint( textWidth, VECTOR2I( 0, 0 ), aText->GetDrawRotation() );
3312
3313 if( attrs.m_Mirrored )
3314 textPos -= textWidth;
3315 else
3316 textPos += textWidth;
3317
3318 attrs.m_Mirrored = !attrs.m_Mirrored;
3319 strokeText( resolvedText, textPos, attrs, metrics );
3320 return;
3321 }
3322
3323 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
3324
3325 if( font->IsOutline() )
3326 cache = aText->GetRenderCache( font, resolvedText );
3327
3328 if( cache )
3329 {
3330 m_gal->SetLineWidth( attrs.m_StrokeWidth );
3331 m_gal->DrawGlyphs( *cache );
3332 }
3333 else
3334 {
3335 strokeText( resolvedText, aText->GetTextPos(), attrs, metrics );
3336 }
3337 }
3338
3339 // Draw the umbilical line for texts in footprints
3340 FOOTPRINT* fp_parent = aText->GetParentFootprint();
3341
3342 if( fp_parent && aText->IsSelected() )
3343 {
3344 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
3345 m_gal->SetStrokeColor( m_pcbSettings.GetColor( nullptr, LAYER_ANCHOR ) );
3346 m_gal->DrawLine( aText->GetTextPos(), fp_parent->GetPosition() );
3347 }
3348}
3349
3350
3351void PCB_PAINTER::draw( const PCB_TEXTBOX* aTextBox, int aLayer )
3352{
3353 if( aTextBox->Type() == PCB_TABLECELL_T )
3354 {
3355 const PCB_TABLECELL* cell = static_cast<const PCB_TABLECELL*>( aTextBox );
3356
3357 if( cell->GetColSpan() == 0 || cell->GetRowSpan() == 0 )
3358 return;
3359 }
3360
3361 COLOR4D color = m_pcbSettings.GetColor( aTextBox, aLayer );
3362 int thickness = getLineThickness( aTextBox->GetWidth() );
3363 LINE_STYLE lineStyle = aTextBox->GetStroke().GetLineStyle();
3364 wxString resolvedText( aTextBox->GetShownText( FOR_CANVAS ) );
3365 KIFONT::FONT* font = aTextBox->GetDrawFont( &m_pcbSettings );
3366
3367 if( aLayer == LAYER_LOCKED_ITEM_SHADOW ) // happens only if locked
3368 {
3369 const COLOR4D sh_color = m_pcbSettings.GetColor( aTextBox, aLayer );
3370
3371 m_gal->SetIsFill( true );
3372 m_gal->SetIsStroke( false );
3373 m_gal->SetFillColor( sh_color );
3374 m_gal->SetStrokeColor( sh_color );
3375
3376 // Draw the box with a larger thickness than box thickness to show
3377 // the shadow mask
3378 std::vector<VECTOR2I> pts = aTextBox->GetCorners();
3379 int line_thickness = std::max( thickness*3, pcbIUScale.mmToIU( 0.2 ) );
3380
3381 std::deque<VECTOR2D> dpts;
3382
3383 for( const VECTOR2I& pt : pts )
3384 dpts.push_back( VECTOR2D( pt ) );
3385
3386 dpts.push_back( VECTOR2D( pts[0] ) );
3387
3388 m_gal->SetIsStroke( true );
3389 m_gal->SetLineWidth( line_thickness );
3390 m_gal->DrawPolygon( dpts );
3391 }
3392
3393 m_gal->SetFillColor( color );
3394 m_gal->SetStrokeColor( color );
3395 m_gal->SetIsFill( true );
3396 m_gal->SetIsStroke( false );
3397
3398 if( aTextBox->Type() != PCB_TABLECELL_T && aTextBox->IsBorderEnabled() )
3399 {
3400 if( lineStyle <= LINE_STYLE::FIRST_TYPE )
3401 {
3402 // Simple case: faster than not solid hape lines
3403 if( thickness > 0 )
3404 {
3405 std::vector<VECTOR2I> pts = aTextBox->GetCorners();
3406
3407 for( size_t ii = 0; ii < pts.size(); ++ii )
3408 m_gal->DrawSegment( pts[ii], pts[( ii + 1 ) % pts.size()], thickness );
3409 }
3410 }
3411 else
3412 {
3413 std::vector<SHAPE*> shapes = aTextBox->GetPolygonalBoxShape().MakeEffectiveShapes( true );
3414
3415 for( SHAPE* shape : shapes )
3416 {
3417 STROKE_PARAMS::Stroke( shape, lineStyle, thickness, &m_pcbSettings,
3418 [&]( const VECTOR2I& a, const VECTOR2I& b )
3419 {
3420 m_gal->DrawSegment( a, b, thickness );
3421 } );
3422 }
3423
3424 for( SHAPE* shape : shapes )
3425 delete shape;
3426 }
3427 }
3428
3429 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
3430 {
3431 // For now, the textbox is a filled shape.
3432 // so the text drawn on LAYER_LOCKED_ITEM_SHADOW with a thick width is disabled
3433 // If enabled, the thick text position must be offsetted to be exactly on the
3434 // initial text, which is not easy, depending on its rotation and justification.
3435#if 0
3436 const COLOR4D sh_color = m_pcbSettings.GetColor( aTextBox, aLayer );
3437 m_canvas->SetFillColor( sh_color );
3438 m_canvas->SetStrokeColor( sh_color );
3439 attrs.m_StrokeWidth += m_lockedShadowMargin;
3440#else
3441 return;
3442#endif
3443 }
3444
3445 if( aTextBox->IsKnockout() )
3446 {
3447 SHAPE_POLY_SET finalPoly;
3448 aTextBox->TransformTextToPolySet( finalPoly, 0, m_maxError, ERROR_INSIDE );
3449 finalPoly.Fracture();
3450
3451 m_gal->SetIsStroke( false );
3452 m_gal->SetIsFill( true );
3453 m_gal->DrawPolygon( finalPoly );
3454 }
3455 else
3456 {
3457 if( resolvedText.Length() == 0 )
3458 return;
3459
3460 const KIFONT::METRICS& metrics = aTextBox->GetFontMetrics();
3461 TEXT_ATTRIBUTES attrs = aTextBox->GetAttributes();
3462 // Raw attrs are lib frame for FP children, pull scaled size for render.
3463 attrs.m_Size = aTextBox->GetTextSize();
3465
3466 if( m_gal->IsFlippedX() && !aTextBox->IsSideSpecific() )
3467 {
3468 attrs.m_Mirrored = !attrs.m_Mirrored;
3469 strokeText( resolvedText, aTextBox->GetDrawPos( true ), attrs, metrics );
3470 return;
3471 }
3472
3473 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
3474
3475 if( font->IsOutline() )
3476 cache = aTextBox->GetRenderCache( font, resolvedText );
3477
3478 if( cache )
3479 {
3480 m_gal->SetLineWidth( attrs.m_StrokeWidth );
3481 m_gal->DrawGlyphs( *cache );
3482 }
3483 else
3484 {
3485 strokeText( resolvedText, aTextBox->GetDrawPos(), attrs, metrics );
3486 }
3487 }
3488}
3489
3490void PCB_PAINTER::draw( const PCB_TABLE* aTable, int aLayer )
3491{
3492 if( aTable->GetCells().empty() )
3493 return;
3494
3495 for( PCB_TABLECELL* cell : aTable->GetCells() )
3496 {
3497 if( cell->GetColSpan() > 0 || cell->GetRowSpan() > 0 )
3498 draw( static_cast<PCB_TEXTBOX*>( cell ), aLayer );
3499 }
3500
3501 COLOR4D color = m_pcbSettings.GetColor( aTable, aLayer );
3502
3503 aTable->DrawBorders(
3504 [&]( const VECTOR2I& ptA, const VECTOR2I& ptB, const STROKE_PARAMS& stroke )
3505 {
3506 int lineWidth = getLineThickness( stroke.GetWidth() );
3507 LINE_STYLE lineStyle = stroke.GetLineStyle();
3508
3509 m_gal->SetIsFill( false );
3510 m_gal->SetIsStroke( true );
3511 m_gal->SetStrokeColor( color );
3512 m_gal->SetLineWidth( lineWidth );
3513
3514 if( lineStyle <= LINE_STYLE::FIRST_TYPE )
3515 {
3516 m_gal->DrawLine( ptA, ptB );
3517 }
3518 else
3519 {
3520 SHAPE_SEGMENT seg( ptA, ptB );
3521
3522 STROKE_PARAMS::Stroke( &seg, lineStyle, lineWidth, &m_pcbSettings,
3523 [&]( const VECTOR2I& a, const VECTOR2I& b )
3524 {
3525 // DrawLine has problem with 0 length lines so enforce minimum
3526 if( a == b )
3527 m_gal->DrawLine( a+1, b );
3528 else
3529 m_gal->DrawLine( a, b );
3530 } );
3531 }
3532 } );
3533
3534 // Highlight selected tablecells with a background wash.
3535 for( PCB_TABLECELL* cell : aTable->GetCells() )
3536 {
3537 if( aTable->IsSelected() || cell->IsSelected() )
3538 {
3539 std::vector<VECTOR2I> corners = cell->GetCorners();
3540 std::deque<VECTOR2D> pts;
3541
3542 pts.insert( pts.end(), corners.begin(), corners.end() );
3543
3544 m_gal->SetFillColor( color.WithAlpha( 0.5 ) );
3545 m_gal->SetIsFill( true );
3546 m_gal->SetIsStroke( false );
3547 m_gal->DrawPolygon( pts );
3548 }
3549 }
3550}
3551
3552
3553void PCB_PAINTER::draw( const FOOTPRINT* aFootprint, int aLayer )
3554{
3555 if( aLayer == LAYER_ANCHOR )
3556 {
3557 const COLOR4D color = m_pcbSettings.GetColor( aFootprint, aLayer );
3558
3559 // A fixed pixel size at any zoom, which the shader rebuilds from the live projection
3560 double worldScale = m_gal->GetWorldScaleForScreenSpace();
3561 double anchorSize = 5.0 / worldScale;
3562 double anchorThickness = 1.0 / worldScale;
3563
3564 m_gal->SetIsFill( false );
3565 m_gal->SetIsStroke( true );
3566 m_gal->SetStrokeColor( color );
3567 m_gal->SetLineWidth( anchorThickness );
3568 m_gal->SetScreenSpaceMode( true );
3569
3570 VECTOR2D center = aFootprint->GetPosition();
3571 m_gal->DrawLine( center - VECTOR2D( anchorSize, 0 ), center + VECTOR2D( anchorSize, 0 ) );
3572 m_gal->DrawLine( center - VECTOR2D( 0, anchorSize ), center + VECTOR2D( 0, anchorSize ) );
3573
3574 m_gal->SetScreenSpaceMode( false );
3575 }
3576
3577 if( aLayer == LAYER_LOCKED_ITEM_SHADOW && m_frameType == FRAME_PCB_EDITOR ) // happens only if locked
3578 {
3579 const COLOR4D color = m_pcbSettings.GetColor( aFootprint, aLayer );
3580
3581 m_gal->SetIsFill( true );
3582 m_gal->SetIsStroke( false );
3583 m_gal->SetFillColor( color );
3584
3585#if 0 // GetBoundingHull() can be very slow, especially for logos imported from graphics
3586 const SHAPE_POLY_SET& poly = aFootprint->GetBoundingHull();
3587 m_canvas->DrawPolygon( poly );
3588#else
3589 BOX2I bbox = aFootprint->GetBoundingBox( false );
3590 VECTOR2I topLeft = bbox.GetPosition();
3591 VECTOR2I botRight = bbox.GetPosition() + bbox.GetSize();
3592
3593 m_gal->DrawRectangle( topLeft, botRight );
3594
3595 // Use segments to produce a margin with rounded corners
3596 m_gal->DrawSegment( topLeft, VECTOR2I( botRight.x, topLeft.y ), m_lockedShadowMargin );
3597 m_gal->DrawSegment( VECTOR2I( botRight.x, topLeft.y ), botRight, m_lockedShadowMargin );
3598 m_gal->DrawSegment( botRight, VECTOR2I( topLeft.x, botRight.y ), m_lockedShadowMargin );
3599 m_gal->DrawSegment( VECTOR2I( topLeft.x, botRight.y ), topLeft, m_lockedShadowMargin );
3600#endif
3601 }
3602
3603 if( aLayer == LAYER_CONFLICTS_SHADOW && aFootprint->IsConflicting() )
3604 {
3605 const SHAPE_POLY_SET& frontpoly = aFootprint->GetCourtyard( F_CrtYd );
3606 const SHAPE_POLY_SET& backpoly = aFootprint->GetCourtyard( B_CrtYd );
3607
3608 const COLOR4D color = m_pcbSettings.GetColor( aFootprint, aLayer );
3609
3610 m_gal->SetIsFill( true );
3611 m_gal->SetIsStroke( false );
3612 m_gal->SetFillColor( color );
3613
3614 if( frontpoly.OutlineCount() > 0 )
3615 m_gal->DrawPolygon( frontpoly );
3616
3617 if( backpoly.OutlineCount() > 0 )
3618 m_gal->DrawPolygon( backpoly );
3619 }
3620}
3621
3622
3623void PCB_PAINTER::draw( const PCB_GROUP* aGroup, int aLayer )
3624{
3625 if( aLayer == LAYER_ANCHOR )
3626 {
3627 if( aGroup->IsSelected() && !( aGroup->GetParent() && aGroup->GetParent()->IsSelected() ) )
3628 {
3629 // Selected on our own; draw enclosing box
3630 }
3631 else if( aGroup->IsEntered() )
3632 {
3633 // Entered group; draw enclosing box
3634 }
3635 else
3636 {
3637 // Neither selected nor entered; draw nothing at the group level (ie: only draw
3638 // its members)
3639 return;
3640 }
3641
3642 const COLOR4D color = m_pcbSettings.GetColor( aGroup, LAYER_ANCHOR );
3643
3644 m_gal->SetStrokeColor( color );
3645 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth * 2.0f );
3646
3647 BOX2I bbox = aGroup->GetBoundingBox();
3648 VECTOR2I topLeft = bbox.GetPosition();
3649 VECTOR2I width = VECTOR2I( bbox.GetWidth(), 0 );
3650 VECTOR2I height = VECTOR2I( 0, bbox.GetHeight() );
3651
3652 m_gal->DrawLine( topLeft, topLeft + width );
3653 m_gal->DrawLine( topLeft + width, topLeft + width + height );
3654 m_gal->DrawLine( topLeft + width + height, topLeft + height );
3655 m_gal->DrawLine( topLeft + height, topLeft );
3656
3657 wxString name = aGroup->GetName();
3658
3659 if( name.IsEmpty() )
3660 return;
3661
3662 int ptSize = 12;
3663 int scaledSize = abs( KiROUND( m_gal->GetScreenWorldMatrix().GetScale().x * ptSize ) );
3664 int unscaledSize = pcbIUScale.MilsToIU( ptSize );
3665
3666 // Scale by zoom a bit, but not too much
3667 int textSize = ( scaledSize + ( unscaledSize * 2 ) ) / 3;
3668 VECTOR2I textOffset = KiROUND( width.x / 2.0, -textSize * 0.5 );
3669 VECTOR2I titleHeight = KiROUND( 0.0, textSize * 2.0 );
3670
3671 if( PrintableCharCount( name ) * textSize < bbox.GetWidth() )
3672 {
3673 m_gal->DrawLine( topLeft, topLeft - titleHeight );
3674 m_gal->DrawLine( topLeft - titleHeight, topLeft + width - titleHeight );
3675 m_gal->DrawLine( topLeft + width - titleHeight, topLeft + width );
3676
3677 TEXT_ATTRIBUTES attrs;
3678 attrs.m_Italic = true;
3681 attrs.m_Size = VECTOR2I( textSize, textSize );
3682 attrs.m_StrokeWidth = GetPenSizeForNormal( textSize );
3683
3684 KIFONT::FONT::GetFont()->Draw( m_gal, aGroup->GetName(), topLeft + textOffset, attrs,
3685 aGroup->GetFontMetrics() );
3686 }
3687 }
3688}
3689
3690
3691bool KIGFX::ZoneOutlineDrawnOnLayer( bool aOutlineOnly, int aLayer )
3692{
3693 if( aOutlineOnly )
3694 return IsZoneFillLayer( aLayer );
3695
3696 return !IsZoneFillLayer( aLayer );
3697}
3698
3699
3700void PCB_PAINTER::draw( const ZONE* aZone, int aLayer )
3701{
3702 SHAPE_POLY_SET zoneOutlineStorage;
3703 const SHAPE_POLY_SET* zoneOutline = &zoneOutlineStorage;
3704
3705 if( aZone->GetParentFootprint() )
3706 zoneOutlineStorage = aZone->GetBoardOutline();
3707 else
3708 zoneOutline = aZone->Outline();
3709
3710 if( aLayer == LAYER_CONFLICTS_SHADOW )
3711 {
3712 if( aZone->IsConflicting() && aZone->GetIsRuleArea() )
3713 {
3714 COLOR4D color = m_pcbSettings.GetColor( aZone, aLayer );
3715
3716 m_gal->SetIsFill( true );
3717 m_gal->SetIsStroke( false );
3718 m_gal->SetFillColor( color );
3719
3720 m_gal->DrawPolygon( zoneOutline->Outline( 0 ) );
3721 }
3722
3723 return;
3724 }
3725
3726 /*
3727 * aLayer will be the virtual zone layer (LAYER_ZONE_START, ... in GAL_LAYER_ID)
3728 * This is used for draw ordering in the GAL.
3729 * The color for the zone comes from the associated copper layer ( aLayer - LAYER_ZONE_START )
3730 * and the visibility comes from the combination of that copper layer and LAYER_ZONES
3731 */
3732 PCB_LAYER_ID layer;
3733
3734 if( IsZoneFillLayer( aLayer ) )
3735 layer = ToLAYER_ID( aLayer - LAYER_ZONE_START );
3736 else
3737 layer = ToLAYER_ID( aLayer );
3738
3739 if( !aZone->IsOnLayer( layer ) )
3740 return;
3741
3742 COLOR4D color = m_pcbSettings.GetColor( aZone, layer );
3743 std::deque<VECTOR2D> corners;
3744 ZONE_DISPLAY_MODE displayMode = m_pcbSettings.m_ZoneDisplayMode;
3745
3746 if( aZone->IsTeardropArea() )
3747 displayMode = ZONE_DISPLAY_MODE::SHOW_FILLED;
3748
3749 // An outline-only zone draws on the zone layer, above copper, so tracks and pads can't hide it
3750 if( ZoneOutlineDrawnOnLayer( zoneOutlineOnly( aZone ), aLayer ) )
3751 {
3752 bool allowDrawOutline = aZone->GetHatchStyle() != ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER;
3753
3754 if( allowDrawOutline && !m_pcbSettings.m_isPrinting && zoneOutline && zoneOutline->OutlineCount() > 0 )
3755 {
3756 m_gal->SetStrokeColor( color.a > 0.0 ? color.WithAlpha( 1.0 ) : color );
3757 m_gal->SetIsFill( false );
3758 m_gal->SetIsStroke( true );
3759 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
3760
3761 // Draw each contour (main contour and holes)
3762
3763 /*
3764 * m_canvas->DrawPolygon( *outline );
3765 * should be enough, but currently does not work to draw holes contours in a complex
3766 * polygon so each contour is draw as a simple polygon
3767 */
3768
3769 // Draw the main contour(s?)
3770 for( int ii = 0; ii < zoneOutline->OutlineCount(); ++ii )
3771 {
3772 m_gal->DrawPolyline( zoneOutline->COutline( ii ) );
3773
3774 // Draw holes
3775 int holes_count = zoneOutline->HoleCount( ii );
3776
3777 for( int jj = 0; jj < holes_count; ++jj )
3778 m_gal->DrawPolyline( zoneOutline->CHole( ii, jj ) );
3779 }
3780
3781 // Draw hatch lines
3782 for( const SEG& hatchLine : aZone->GetHatchLines() )
3783 m_gal->DrawLine( hatchLine.A, hatchLine.B );
3784 }
3785 }
3786
3787 // Draw the filling
3788 if( IsZoneFillLayer( aLayer )
3789 && ( displayMode == ZONE_DISPLAY_MODE::SHOW_FILLED
3791 || displayMode == ZONE_DISPLAY_MODE::SHOW_TRIANGULATION ) )
3792 {
3793 const std::shared_ptr<SHAPE_POLY_SET>& polySet = aZone->GetFilledPolysList( layer );
3794
3795 if( polySet->OutlineCount() == 0 ) // Nothing to draw
3796 return;
3797
3798 m_gal->SetStrokeColor( color );
3799 m_gal->SetFillColor( color );
3800 m_gal->SetLineWidth( 0 );
3801
3802 if( displayMode == ZONE_DISPLAY_MODE::SHOW_FILLED )
3803 {
3804 m_gal->SetIsFill( true );
3805 m_gal->SetIsStroke( false );
3806 }
3807 else
3808 {
3809 m_gal->SetIsFill( false );
3810 m_gal->SetIsStroke( true );
3811 }
3812
3813 // On Opengl, a not convex filled polygon is usually drawn by using triangles
3814 // as primitives. CacheTriangulation() can create basic triangle primitives to
3815 // draw the polygon solid shape on Opengl. GLU tessellation is much slower,
3816 // so currently we are using our tessellation.
3817 if( m_gal->UsesPolygonTriangulationCache() && !polySet->IsTriangulationUpToDate() )
3818 polySet->CacheTriangulation( true );
3819
3820 m_gal->DrawPolygon( *polySet, displayMode == ZONE_DISPLAY_MODE::SHOW_TRIANGULATION );
3821 }
3822}
3823
3824
3825void PCB_PAINTER::draw( const PCB_BARCODE* aBarcode, int aLayer )
3826{
3827 const COLOR4D& color = m_pcbSettings.GetColor( aBarcode, aLayer );
3828
3829 m_gal->SetIsFill( true );
3830 m_gal->SetIsStroke( false );
3831 m_gal->SetFillColor( color );
3832
3833 // Draw the barcode
3834 SHAPE_POLY_SET shape;
3835
3836 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
3837 aBarcode->GetBoundingHull( shape, aBarcode->GetLayer(), m_lockedShadowMargin, m_maxError, ERROR_INSIDE );
3838 else
3839 aBarcode->TransformShapeToPolySet( shape, aBarcode->GetLayer(), 0, m_maxError, ERROR_INSIDE );
3840
3841 if( shape.OutlineCount() != 0 )
3842 m_gal->DrawPolygon( shape );
3843}
3844
3845
3846void PCB_PAINTER::draw( const PCB_DIMENSION_BASE* aDimension, int aLayer )
3847{
3848 COLOR4D color = m_pcbSettings.GetColor( aDimension, aLayer );
3849
3850 if( aLayer == LAYER_LOCKED_ITEM_SHADOW || aLayer == LAYER_CONSTRAINT_SHADOW )
3851 {
3852 int margin = m_lockedShadowMargin;
3853
3854 if( aLayer == LAYER_CONSTRAINT_SHADOW
3855 && m_pcbSettings.GetHighlightedConstraintMembers().count( aDimension->m_Uuid ) )
3856 {
3857 color = color.Brightened( 0.5 );
3858 margin *= 2;
3859 }
3860
3861 m_gal->SetIsFill( true );
3862 m_gal->SetIsStroke( true );
3863 m_gal->SetFillColor( color );
3864 m_gal->SetStrokeColor( color );
3865 m_gal->SetLineWidth( margin );
3866
3867 for( const std::shared_ptr<SHAPE>& shape : aDimension->GetShapes() )
3868 {
3869 switch( shape->Type() )
3870 {
3871 case SH_SEGMENT:
3872 {
3873 const SEG& seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg();
3874 m_gal->DrawSegment( seg.A, seg.B, margin );
3875 break;
3876 }
3877
3878 case SH_CIRCLE:
3879 {
3880 int radius = static_cast<const SHAPE_CIRCLE*>( shape.get() )->GetRadius();
3881 m_gal->DrawCircle( shape->Centre(), radius );
3882 break;
3883 }
3884
3885 default: break;
3886 }
3887 }
3888
3889 SHAPE_POLY_SET poly;
3890 aDimension->PCB_TEXT::TransformShapeToPolygon( poly, aDimension->GetLayer(), 0, m_maxError, ERROR_OUTSIDE );
3891 m_gal->DrawPolygon( poly );
3892
3893 return;
3894 }
3895
3896 m_gal->SetStrokeColor( color );
3897 m_gal->SetFillColor( color );
3898 m_gal->SetIsFill( false );
3899 m_gal->SetIsStroke( true );
3900
3902
3903 if( outline_mode )
3904 m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
3905 else
3906 m_gal->SetLineWidth( getLineThickness( aDimension->GetLineThickness() ) );
3907
3908 // Draw dimension shapes
3909 // TODO(JE) lift this out
3910 for( const std::shared_ptr<SHAPE>& shape : aDimension->GetShapes() )
3911 {
3912 switch( shape->Type() )
3913 {
3914 case SH_SEGMENT:
3915 {
3916 const SEG& seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg();
3917 m_gal->DrawLine( seg.A, seg.B );
3918 break;
3919 }
3920
3921 case SH_CIRCLE:
3922 {
3923 int radius = static_cast<const SHAPE_CIRCLE*>( shape.get() )->GetRadius();
3924 m_gal->DrawCircle( shape->Centre(), radius );
3925 break;
3926 }
3927
3928 default:
3929 break;
3930 }
3931 }
3932
3933 // Draw text
3934 wxString resolvedText = aDimension->GetShownText( FOR_CANVAS );
3935 TEXT_ATTRIBUTES attrs = aDimension->GetAttributes();
3936
3937 if( m_gal->IsFlippedX() && !aDimension->IsSideSpecific() )
3938 attrs.m_Mirrored = !attrs.m_Mirrored;
3939
3940 if( outline_mode )
3941 attrs.m_StrokeWidth = m_pcbSettings.m_outlineWidth;
3942 else
3944
3945 std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
3946
3947 if( aDimension->GetFont() && aDimension->GetFont()->IsOutline() )
3948 cache = aDimension->GetRenderCache( aDimension->GetFont(), resolvedText );
3949
3950 if( cache )
3951 {
3952 for( const std::unique_ptr<KIFONT::GLYPH>& glyph : *cache )
3953 m_gal->DrawGlyph( *glyph );
3954 }
3955 else
3956 {
3957 strokeText( resolvedText, aDimension->GetTextPos(), attrs, aDimension->GetFontMetrics() );
3958 }
3959}
3960
3961
3962void PCB_PAINTER::draw( const PCB_GRID_ITEM* aGridItem, int aLayer )
3963{
3964 // Grid content (lines/dots/crosses) is rendered by the GAL backend through
3965 // GRID_SOURCE (see PCB_DRAW_PANEL_GAL::prepareGridSources). Only selection
3966 // decorations - outline and centre marker - live here, on LAYER_GRID_ITEMS.
3967 // The item is also registered on m_layer for VIEW::Query (selection); skip
3968 // those passes.
3969 const bool shadow = aLayer == LAYER_LOCKED_ITEM_SHADOW; // happens only if locked
3970
3971 if( aLayer != LAYER_SUBGRIDS && !shadow )
3972 return;
3973
3974 if( !shadow && !aGridItem->IsSelected() )
3975 return;
3976
3977 m_gal->SetLineWidth( shadow ? (float) m_lockedShadowMargin : m_pcbSettings.m_outlineWidth );
3978 m_gal->SetStrokeColor( m_pcbSettings.GetColor( aGridItem, aLayer ) );
3979 m_gal->SetIsFill( false );
3980 m_gal->SetIsStroke( true );
3981
3982 m_gal->Save();
3983 m_gal->Translate( VECTOR2D( aGridItem->GetPosition() ) );
3984 // GAL::Rotate is math-convention; grid orientation is screen-convention - negate.
3985 m_gal->Rotate( -aGridItem->GetOrientation().AsRadians() );
3986
3987 switch( aGridItem->GetGridItemType() )
3988 {
3990 {
3991 // hairline outline at the maximum radius, only over the active phi range
3992 const int radius = aGridItem->GetRadiusExtent();
3993 const double phiMax = aGridItem->GetPhiExtent().AsRadians();
3994 m_gal->DrawArc( VECTOR2D( 0, 0 ), radius, EDA_ANGLE( 0, RADIANS_T ), EDA_ANGLE( phiMax, RADIANS_T ) );
3995
3996 // bounding "pie" radials when phi is less than full circle
3997 if( phiMax + 1e-9 < 2 * M_PI )
3998 {
3999 m_gal->DrawLine( VECTOR2D( 0, 0 ), VECTOR2D( radius, 0 ) );
4000 m_gal->DrawLine( VECTOR2D( 0, 0 ), VECTOR2D( radius * std::cos( phiMax ), radius * std::sin( phiMax ) ) );
4001 }
4002 break;
4003 }
4004
4006 {
4007 // hairline outline rectangle centred on the grid origin
4008 const VECTOR2I extent = aGridItem->GetExtent();
4009 m_gal->DrawLine( VECTOR2D( -extent.x, -extent.y ), VECTOR2D( extent.x, -extent.y ) );
4010 m_gal->DrawLine( VECTOR2D( extent.x, -extent.y ), VECTOR2D( extent.x, extent.y ) );
4011 m_gal->DrawLine( VECTOR2D( extent.x, extent.y ), VECTOR2D( -extent.x, extent.y ) );
4012 m_gal->DrawLine( VECTOR2D( -extent.x, extent.y ), VECTOR2D( -extent.x, -extent.y ) );
4013 break;
4014 }
4015
4016 default:
4017 wxFAIL_MSG( wxT( "draw(PCB_GRID_ITEM*): unhandled PCB_GRID_TYPE" ) );
4018 break;
4019 }
4020
4021 if( shadow )
4022 {
4023 m_gal->Restore();
4024 return;
4025 }
4026
4027 // Centre marker: screen-pixel-sized '+' cross in the LAYER_ANCHOR color, same
4028 // convention FOOTPRINT uses for its anchor (see draw(FOOTPRINT*)).
4029 const double anchorSize = 5.0 / m_gal->GetWorldScale();
4030 const double anchorThickness = 1.0 / m_gal->GetWorldScale();
4031 const COLOR4D anchorColor = m_pcbSettings.GetColor( aGridItem, LAYER_ANCHOR );
4032
4033 m_gal->SetStrokeColor( anchorColor );
4034 m_gal->SetLineWidth( anchorThickness );
4035 m_gal->DrawLine( VECTOR2D( -anchorSize, 0 ), VECTOR2D( anchorSize, 0 ) );
4036 m_gal->DrawLine( VECTOR2D( 0, -anchorSize ), VECTOR2D( 0, anchorSize ) );
4037
4038 m_gal->Restore();
4039}
4040
4041
4042void PCB_PAINTER::draw( const PCB_TARGET* aTarget )
4043{
4044 const COLOR4D strokeColor = m_pcbSettings.GetColor( aTarget, aTarget->GetLayer() );
4045 VECTOR2D position( aTarget->GetPosition() );
4046 double size, radius;
4047
4048 m_gal->SetLineWidth( getLineThickness( aTarget->GetWidth() ) );
4049 m_gal->SetStrokeColor( strokeColor );
4050 m_gal->SetIsFill( false );
4051 m_gal->SetIsStroke( true );
4052
4053 m_gal->Save();
4054 m_gal->Translate( position );
4055
4056 if( aTarget->GetShape() )
4057 {
4058 // shape x
4059 m_gal->Rotate( M_PI / 4.0 );
4060 size = 2.0 * aTarget->GetSize() / 3.0;
4061 radius = aTarget->GetSize() / 2.0;
4062 }
4063 else
4064 {
4065 // shape +
4066 size = aTarget->GetSize() / 2.0;
4067 radius = aTarget->GetSize() / 3.0;
4068 }
4069
4070 m_gal->DrawLine( VECTOR2D( -size, 0.0 ), VECTOR2D( size, 0.0 ) );
4071 m_gal->DrawLine( VECTOR2D( 0.0, -size ), VECTOR2D( 0.0, size ) );
4072 m_gal->DrawCircle( VECTOR2D( 0.0, 0.0 ), radius );
4073
4074 m_gal->Restore();
4075}
4076
4077
4078void PCB_PAINTER::draw( const PCB_POINT* aPoint, int aLayer )
4079{
4080 // aLayer will be the virtual point layer (LAYER_POINT_START, ... in GAL_LAYER_ID).
4081 // This is used for draw ordering in the GAL.
4082 // The cross color comes from LAYER_POINTS and the ring color follows the point's board layer.
4083 // Visibility comes from the combination of that board layer and LAYER_POINTS.
4084
4085 double size = (double)aPoint->GetSize() / 2;
4086
4087 // Keep the width constant, not related to the scale because the anchor
4088 // is just a marker on screen, just draw in pixels
4089 double thickness = m_pcbSettings.m_outlineWidth;
4090
4091 // The general "points" colour
4092 COLOR4D crossColor = m_pcbSettings.GetColor( aPoint, LAYER_POINTS );
4093 // The colour for the ring around the point follows the "real" layer of the point
4094 COLOR4D ringColor = m_pcbSettings.GetColor( aPoint, aPoint->GetLayer() );
4095
4096 if( aLayer == LAYER_LOCKED_ITEM_SHADOW )
4097 {
4098 thickness += m_lockedShadowMargin;
4099 crossColor = m_pcbSettings.GetColor( aPoint, aLayer );
4100 ringColor = m_pcbSettings.GetColor( aPoint, aLayer );
4101 }
4102
4103 VECTOR2D position( aPoint->GetPosition() );
4104
4105 m_gal->SetLineWidth( (float) thickness );
4106 m_gal->SetStrokeColor( crossColor );
4107 m_gal->SetIsFill( false );
4108 m_gal->SetIsStroke( true );
4109
4110 m_gal->Save();
4111 m_gal->Translate( position );
4112
4113 // Draw as X to make it clearer when overlaid on cursor or axes
4114 m_gal->DrawLine( VECTOR2D( -size, -size ), VECTOR2D( size, size ) );
4115 m_gal->DrawLine( VECTOR2D( size, -size ), VECTOR2D( -size, size ) );
4116
4117 // Draw the circle in the layer colour
4118 m_gal->SetStrokeColor( ringColor );
4119 m_gal->DrawCircle( VECTOR2D( 0.0, 0.0 ), size / 2 );
4120
4121 m_gal->Restore();
4122}
4123
4124
4125void PCB_PAINTER::draw( const PCB_MARKER* aMarker, int aLayer )
4126{
4127 // Don't paint invisible markers.
4128 // It would be nice to do this through layer dependencies but we can't do an "or" there today
4129 if( aMarker->GetBoard() && !aMarker->GetBoard()->IsElementVisible( aMarker->GetColorLayer() ) )
4130 return;
4131
4132 // The active marker is redrawn on LAYER_DRC_HIGHLIGHTED so it lands on top of any
4133 // neighbouring inactive markers
4134 if( aLayer == LAYER_DRC_HIGHLIGHTED && !aMarker->IsBrightened() && !aMarker->IsSelected() )
4135 return;
4136
4137 bool isShadow = aLayer == LAYER_MARKER_SHADOWS;
4138 COLOR4D color = m_pcbSettings.GetColor( aMarker, aMarker->GetColorLayer() );
4139 COLOR4D shadowColor = m_pcbSettings.GetColor( aMarker, LAYER_MARKER_SHADOWS );
4140 SHAPE_LINE_CHAIN polygon;
4141
4142 aMarker->SetZoom( 1.0 / sqrt( m_gal->GetZoomFactor() ) );
4143 aMarker->ShapeToPolygon( polygon );
4144
4145 m_gal->Save();
4146 m_gal->Translate( aMarker->GetPosition() );
4147
4148 m_gal->SetStrokeColor( shadowColor );
4149 m_gal->SetFillColor( color );
4150
4151 if( isShadow )
4152 {
4153 m_gal->SetIsFill( false );
4154 m_gal->SetIsStroke( true );
4155 m_gal->SetLineWidth( (float) aMarker->MarkerScale() );
4156 }
4157 else
4158 {
4159 m_gal->SetIsFill( true );
4160 m_gal->SetIsStroke( false );
4161 }
4162
4163 m_gal->DrawPolygon( polygon );
4164 m_gal->Restore();
4165
4166 // Draw the error legend shapes.
4167 if( aLayer == LAYER_DRC_HIGHLIGHTED )
4168 {
4169 COLOR4D legendColor = m_pcbSettings.m_backgroundColor;
4170 double bg_h, bg_s, bg_l;
4171 COLOR4D haloColor;
4172
4173 legendColor.ToHSL( bg_h, bg_s, bg_l );
4174 haloColor.FromHSL( bg_h, bg_s, bg_l < 0.5 ? 1.0 : 0.0 );
4175
4176 m_gal->SetLineWidth( (float) aMarker->MarkerScale() / 3.0f );
4177 m_gal->SetStrokeColor( legendColor.WithAlpha( 1.0 ) );
4178 m_gal->SetFillColor( haloColor.WithAlpha( 0.5 ) );
4179
4180 for( const PCB_SHAPE& shape : aMarker->GetErrorLegendShapes() )
4181 {
4182 if( shape.GetStroke().GetWidth() == 1.0 ) // Item is a legend graphic
4183 {
4184 m_gal->SetIsFill( false );
4185 m_gal->SetIsStroke( true );
4186
4187 if( shape.GetShape() == SHAPE_T::SEGMENT )
4188 {
4189 m_gal->DrawLine( shape.GetStart(), shape.GetEnd() );
4190 }
4191 else if( shape.GetShape() == SHAPE_T::ARC )
4192 {
4193 EDA_ANGLE startAngle, endAngle;
4194 shape.CalcArcAngles( startAngle, endAngle );
4195
4196 m_gal->DrawArc( shape.GetCenter(), shape.GetRadius(), startAngle, shape.GetArcAngle() );
4197 }
4198 }
4199 else // Item is a highlight halo
4200 {
4201 m_gal->SetIsFill( true );
4202 m_gal->SetIsStroke( false );
4203
4204 if( shape.GetShape() == SHAPE_T::SEGMENT )
4205 {
4206 m_gal->DrawSegment( shape.GetStart(), shape.GetEnd(), shape.GetWidth() );
4207 }
4208 else if( shape.GetShape() == SHAPE_T::ARC )
4209 {
4210 EDA_ANGLE startAngle, endAngle;
4211 shape.CalcArcAngles( startAngle, endAngle );
4212
4213 m_gal->DrawArcSegment( shape.GetCenter(), shape.GetRadius(), startAngle, shape.GetArcAngle(),
4214 shape.GetWidth(), ARC_HIGH_DEF );
4215 }
4216 }
4217 }
4218 }
4219}
4220
4221
4222void PCB_PAINTER::draw( const PCB_BOARD_OUTLINE* aBoardOutline, int aLayer )
4223{
4224 if( !aBoardOutline->HasOutline() )
4225 return;
4226
4227 // aBoardOutline makes sense only for the board editor. for fp holder boards
4228 // there are no board outlines area.
4229 const BOARD* brd = aBoardOutline->GetBoard();
4230
4231 if( !brd || brd->GetBoardUse() == BOARD_USE::FPHOLDER )
4232 return;
4233
4235 m_gal->Save();
4236
4237 const COLOR4D& outlineColor = m_pcbSettings.GetColor( aBoardOutline, aLayer );
4238 m_gal->SetFillColor( outlineColor );
4239 m_gal->AdvanceDepth();
4240 m_gal->SetLineWidth( 0 );
4241 m_gal->SetIsFill( true );
4242 m_gal->SetIsStroke( false );
4243 m_gal->DrawPolygon( aBoardOutline->GetOutline() );
4244
4245 m_gal->Restore();
4246}
4247
4248
4250 int aDrillSize, PCB_LAYER_ID aStartLayer, PCB_LAYER_ID aEndLayer )
4251{
4252 double backdrillRadius = aDrillSize / 2.0;
4253 double lineWidth = std::max( backdrillRadius / 16.0, m_pcbSettings.m_outlineWidth * 2.0 );
4254
4255 // Inset so entire graphic is within backdrill extent
4256 backdrillRadius -= lineWidth / 2;
4257
4259 m_gal->AdvanceDepth();
4260 m_gal->SetIsFill( false );
4261 m_gal->SetIsStroke( true );
4262 m_gal->SetLineWidth( (float) lineWidth );
4263
4264 // Draw dashed circle manually with fixed number of segments for consistent appearance
4265 constexpr int NUM_DASHES = 12; // Number of dashes around the circle
4266 EDA_ANGLE dashAngle = ANGLE_360 / ( NUM_DASHES * 2 ); // Dash and gap are equal size
4267
4268 for( int i = 0; i < NUM_DASHES; ++i )
4269 {
4270 EDA_ANGLE startAngle = dashAngle * ( i * 2 );
4271 m_gal->SetStrokeColor( m_pcbSettings.GetColor( aItem, i % 2 ? aStartLayer : aEndLayer ) );
4272 m_gal->DrawArc( aCenter, backdrillRadius, startAngle, dashAngle );
4273 }
4274}
4275
4276
4278{
4279 int size = 0;
4280
4281 // Check to see if the pad or via has a post-machining operation on this layer
4282 if( const PAD* pad = dynamic_cast<const PAD*>( aItem ) )
4283 size = pad->GetPostMachiningKnockout( aLayer );
4284 else if( const PCB_VIA* via = dynamic_cast<const PCB_VIA*>( aItem ) )
4285 size = via->GetPostMachiningKnockout( aLayer );
4286
4287 if( size <= 0 )
4288 return;
4289
4291 m_gal->AdvanceDepth();
4292
4293 double pmRadius = size / 2.0;
4294 // Use a line width proportional to the radius for visibility
4295 double lineWidth = std::max( pmRadius / 16.0, m_pcbSettings.m_outlineWidth * 2.0 );
4296
4297 // Inset so entire graphic is within post machining extent
4298 pmRadius -= lineWidth / 2;
4299
4300 COLOR4D layerColor = m_pcbSettings.GetColor( aItem, aLayer );
4301
4302 m_gal->SetIsFill( false );
4303 m_gal->SetIsStroke( true );
4304 m_gal->SetStrokeColor( layerColor );
4305 m_gal->SetLineWidth( (float) lineWidth );
4306
4307 // Draw dashed circle manually with fixed number of segments for consistent appearance
4308 constexpr int NUM_DASHES = 12; // Number of dashes around the circle
4309 EDA_ANGLE dashAngle = ANGLE_360 / ( NUM_DASHES * 2 ); // Dash and gap are equal size
4310
4311 for( int i = 0; i < NUM_DASHES; ++i )
4312 {
4313 EDA_ANGLE startAngle = dashAngle * ( i * 2 );
4314 m_gal->DrawArc( aCenter, pmRadius, startAngle, dashAngle );
4315 }
4316}
4317
4318
4319const 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:144
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
@ FPHOLDER
Definition board.h:403
@ NORMAL
Inactive layers are shown normally (no high-contrast mode)
@ HIDDEN
Inactive layers are hidden.
@ DIMMED
Inactive layers are dimmed (old high-contrast mode)
@ RATSNEST
Net/netclass colors are shown on ratsnest lines only.
@ ALL
Net/netclass colors are shown on all net copper.
ZONE_DISPLAY_MODE
Determine how zones should be displayed.
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
BOX2< VECTOR2D > BOX2D
Definition box2.h:915
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.
Generic cubic Bezier representation.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
NETINFO_ITEM * GetNet() const
Return NETINFO_ITEM 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.
DRILL_SYMBOL_PROFILE & GetDrillSymbolProfile()
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:85
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:173
virtual bool IsKnockout() const
Definition board_item.h:423
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
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:355
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:560
const KIFONT::METRICS & GetFontMetrics() const
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:267
bool IsSideSpecific() const
virtual int BoardCopperLayerCount() const
Return the total number of copper layers for the board that this item resides on.
virtual bool IsOnCopperLayer() const
Definition board_item.h:190
int GetMaxError() const
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
std::vector< const PCB_DRILL_MAP * > DrillMapsOnLayer(PCB_LAYER_ID aLayer) const
Every map on this layer.
Definition board.cpp:207
BOARD_USE GetBoardUse() const
Get what the board use is.
Definition board.h:430
bool IsElementVisible(GAL_LAYER_ID aLayer) const
Test whether a given element category is visible.
Definition board.cpp:1241
const LSET & GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition board.cpp:1188
std::shared_ptr< const DRILL_SYMBOL_CACHE > DrillSymbolCache() const
Resolved drill symbols, by group and by owning item.
Definition board.cpp:265
KIGFX::COLOR4D GetNetChainColor(const wxString &aChain) const
Definition board.h:1344
const LSET & GetEnabledLayers() const
A proxy function that calls the corresponding function in m_BoardSettings.
Definition board.cpp:1174
BOARD_DESIGN_SETTINGS & GetDesignSettings()
Definition board.cpp:1290
constexpr const Vec & GetPosition() const
Definition box2.h:208
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr const Vec GetEnd() const
Definition box2.h:209
constexpr void SetOrigin(const Vec &pos)
Definition box2.h:234
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition box2.h:143
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr Vec Centre() const
Definition box2.h:94
constexpr const Vec GetCenter() const
Definition box2.h:227
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
constexpr const Vec & GetOrigin() const
Definition box2.h:207
constexpr const SizeVec & GetSize() const
Definition box2.h:203
constexpr void SetEnd(coord_type x, coord_type y)
Definition box2.h:294
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
Grouping rules and symbol assignments, shared by reference so a chart and its map can never disagree ...
EDA_ANGLE Normalize90()
Definition eda_angle.h:260
double AsRadians() const
Definition eda_angle.h:119
wxString GetName() const
Definition eda_group.h:75
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:270
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
bool IsEntered() const
Definition eda_item.h:135
bool IsSelected() const
Definition eda_item.h:134
bool IsBrightened() const
Definition eda_item.h:136
bool IsMoving() const
Definition eda_item.h:132
EDA_ANGLE GetAngle() const
Definition eda_angle.h:533
static bool ShortenSegmentForEndings(VECTOR2I &aStart, VECTOR2I &aEnd, const LINE_ENDING &aStartEnding, const LINE_ENDING &aEndEnding, int aLineWidth)
Shorten a segment body for line endings.
int GetEllipseMinorRadius() const
Definition eda_shape.h:403
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:385
virtual VECTOR2I GetTopLeft() const
Definition eda_shape.h:364
EDA_ANGLE GetEllipseEndAngle() const
Definition eda_shape.h:431
int GetEllipseMajorRadius() const
Definition eda_shape.h:394
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:565
void GetEndingTangents(EDA_ANGLE &aStartTangent, EDA_ANGLE &aEndTangent, int aLineWidth=0) const
Compute outward-facing tangent angles at the start and end of the shape.
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:412
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:177
std::vector< SHAPE * > MakeEffectiveShapesForStroking(int aLineWidth=-1) const
Make a set of SHAPE objects to hand to STROKE_PARAMS::Stroke().
virtual VECTOR2I GetBotRight() const
Definition eda_shape.h:365
bool GetShortenedBodyPolyPoints(const SHAPE_LINE_CHAIN &aOutline, int aOutlineIdx, std::vector< VECTOR2I > &aPoints, int aLineWidth) const
Copy an outline and apply line-ending body shortening when applicable.
const std::vector< SEG > & GetHatchLines() const
bool IsHatchedFill() const
Definition eda_shape.h:132
bool GetLineEndingEndpoints(VECTOR2I &aStartPoint, VECTOR2I &aEndPoint) const
Return the source endpoints used to place line endings.
virtual int GetHatchLineWidth() const
Definition eda_shape.h:167
bool IsSolidFill() const
Definition eda_shape.h:125
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:333
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:283
std::vector< VECTOR2I > GetRectCorners() const
bool IsAnyFill() const
Definition eda_shape.h:120
EDA_ANGLE GetEllipseStartAngle() const
Definition eda_shape.h:422
const LINE_ENDING & GetStartEnding() const
Definition eda_shape.h:179
std::optional< BEZIER< double > > ShortenedBezierCurve(int aLineWidth) const
Return the cubic Bezier curve shortened for line endings.
int GetRectangleHeight() const
bool ShortenArcForEndings(EDA_ANGLE &aStartAngle, EDA_ANGLE &aArcAngle, double aRadius, int aLineWidth) const
Shorten an arc body for line endings.
const LINE_ENDING & GetEndEnding() const
Definition eda_shape.h:185
int GetCornerRadius() const
virtual bool IsVisible() const
Definition eda_text.h:226
KIFONT::FONT * GetFont() const
Definition eda_text.h:286
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:683
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:754
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:239
virtual KIFONT::FONT * GetDrawFont(const RENDER_SETTINGS *aSettings) const
Definition eda_text.cpp:647
const TEXT_ATTRIBUTES & GetAttributes() const
Definition eda_text.h:270
int GetEffectiveTextPenWidth(int aDefaultPenWidth=0) const
The EffectiveTextPenWidth uses the text thickness if > 1 or aDefaultPenWidth.
Definition eda_text.cpp:427
LSET GetPrivateLayers() const
Definition footprint.h:295
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:379
DRAWINGS & GraphicalItems()
Definition footprint.h:358
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:92
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:144
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:243
virtual bool IsOutline() const
Definition font.h:102
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
double r
Red component.
Definition color4d.h:391
double g
Green component.
Definition color4d.h:392
COLOR4D WithAlpha(double aAlpha) const
Return a color with the same color, but the given alpha.
Definition color4d.h:309
static const COLOR4D CLEAR
Definition color4d.h:405
void ToHSL(double &aOutHue, double &aOutSaturation, double &aOutLightness) const
Converts current color (stored in RGB) to HSL format.
Definition color4d.cpp:309
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:321
COLOR4D & Brighten(double aFactor)
Makes the color brighter by a given factor.
Definition color4d.h:206
static const COLOR4D WHITE
Definition color4d.h:403
double a
Alpha component.
Definition color4d.h:394
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:400
void FromHSL(double aInHue, double aInSaturation, double aInLightness)
Change currently used color to the one given by hue, saturation and lightness parameters.
Definition color4d.cpp:342
static const COLOR4D BLACK
Definition color4d.h:404
COLOR4D Mix(const COLOR4D &aColor, double aFactor) const
Return a color that is mixed with the input by a factor.
Definition color4d.h:293
double b
Blue component.
Definition color4d.h:393
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:104
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).
void drawChartSymbols(const PCB_DRILL_CHART *aChart, int aLayer)
PCB_PAINTER(GAL *aGal, FRAME_T aFrameType)
int getLineThickness(int aActualThickness) const
Get the thickness to draw for a line (e.g.
void drawDrillSymbol(const BOARD_ITEM *aItem, int aLayer)
virtual bool HasUniformColor(const VIEW_ITEM *aItem, int aLayer) const override
Return false when drawing the item on the given layer emits more than one colour, so a cached group o...
void renderNetNameForSegment(const SHAPE_SEGMENT &aSeg, const COLOR4D &aColor, const wxString &aNetName) const
bool zoneOutlineOnly(const ZONE *aZone) const
True when a zone's only visual is its outline, as for a rule area or outline-only display.
PCB_VIEWERS_SETTINGS_BASE * viewer_settings()
void draw(const PCB_TRACK *aTrack, int aLayer)
void drawDrillMarks(const PCB_DRILL_MAP *aMap, const std::vector< DRILL_SYMBOL_ENTRY > &aEntries, const COLOR4D &aColor, const KIFONT::METRICS &aFontMetrics)
One hole's marks for one map.
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
Overrides for specific net colors, stored as netcodes for the ratsnest to access easily.
NET_COLOR_MODE m_netColorMode
How to display nets and netclasses with color overrides.
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 IsDimExempt(const VIEW_ITEM *aItem) const override
Return true for an item that keeps its colour on a layer the canvas fades, such as a selected item in...
bool GetShowPageLimits() const override
std::bitset< MAX_LAYERS_FOR_VIEW > m_canvasFadedLayers
Layers the canvas fades for high contrast, see SetCanvasFadedLayers()
void LoadDisplayOptions(const PCB_DISPLAY_OPTIONS &aOptions)
Load settings related to display options (high-contrast mode, full or outline modes for vias/pads/tra...
bool GetLayerDim(int aLayer, COLOR4D &aToward, double &aKeep) const override
Get how the canvas fades a whole layer, see GAL::SetSceneLayerDim().
bool IsCanvasFadedLayer(int aLayer) const
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
int GetShortenDepth(int aLineWidth) const
Return how far the line should be shortened at this ending.
void Draw(KIGFX::GAL &aGal, const VECTOR2I &aPoint, const EDA_ANGLE &aTangent, double aLineWidth, const KIGFX::COLOR4D &aColor) const
LINE_ENDING_STYLE GetStyle() const
Definition line_ending.h:81
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 & AllLayersMask()
Definition lset.cpp:637
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...
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:44
COLOR4D GetPcbColor(bool aIsForSave=false) const
Definition netclass.h:214
bool HasPcbColor() const
Definition netclass.h:213
Handle the data for a net.
Definition netinfo.h:50
const wxString & GetNetChain() const
Definition netinfo.h:122
PAD * GetTerminalPad(int aIndex) const
Definition netinfo.h:125
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
Definition netinfo.h:281
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
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:2060
LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition pad.h:557
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:375
int GetRoundRectCornerRadius(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1181
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
Definition pad.cpp:686
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const override
Return a SHAPE_SEGMENT object representing the pad's hole.
Definition pad.cpp:1361
const BOX2I GetBoundingBox() const override
The bounding box is cached, so this will be efficient most of the time.
Definition pad.cpp:1673
PAD_ATTRIB GetAttribute() const
Definition pad.h:560
const wxString & GetPinFunction() const
Definition pad.h:154
const wxString & GetNumber() const
Definition pad.h:143
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE, bool aIgnoreLineWidth=false) const override
Convert the pad shape to a closed polygon.
Definition pad.cpp:3136
VECTOR2I GetPosition() const override
Definition pad.cpp:255
PCB_LAYER_ID GetTertiaryDrillEndLayer() const
Definition pad.h:542
VECTOR2I GetOffset(PCB_LAYER_ID aLayer) const
Definition pad.cpp:837
VECTOR2I GetDrillSize() const
Definition pad.h:320
PCB_LAYER_ID GetTertiaryDrillStartLayer() const
Definition pad.h:540
bool IsNoConnectPad() const
Definition pad.cpp:603
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition pad.h:205
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition pad.cpp:2067
VECTOR2I GetSize(PCB_LAYER_ID aLayer) const
Definition pad.cpp:297
bool IsFreePad() const
Definition pad.cpp:609
const VECTOR2I & GetSecondaryDrillSize() const
Definition pad.h:511
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition pad.cpp:1795
PAD_DRILL_SHAPE GetDrillShape() const
Definition pad.h:434
std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition pad.cpp:1252
int GetPostMachiningKnockout(PCB_LAYER_ID aLayer) const
Get the knockout diameter for a layer affected by post-machining.
Definition pad.cpp:955
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:2129
PCB_LAYER_ID GetSecondaryDrillStartLayer() const
Definition pad.h:523
const VECTOR2I & GetTertiaryDrillSize() const
Definition pad.h:528
VECTOR2I ShapePos(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1906
PCB_LAYER_ID GetSecondaryDrillEndLayer() const
Definition pad.h:525
DISPLAY_OPTIONS m_Display
bool IsDegenerated(int aThreshold=5) const
virtual double GetLength() const override
Return the length of the arc track.
Definition pcb_track.h:334
EDA_ANGLE GetArcAngleStart() const
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_track.h:307
const VECTOR2I GetFocusPosition() const override
Similar to GetPosition() but allows items to return their visual center rather than their anchor.
Definition pcb_track.h:305
double GetRadius() const
EDA_ANGLE GetAngle() const
const VECTOR2I & GetMid() const
Definition pcb_track.h:300
void GetBoundingHull(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
Add two rectangular polygons separately bounding the barcode's symbol and the barcode's text.
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 drill chart placed on the board, kept in step with the holes.
const std::map< int, int > & RowShapes() const
Curated shape index for each generated row, by table row.
int GetSymbolColumn() const
Table column the symbol is drawn in, or -1 when the chart has no symbol column.
Turns on drill symbols at the holes, for one layer.
bool GetAllSpans() const
int GetSymbolSize() const
bool GetGuideCross() const
bool GetOutlineSlots() const
const DRILL_SPAN & GetSpan() const
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
std::shared_ptr< const SHAPE_POLY_SET > GetBoardOutlines() const
The board outline displaced by this map's offset.
const VECTOR2I & GetOffset() const
EDA_ANGLE GetPhiExtent() const
VECTOR2I GetExtent() const
VECTOR2I GetPosition() const override
EDA_ANGLE GetOrientation() const
int GetRadiusExtent() const
PCB_GRID_TYPE GetGridItemType() const
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 > GetErrorLegendShapes() const
GAL_LAYER_ID GetColorLayer() const
VECTOR2I GetPosition() const override
Definition pcb_marker.h:67
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:80
int GetWidth() const override
bool HasSolderMask() const
Definition pcb_shape.h:347
int GetSolderMaskExpansion() const
bool IsProxyItem() const override
Definition pcb_shape.h:155
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:70
int GetRowSpan() const
int GetColSpan() const
PCB_TABLECELL * GetCell(int aRow, int aCol) const
Definition pcb_table.h:155
std::vector< PCB_TABLECELL * > GetCells() const
Definition pcb_table.h:165
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
Tests whether the border is disabled, as configured by the stroke.
void TransformTextToPolySet(SHAPE_POLY_SET &aBuffer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc) const
Convert the text to a polygonSet describing the actual character strokes (one per segment).
VECTOR2I GetTextSize() const override
VECTOR2I GetDrawPos() const override
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
PCB_SHAPE GetPolygonalBoxShape() const
wxString GetShownText(RESOLUTION_CONTEXT aContext, 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:860
const SHAPE_POLY_SET & GetKnockoutCache(const KIFONT::FONT *aFont, const wxString &forResolvedText, int aMaxError) const
Definition pcb_text.cpp:724
VECTOR2I GetTextPos() const override
Definition pcb_text.cpp:461
int GetTextThickness() const override
Definition pcb_text.cpp:497
EDA_ANGLE GetDrawRotation() const override
Definition pcb_text.cpp:220
wxString GetShownText(RESOLUTION_CONTEXT aContext, int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition pcb_text.cpp:178
VECTOR2I GetTextSize() const override
Definition pcb_text.cpp:470
int GetSolderMaskExpansion() const
const VECTOR2I & GetStart() const
Definition pcb_track.h:98
const VECTOR2I & GetEnd() const
Definition pcb_track.h:95
virtual int GetWidth() const
Definition pcb_track.h:92
PCB_LAYER_ID BottomLayer() const
PCB_LAYER_ID GetTertiaryDrillEndLayer() const
Definition pcb_track.h:848
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:834
int GetWidth() const override
PCB_LAYER_ID GetTertiaryDrillStartLayer() const
Definition pcb_track.h:845
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:422
PCB_LAYER_ID GetSecondaryDrillStartLayer() const
Definition pcb_track.h:831
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:123
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
EDA_ANGLE GetOrientation() const
VECTOR2I GetPosition() const
double GetOpacity() 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 B
Definition seg.h:46
int Length() const
Return the length (this).
Definition seg.h:340
static SEG::ecoord Square(int a)
Definition seg.h:119
ecoord SquaredLength() const
Definition seg.h:345
T * GetAppSettings(const char *aFilename)
Return a handle to the a given settings by type.
VECTOR2I GetEnd() const override
Definition shape_arc.h:208
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:207
const VECTOR2I & GetCenter() const
int GetRadius() const
SHAPE_LINE_CHAIN ConvertToPolyline(int aMaxError) const
Build a polyline approximation of the ellipse or arc.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the 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
virtual void CacheTriangulation(bool aSimplify=false, const TASK_SUBMITTER &aSubmitter={}, bool aBuildIndexes=false)
Build a polygon triangulation, needed to draw a polygon on OpenGL and in some other calculations.
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)
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:180
const VECTOR2I & GetPosition() const
Definition shape_rect.h:164
const VECTOR2I GetSize() const
Definition shape_rect.h:172
int GetHeight() const
Definition shape_rect.h:188
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)
EDA_ORIENTATION m_Angle
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
KIFONT::FONT * m_Font
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition vector2d.h:312
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:406
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:832
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:699
SHAPE_POLY_SET * Outline()
Definition zone.h:421
SHAPE_POLY_SET GetBoardOutline() const
Definition zone.cpp:951
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:806
bool IsTeardropArea() const
Definition zone.h:807
ZONE_BORDER_DISPLAY_STYLE GetHatchStyle() const
Definition zone.h:687
bool IsConflicting() const
For rule areas which exclude footprints (and therefore participate in courtyard conflicts during move...
Definition zone.cpp:593
std::vector< SEG > GetHatchLines() const
Definition zone.cpp:1654
@ MAGENTA
Definition color4d.h:56
@ CYAN
Definition color4d.h:54
@ FOR_CANVAS
Definition common.h:92
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.
KIFACE_BASE & Kiface()
Global KIFACE_BASE "get" accessor.
Definition cvpcb.cpp:165
#define MAX_LAYERS_FOR_VIEW
Definition definitions.h:41
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
@ RADIANS_T
Definition eda_angle.h:31
@ DEGREES_T
Definition eda_angle.h:30
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:445
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:454
#define IGNORE_PARENT_GROUP
Definition eda_item.h:55
@ UNDEFINED
Definition eda_shape.h:55
@ ELLIPSE
Definition eda_shape.h:62
@ SEGMENT
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
Definition eda_shape.h:63
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:58
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:812
@ 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 IsDrillSymbolLayer(int aLayer)
Definition layer_ids.h:963
bool IsPcbLayer(int aLayer)
Test whether a layer is a valid layer for Pcbnew.
Definition layer_ids.h:730
bool IsPadCopperLayer(int aLayer)
Definition layer_ids.h:945
#define BOARD_LAYER_FOR_DRILL_SYMBOL(galLayer)
Definition layer_ids.h:398
bool IsPointsLayer(int aLayer)
Definition layer_ids.h:969
int GetNetnameLayer(int aLayer)
Return a netname layer corresponding to the given layer.
Definition layer_ids.h:918
bool IsClearanceLayer(int aLayer)
Definition layer_ids.h:957
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:741
@ 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:355
@ 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:337
@ LAYER_PADS
Meta control for all pads opacity/visibility (color ignored).
Definition layer_ids.h:288
@ LAYER_DRC_WARNING
Layer for DRC markers with RPT_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:385
@ LAYER_VIA_COPPER_START
Virtual layers for via copper on a given copper layer.
Definition layer_ids.h:359
@ 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:363
@ LAYER_ZONE_START
Virtual layers for stacking zones and tracks on a given copper layer.
Definition layer_ids.h:351
@ 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_SUBGRIDS
Routing/placement subgrids (PCB_GRID_ITEM) visibility and color.
Definition layer_ids.h:323
@ LAYER_DRC_ERROR
Layer for DRC markers with RPT_SEVERITY_ERROR.
Definition layer_ids.h:273
@ LAYER_PAD_HOLEWALLS
Definition layer_ids.h:293
bool IsViaCopperLayer(int aLayer)
Definition layer_ids.h:951
#define DRILL_SYMBOL_LAYER_FOR(boardLayer)
Definition layer_ids.h:397
bool IsNetnameLayer(int aLayer)
Test whether a layer is a netname layer.
Definition layer_ids.h:933
bool IsHoleLayer(int aLayer)
Definition layer_ids.h:803
bool IsExternalCopperLayer(int aLayerId)
Test whether a layer is an external (F_Cu or B_Cu) copper layer.
Definition layer_ids.h:752
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:939
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition lset.cpp:750
MATRIX3x3< double > MATRIX3x3D
Definition matrix3x3.h:469
unsigned CuratedShape(int aIndex)
Pattern index for the nth curated mark.
std::vector< MARKER_PART > BuildMarker(const VECTOR2I &aPosition, int aRadius, unsigned aShapeId)
Decompose one mark, in the order the parts must be drawn.
The Cairo implementation of the graphics abstraction layer.
Definition eda_group.h:30
bool ZoneOutlineDrawnOnLayer(bool aOutlineOnly, int aLayer)
Decide which GAL draw pass paints a zone's outline.
PAD_DRILL_SHAPE
The set of pad drill shapes, used with PAD::{Set,Get}DrillShape()
Definition padstack.h:68
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:102
@ PTH
Plated through hole pad.
Definition padstack.h:97
PAD_SHAPE
The set of pad shapes, used with PAD::{Set,Get}Shape()
Definition padstack.h:51
@ CHAMFERED_RECT
Definition padstack.h:59
@ ROUNDRECT
Definition padstack.h:56
@ TRAPEZOID
Definition padstack.h:55
@ RECTANGLE
Definition padstack.h:53
BARCODE class definition.
Class to handle a set of BOARD_ITEMs.
static bool viaHoleShowsLayerPair(const PCB_VIA *aVia)
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.
One stroke of a mark.
One drawable mark, with the geometry the renderer needs to place it.
VECTOR2I center
const SHAPE_LINE_CHAIN chain
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:227
constexpr KICAD_T BaseType(const KICAD_T aType)
Return the underlying type of the given type.
Definition typeinfo.h:260
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:98
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:95
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DRILL_MAP_T
class PCB_DRILL_MAP, drill symbols drawn at the holes
Definition typeinfo.h:240
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:96
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:103
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:85
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:81
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition typeinfo.h:91
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:93
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition typeinfo.h:99
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:87
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_GRID_ITEM_T
a subgrid placed on a board
Definition typeinfo.h:238
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:94
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_BOARD_OUTLINE_T
class PCB_BOARD_OUTLINE_T, a pcb board outline item
Definition typeinfo.h:104
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition typeinfo.h:86
@ PCB_POINT_T
class PCB_POINT, a 0-dimensional point
Definition typeinfo.h:105
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:97
@ PCB_DRILL_CHART_T
class PCB_DRILL_CHART, a live drill chart derived from PCB_TABLE
Definition typeinfo.h:239
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707