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