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