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 (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
8  * @author Maciej Suminski <maciej.suminski@cern.ch>
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 <board.h>
29 #include <board_design_settings.h>
30 #include <pcb_track.h>
31 #include <pcb_group.h>
32 #include <footprint.h>
33 #include <pad.h>
34 #include <pcb_shape.h>
35 #include <string_utils.h>
36 #include <zone.h>
37 #include <pcb_text.h>
38 #include <pcb_marker.h>
39 #include <pcb_dimension.h>
40 #include <pcb_target.h>
41 #include <advanced_config.h>
42 #include <core/arraydim.h>
43 
44 #include <layer_ids.h>
45 #include <pcb_painter.h>
46 #include <pcb_display_options.h>
47 #include <project/net_settings.h>
48 #include <settings/color_settings.h>
49 
50 #include <convert_basic_shapes_to_polygon.h>
51 #include <gal/graphics_abstraction_layer.h>
52 #include <geometry/geometry_utils.h>
53 #include <geometry/shape_line_chain.h>
54 #include <geometry/shape_rect.h>
55 #include <geometry/shape_segment.h>
56 #include <geometry/shape_simple.h>
57 #include <geometry/shape_circle.h>
58 #include <bezier_curves.h>
59 
60 using namespace KIGFX;
61 
PCB_RENDER_SETTINGS()62 PCB_RENDER_SETTINGS::PCB_RENDER_SETTINGS()
63 {
64     m_backgroundColor = COLOR4D( 0.0, 0.0, 0.0, 1.0 );
65     m_padNumbers = true;
66     m_netNamesOnPads = true;
67     m_netNamesOnTracks = true;
68     m_netNamesOnVias = true;
69     m_zoneOutlines = true;
70     m_zoneDisplayMode = ZONE_DISPLAY_MODE::SHOW_FILLED;
71     m_clearanceDisplayFlags = CL_NONE;
72     m_sketchGraphics = false;
73     m_sketchText = false;
74     m_netColorMode = NET_COLOR_MODE::RATSNEST;
75     m_contrastModeDisplay = HIGH_CONTRAST_MODE::NORMAL;
76     m_ratsnestDisplayMode = RATSNEST_MODE::ALL;
77 
78     m_trackOpacity = 1.0;
79     m_viaOpacity   = 1.0;
80     m_padOpacity   = 1.0;
81     m_zoneOpacity  = 1.0;
82 
83     // By default everything should be displayed as filled
84     for( unsigned int i = 0; i < arrayDim( m_sketchMode ); ++i )
85         m_sketchMode[i] = false;
86 
87     update();
88 }
89 
90 
LoadColors(const COLOR_SETTINGS * aSettings)91 void PCB_RENDER_SETTINGS::LoadColors( const COLOR_SETTINGS* aSettings )
92 {
93     SetBackgroundColor( aSettings->GetColor( LAYER_PCB_BACKGROUND ) );
94 
95     // Init board layers colors:
96     for( int i = 0; i < PCB_LAYER_ID_COUNT; i++ )
97     {
98         m_layerColors[i] = aSettings->GetColor( i );
99 
100         // Guard: if the alpha channel is too small, the layer is not visible.
101         if( m_layerColors[i].a < 0.2 )
102             m_layerColors[i].a = 0.2;
103     }
104 
105     // Init specific graphic layers colors:
106     for( int i = GAL_LAYER_ID_START; i < GAL_LAYER_ID_END; i++ )
107         m_layerColors[i] = aSettings->GetColor( i );
108 
109     // Colors for layers that aren't theme-able
110     m_layerColors[LAYER_PAD_PLATEDHOLES] = aSettings->GetColor( LAYER_PCB_BACKGROUND );
111     m_layerColors[LAYER_VIA_NETNAMES]    = COLOR4D( 0.2, 0.2, 0.2, 0.9 );
112     m_layerColors[LAYER_PAD_NETNAMES]    = COLOR4D( 1.0, 1.0, 1.0, 0.9 );
113     m_layerColors[LAYER_PAD_FR]          = aSettings->GetColor( F_Cu );
114     m_layerColors[LAYER_PAD_BK]          = aSettings->GetColor( B_Cu );
115     m_layerColors[LAYER_PAD_FR_NETNAMES] = COLOR4D( 1.0, 1.0, 1.0, 0.9 );
116     m_layerColors[LAYER_PAD_BK_NETNAMES] = COLOR4D( 1.0, 1.0, 1.0, 0.9 );
117 
118     // Netnames for copper layers
119     for( LSEQ cu = LSET::AllCuMask().CuStack();  cu;  ++cu )
120     {
121         const COLOR4D lightLabel( 0.8, 0.8, 0.8, 0.7 );
122         const COLOR4D darkLabel = lightLabel.Inverted();
123         PCB_LAYER_ID  layer = *cu;
124 
125         if( m_layerColors[layer].GetBrightness() > 0.5 )
126             m_layerColors[GetNetnameLayer( layer )] = darkLabel;
127         else
128             m_layerColors[GetNetnameLayer( layer )] = lightLabel;
129     }
130 
131     update();
132 }
133 
134 
LoadDisplayOptions(const PCB_DISPLAY_OPTIONS & aOptions,bool aShowPageLimits)135 void PCB_RENDER_SETTINGS::LoadDisplayOptions( const PCB_DISPLAY_OPTIONS& aOptions,
136                                               bool aShowPageLimits )
137 {
138     m_hiContrastEnabled = ( aOptions.m_ContrastModeDisplay !=
139                             HIGH_CONTRAST_MODE::NORMAL );
140     m_padNumbers        = aOptions.m_DisplayPadNum;
141     m_sketchGraphics    = !aOptions.m_DisplayGraphicsFill;
142     m_sketchText        = !aOptions.m_DisplayTextFill;
143     m_curvedRatsnestlines = aOptions.m_DisplayRatsnestLinesCurved;
144     m_globalRatsnestlines = aOptions.m_ShowGlobalRatsnest;
145 
146     // Whether to draw tracks, vias & pads filled or as outlines
147     m_sketchMode[LAYER_PADS_TH]      = !aOptions.m_DisplayPadFill;
148     m_sketchMode[LAYER_VIA_THROUGH]  = !aOptions.m_DisplayViaFill;
149     m_sketchMode[LAYER_VIA_BBLIND]   = !aOptions.m_DisplayViaFill;
150     m_sketchMode[LAYER_VIA_MICROVIA] = !aOptions.m_DisplayViaFill;
151     m_sketchMode[LAYER_TRACKS]       = !aOptions.m_DisplayPcbTrackFill;
152 
153     // Net names display settings
154     switch( aOptions.m_DisplayNetNamesMode )
155     {
156     case 0:
157         m_netNamesOnPads   = false;
158         m_netNamesOnTracks = false;
159         m_netNamesOnVias   = false;
160         break;
161 
162     case 1:
163         m_netNamesOnPads   = true;
164         m_netNamesOnTracks = false;
165         m_netNamesOnVias   = true;        // Follow pads or tracks?  For now we chose pads....
166         break;
167 
168     case 2:
169         m_netNamesOnPads   = false;
170         m_netNamesOnTracks = true;
171         m_netNamesOnVias   = false;       // Follow pads or tracks?  For now we chose pads....
172         break;
173 
174     case 3:
175         m_netNamesOnPads   = true;
176         m_netNamesOnTracks = true;
177         m_netNamesOnVias   = true;
178         break;
179     }
180 
181     // Zone display settings
182     m_zoneDisplayMode = aOptions.m_ZoneDisplayMode;
183 
184     // Clearance settings
185     switch( aOptions.m_ShowTrackClearanceMode )
186     {
187         case PCB_DISPLAY_OPTIONS::DO_NOT_SHOW_CLEARANCE:
188             m_clearanceDisplayFlags = CL_NONE;
189             break;
190 
191         case PCB_DISPLAY_OPTIONS::SHOW_TRACK_CLEARANCE_WHILE_ROUTING:
192             m_clearanceDisplayFlags = CL_NEW | CL_TRACKS;
193             break;
194 
195         case PCB_DISPLAY_OPTIONS::SHOW_TRACK_CLEARANCE_WITH_VIA_WHILE_ROUTING:
196             m_clearanceDisplayFlags = CL_NEW | CL_TRACKS | CL_VIAS;
197             break;
198 
199         case PCB_DISPLAY_OPTIONS::SHOW_WHILE_ROUTING_OR_DRAGGING:
200             m_clearanceDisplayFlags = CL_NEW | CL_EDITED | CL_TRACKS | CL_VIAS;
201             break;
202 
203         case PCB_DISPLAY_OPTIONS::SHOW_TRACK_CLEARANCE_WITH_VIA_ALWAYS:
204             m_clearanceDisplayFlags = CL_NEW | CL_EDITED | CL_EXISTING | CL_TRACKS | CL_VIAS;
205             break;
206     }
207 
208     if( aOptions.m_DisplayPadClearance )
209         m_clearanceDisplayFlags |= CL_PADS;
210 
211     m_contrastModeDisplay = aOptions.m_ContrastModeDisplay;
212 
213     m_netColorMode = aOptions.m_NetColorMode;
214 
215     m_ratsnestDisplayMode = aOptions.m_RatsnestMode;
216 
217     m_trackOpacity = aOptions.m_TrackOpacity;
218     m_viaOpacity   = aOptions.m_ViaOpacity;
219     m_padOpacity   = aOptions.m_PadOpacity;
220     m_zoneOpacity  = aOptions.m_ZoneOpacity;
221 
222     m_showPageLimits = aShowPageLimits;
223 }
224 
225 
GetColor(const VIEW_ITEM * aItem,int aLayer) const226 COLOR4D PCB_RENDER_SETTINGS::GetColor( const VIEW_ITEM* aItem, int aLayer ) const
227 {
228     const EDA_ITEM*             item = dynamic_cast<const EDA_ITEM*>( aItem );
229     const BOARD_CONNECTED_ITEM* conItem = dynamic_cast<const BOARD_CONNECTED_ITEM*> ( aItem );
230     int                         netCode = -1;
231     int                         originalLayer = aLayer;
232 
233     // Marker shadows
234     if( aLayer == LAYER_MARKER_SHADOWS )
235         return m_backgroundColor.WithAlpha( 0.6 );
236 
237     if( IsHoleLayer( aLayer ) && m_isPrinting )
238     {
239         // Careful that we don't end up with the same colour for the annular ring and the hole
240         // when printing in B&W.
241         const PAD*     pad = dynamic_cast<const PAD*>( item );
242         const PCB_VIA* via = dynamic_cast<const PCB_VIA*>( item );
243         int            holeLayer = aLayer;
244         int            annularRingLayer = UNDEFINED_LAYER;
245 
246         if( pad && pad->GetAttribute() == PAD_ATTRIB::PTH )
247             annularRingLayer = LAYER_PADS_TH;
248         else if( via && via->GetViaType() == VIATYPE::MICROVIA )
249             annularRingLayer = LAYER_VIA_MICROVIA;
250         else if( via && via->GetViaType() == VIATYPE::BLIND_BURIED )
251             annularRingLayer = LAYER_VIA_BBLIND;
252         else if( via && via->GetViaType() == VIATYPE::THROUGH )
253             annularRingLayer = LAYER_VIA_THROUGH;
254 
255         if( annularRingLayer != UNDEFINED_LAYER
256                 && m_layerColors[ holeLayer ] == m_layerColors[ annularRingLayer ] )
257         {
258             aLayer = LAYER_PCB_BACKGROUND;
259         }
260     }
261 
262     // Zones should pull from the copper layer
263     if( item && ( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T ) )
264     {
265         if( IsZoneLayer( aLayer ) )
266             aLayer = aLayer - LAYER_ZONE_START;
267     }
268 
269     // Hole walls should pull from the copper layer
270     if( aLayer == LAYER_PAD_HOLEWALLS )
271         aLayer = LAYER_PADS_TH;
272     else if( aLayer == LAYER_VIA_HOLEWALLS )
273         aLayer = LAYER_VIA_THROUGH;
274 
275     // Normal path: get the layer base color
276     COLOR4D color = m_layerColors[aLayer];
277 
278     if( !item )
279         return m_layerColors[aLayer];
280 
281     // Selection disambiguation
282     if( item->IsBrightened() )
283         return color.Brightened( m_selectFactor ).WithAlpha( 0.8 );
284 
285     // Normal selection
286     if( item->IsSelected() )
287         color = m_layerColorsSel[aLayer];
288 
289     // Try to obtain the netcode for the item
290     if( conItem )
291         netCode = conItem->GetNetCode();
292 
293     bool highlighted = m_highlightEnabled && m_highlightNetcodes.count( netCode );
294     bool selected    = item->IsSelected();
295 
296     // Apply net color overrides
297     if( conItem && m_netColorMode == NET_COLOR_MODE::ALL && IsNetCopperLayer( aLayer ) )
298     {
299         COLOR4D netColor = COLOR4D::UNSPECIFIED;
300 
301         auto ii = m_netColors.find( netCode );
302 
303         if( ii != m_netColors.end() )
304             netColor = ii->second;
305 
306         if( netColor == COLOR4D::UNSPECIFIED )
307         {
308             auto jj = m_netclassColors.find( conItem->GetNetClassName() );
309 
310             if( jj != m_netclassColors.end() )
311                 netColor = jj->second;
312         }
313 
314         if( netColor == COLOR4D::UNSPECIFIED )
315         {
316             netColor = color;
317         }
318         else if( selected )
319         {
320             // Selection brightening overrides highlighting
321             netColor.Brighten( m_selectFactor );
322         }
323         else if( m_highlightEnabled )
324         {
325             // Highlight brightens objects on all layers and darkens everything else for contrast
326             if( highlighted )
327                 netColor.Brighten( m_highlightFactor );
328             else
329                 netColor.Darken( 1.0 - m_highlightFactor );
330         }
331 
332         color = netColor;
333     }
334     else if( !selected && m_highlightEnabled )
335     {
336         // Single net highlight mode
337         color = m_highlightNetcodes.count( netCode ) ? m_layerColorsHi[aLayer]
338                                                      : m_layerColorsDark[aLayer];
339     }
340 
341     // Apply high-contrast dimming
342     if( m_hiContrastEnabled && m_highContrastLayers.size() && !highlighted && !selected )
343     {
344         PCB_LAYER_ID primary = GetPrimaryHighContrastLayer();
345         bool         isActive = m_highContrastLayers.count( aLayer );
346 
347         switch( originalLayer )
348         {
349         case LAYER_PADS_TH:
350             if( !static_cast<const PAD*>( item )->FlashLayer( primary ) )
351                 isActive = false;
352 
353             break;
354 
355         case LAYER_VIA_BBLIND:
356         case LAYER_VIA_MICROVIA:
357             // Target graphic is active if the via crosses the primary layer
358             if( static_cast<const PCB_VIA*>( item )->GetLayerSet().test( primary ) == 0 )
359                 isActive = false;
360 
361             break;
362 
363         case LAYER_VIA_THROUGH:
364             if( !static_cast<const PCB_VIA*>( item )->FlashLayer( primary ) )
365                 isActive = false;
366 
367             break;
368 
369         case LAYER_PAD_PLATEDHOLES:
370         case LAYER_PAD_HOLEWALLS:
371         case LAYER_NON_PLATEDHOLES:
372             // Pad holes are active is any physical layer is active
373             if( LSET::PhysicalLayersMask().test( primary ) == 0 )
374                 isActive = false;
375 
376             break;
377 
378         case LAYER_VIA_HOLES:
379         case LAYER_VIA_HOLEWALLS:
380             if( static_cast<const PCB_VIA*>( item )->GetViaType() == VIATYPE::BLIND_BURIED
381                     || static_cast<const PCB_VIA*>( item )->GetViaType() == VIATYPE::MICROVIA )
382             {
383                 // A blind or micro via's hole is active if it crosses the primary layer
384                 if( static_cast<const PCB_VIA*>( item )->GetLayerSet().test( primary ) == 0 )
385                     isActive = false;
386             }
387             else
388             {
389                 // A through via's hole is active if any physical layer is active
390                 if( LSET::PhysicalLayersMask().test( primary ) == 0 )
391                     isActive = false;
392             }
393 
394             break;
395 
396         default:
397             break;
398         }
399 
400         if( !isActive )
401         {
402             if( m_contrastModeDisplay == HIGH_CONTRAST_MODE::HIDDEN || IsNetnameLayer( aLayer ) )
403                 color = COLOR4D::CLEAR;
404             else
405                 color = color.Mix( m_layerColors[LAYER_PCB_BACKGROUND], m_hiContrastFactor );
406         }
407     }
408 
409     // Apply per-type opacity overrides
410     if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
411         color.a *= m_trackOpacity;
412     else if( item->Type() == PCB_VIA_T )
413         color.a *= m_viaOpacity;
414     else if( item->Type() == PCB_PAD_T )
415         color.a *= m_padOpacity;
416     else if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
417         color.a *= m_zoneOpacity;
418 
419     // No special modifiers enabled
420     return color;
421 }
422 
423 
PCB_PAINTER(GAL * aGal)424 PCB_PAINTER::PCB_PAINTER( GAL* aGal ) :
425     PAINTER( aGal ),
426     m_maxError( ARC_HIGH_DEF ),
427     m_holePlatingThickness( 0 )
428 {
429 }
430 
431 
getLineThickness(int aActualThickness) const432 int PCB_PAINTER::getLineThickness( int aActualThickness ) const
433 {
434     // if items have 0 thickness, draw them with the outline
435     // width, otherwise respect the set value (which, no matter
436     // how small will produce something)
437     if( aActualThickness == 0 )
438         return m_pcbSettings.m_outlineWidth;
439 
440     return aActualThickness;
441 }
442 
443 
getDrillShape(const PAD * aPad) const444 int PCB_PAINTER::getDrillShape( const PAD* aPad ) const
445 {
446     return aPad->GetDrillShape();
447 }
448 
449 
getDrillSize(const PAD * aPad) const450 VECTOR2D PCB_PAINTER::getDrillSize( const PAD* aPad ) const
451 {
452     return VECTOR2D( aPad->GetDrillSize() );
453 }
454 
455 
getDrillSize(const PCB_VIA * aVia) const456 int PCB_PAINTER::getDrillSize( const PCB_VIA* aVia ) const
457 {
458     return aVia->GetDrillValue();
459 }
460 
461 
Draw(const VIEW_ITEM * aItem,int aLayer)462 bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
463 {
464     const BOARD_ITEM* item = dynamic_cast<const BOARD_ITEM*>( aItem );
465 
466     if( !item )
467         return false;
468 
469     if( const BOARD* board = item->GetBoard() )
470     {
471         BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
472         m_maxError = bds.m_MaxError;
473         m_holePlatingThickness = bds.GetHolePlatingThickness();
474     }
475     else
476     {
477         m_maxError = ARC_HIGH_DEF;
478         m_holePlatingThickness = 0;
479     }
480 
481     // the "cast" applied in here clarifies which overloaded draw() is called
482     switch( item->Type() )
483     {
484     case PCB_TRACE_T:
485         draw( static_cast<const PCB_TRACK*>( item ), aLayer );
486         break;
487 
488     case PCB_ARC_T:
489         draw( static_cast<const PCB_ARC*>( item ), aLayer );
490         break;
491 
492     case PCB_VIA_T:
493         draw( static_cast<const PCB_VIA*>( item ), aLayer );
494         break;
495 
496     case PCB_PAD_T:
497         draw( static_cast<const PAD*>( item ), aLayer );
498         break;
499 
500     case PCB_SHAPE_T:
501     case PCB_FP_SHAPE_T:
502         draw( static_cast<const PCB_SHAPE*>( item ), aLayer );
503         break;
504 
505     case PCB_TEXT_T:
506         draw( static_cast<const PCB_TEXT*>( item ), aLayer );
507         break;
508 
509     case PCB_FP_TEXT_T:
510         draw( static_cast<const FP_TEXT*>( item ), aLayer );
511         break;
512 
513     case PCB_FOOTPRINT_T:
514         draw( static_cast<const FOOTPRINT*>( item ), aLayer );
515         break;
516 
517     case PCB_GROUP_T:
518         draw( static_cast<const PCB_GROUP*>( item ), aLayer );
519         break;
520 
521     case PCB_ZONE_T:
522         draw( static_cast<const ZONE*>( item ), aLayer );
523         break;
524 
525     case PCB_FP_ZONE_T:
526         draw( static_cast<const ZONE*>( item ), aLayer );
527         break;
528 
529     case PCB_DIM_ALIGNED_T:
530     case PCB_DIM_CENTER_T:
531     case PCB_DIM_ORTHOGONAL_T:
532     case PCB_DIM_LEADER_T:
533         draw( static_cast<const PCB_DIMENSION_BASE*>( item ), aLayer );
534         break;
535 
536     case PCB_TARGET_T:
537         draw( static_cast<const PCB_TARGET*>( item ) );
538         break;
539 
540     case PCB_MARKER_T:
541         draw( static_cast<const PCB_MARKER*>( item ), aLayer );
542         break;
543 
544     default:
545         // Painter does not know how to draw the object
546         return false;
547     }
548 
549     // Draw bounding boxes after drawing objects so they can be seen.
550     if( ADVANCED_CFG::GetCfg().m_DrawBoundingBoxes )
551     {
552         // Show bounding boxes of painted objects for debugging.
553         EDA_RECT box = item->GetBoundingBox();
554         m_gal->SetIsFill( false );
555         m_gal->SetIsStroke( true );
556 
557         if( item->Type() == PCB_FOOTPRINT_T )
558         {
559             m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) :
560                                    COLOR4D( MAGENTA ) );
561         }
562         else
563         {
564             m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) :
565                                    COLOR4D( 0.4, 0.4, 0.4, 1 ) );
566         }
567 
568         m_gal->SetLineWidth( 1 );
569         m_gal->DrawRectangle( box.GetOrigin(), box.GetEnd() );
570 
571         if( item->Type() == PCB_FOOTPRINT_T )
572         {
573             m_gal->SetStrokeColor( item->IsSelected() ? COLOR4D( 1.0, 0.2, 0.2, 1 ) :
574                                    COLOR4D( CYAN ) );
575 
576             const FOOTPRINT* fp = static_cast<const FOOTPRINT*>( item );
577 
578             if( fp )
579             {
580                 SHAPE_POLY_SET convex = fp->GetBoundingHull();
581 
582                 m_gal->DrawPolyline( convex.COutline( 0 ) );
583             }
584         }
585     }
586 
587     return true;
588 }
589 
590 
draw(const PCB_TRACK * aTrack,int aLayer)591 void PCB_PAINTER::draw( const PCB_TRACK* aTrack, int aLayer )
592 {
593     VECTOR2D start( aTrack->GetStart() );
594     VECTOR2D end( aTrack->GetEnd() );
595     int      width = aTrack->GetWidth();
596     COLOR4D  color = m_pcbSettings.GetColor( aTrack, aLayer );
597 
598     if( IsNetnameLayer( aLayer ) )
599     {
600         if( !m_pcbSettings.m_netNamesOnTracks )
601             return;
602 
603         if( aTrack->GetNetCode() <= NETINFO_LIST::UNCONNECTED )
604             return;
605 
606         VECTOR2D line = ( end - start );
607         double length = line.EuclideanNorm();
608 
609         // Check if the track is long enough to have a netname displayed
610         if( length < 10 * width )
611             return;
612 
613         const wxString& netName = UnescapeString( aTrack->GetShortNetname() );
614         double   textSize = width;
615         double   penWidth = width / 12.0;
616         VECTOR2D textPosition = start + line / 2.0;     // center of the track
617         double   textOrientation;
618 
619         if( end.y == start.y ) // horizontal
620         {
621             textOrientation = 0;
622             textPosition.y += penWidth;
623         }
624         else if( end.x == start.x ) // vertical
625         {
626             textOrientation = M_PI / 2;
627             textPosition.x += penWidth;
628         }
629         else
630         {
631             textOrientation = -atan( line.y / line.x );
632             textPosition.x += penWidth / 1.4;
633             textPosition.y += penWidth / 1.4;
634         }
635 
636 
637         m_gal->SetIsStroke( true );
638         m_gal->SetIsFill( false );
639         m_gal->SetStrokeColor( color );
640         m_gal->SetLineWidth( penWidth );
641         m_gal->SetFontBold( false );
642         m_gal->SetFontItalic( false );
643         m_gal->SetFontUnderlined( false );
644         m_gal->SetTextMirrored( false );
645         m_gal->SetGlyphSize( VECTOR2D( textSize * 0.55, textSize * 0.55 ) );
646         m_gal->SetHorizontalJustify( GR_TEXT_HJUSTIFY_CENTER );
647         m_gal->SetVerticalJustify( GR_TEXT_VJUSTIFY_CENTER );
648         m_gal->BitmapText( netName, textPosition, textOrientation );
649 
650         return;
651     }
652     else if( IsCopperLayer( aLayer ) )
653     {
654         // Draw a regular track
655         bool outline_mode = m_pcbSettings.m_sketchMode[LAYER_TRACKS];
656         m_gal->SetStrokeColor( color );
657         m_gal->SetFillColor( color );
658         m_gal->SetIsStroke( outline_mode );
659         m_gal->SetIsFill( not outline_mode );
660         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
661 
662         m_gal->DrawSegment( start, end, width );
663     }
664 
665     // Clearance lines
666     constexpr int clearanceFlags = PCB_RENDER_SETTINGS::CL_EXISTING
667                                  | PCB_RENDER_SETTINGS::CL_TRACKS;
668 
669     if( ( m_pcbSettings.m_clearanceDisplayFlags & clearanceFlags ) == clearanceFlags )
670     {
671         int clearance = aTrack->GetOwnClearance( m_pcbSettings.GetActiveLayer() );
672 
673         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
674         m_gal->SetIsFill( false );
675         m_gal->SetIsStroke( true );
676         m_gal->SetStrokeColor( color );
677         m_gal->DrawSegment( start, end, width + clearance * 2 );
678     }
679 }
680 
681 
draw(const PCB_ARC * aArc,int aLayer)682 void PCB_PAINTER::draw( const PCB_ARC* aArc, int aLayer )
683 {
684     VECTOR2D center( aArc->GetCenter() );
685     int      width = aArc->GetWidth();
686     COLOR4D  color = m_pcbSettings.GetColor( aArc, aLayer );
687     double   radius = aArc->GetRadius();
688     double   start_angle = DECIDEG2RAD( aArc->GetArcAngleStart() );
689     double   angle = DECIDEG2RAD( aArc->GetAngle() );
690 
691     if( IsNetnameLayer( aLayer ) )
692     {
693         // Ummm, yeah.  Anyone fancy implementing text on a path?
694         return;
695     }
696     else if( IsCopperLayer( aLayer ) )
697     {
698         // Draw a regular track
699         bool outline_mode = m_pcbSettings.m_sketchMode[LAYER_TRACKS];
700         m_gal->SetStrokeColor( color );
701         m_gal->SetFillColor( color );
702         m_gal->SetIsStroke( outline_mode );
703         m_gal->SetIsFill( not outline_mode );
704         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
705 
706         m_gal->DrawArcSegment( center, radius, start_angle, start_angle + angle, width, m_maxError );
707     }
708 
709     // Clearance lines
710     constexpr int clearanceFlags = PCB_RENDER_SETTINGS::CL_EXISTING
711                                  | PCB_RENDER_SETTINGS::CL_TRACKS;
712 
713     if( ( m_pcbSettings.m_clearanceDisplayFlags & clearanceFlags ) == clearanceFlags )
714     {
715         int clearance = aArc->GetOwnClearance( m_pcbSettings.GetActiveLayer() );
716 
717         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
718         m_gal->SetIsFill( false );
719         m_gal->SetIsStroke( true );
720         m_gal->SetStrokeColor( color );
721 
722         m_gal->DrawArcSegment( center, radius, start_angle, start_angle + angle,
723                                width + clearance * 2, m_maxError );
724     }
725 
726 // Debug only: enable this code only to test the TransformArcToPolygon function
727 // and display the polygon outline created by it.
728 // arcs on F_Cu are approximated with ERROR_INSIDE, others with ERROR_OUTSIDE
729 #if 0
730     SHAPE_POLY_SET cornerBuffer;
731     ERROR_LOC errorloc = aLayer == F_Cu ? ERROR_LOC::ERROR_INSIDE : ERROR_LOC::ERROR_OUTSIDE;
732     TransformArcToPolygon( cornerBuffer, aArc->GetStart(), aArc->GetMid(), aArc->GetEnd(), width,
733                            m_maxError, errorloc );
734     m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
735     m_gal->SetIsFill( false );
736     m_gal->SetIsStroke( true );
737     m_gal->SetStrokeColor( COLOR4D( 0, 0, 1.0, 1.0 ) );
738     m_gal->DrawPolygon( cornerBuffer );
739 #endif
740 
741 // Debug only: enable this code only to test the SHAPE_ARC::ConvertToPolyline function
742 // and display the polyline created by it.
743 #if 0
744     SHAPE_ARC arc( aArc->GetCenter(), aArc->GetStart(), aArc->GetAngle() / 10.0, aArc->GetWidth() );
745     SHAPE_LINE_CHAIN arcSpine = arc.ConvertToPolyline( m_maxError );
746     m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
747     m_gal->SetIsFill( false );
748     m_gal->SetIsStroke( true );
749     m_gal->SetStrokeColor( COLOR4D( 0.3, 0.2, 0.5, 1.0 ) );
750 
751     for( int idx = 1; idx < arcSpine.PointCount(); idx++ )
752         m_gal->DrawSegment( arcSpine.CPoint( idx-1 ), arcSpine.CPoint( idx ), aArc->GetWidth() );
753 #endif
754 }
755 
756 
draw(const PCB_VIA * aVia,int aLayer)757 void PCB_PAINTER::draw( const PCB_VIA* aVia, int aLayer )
758 {
759     COLOR4D  color = m_pcbSettings.GetColor( aVia, aLayer );
760     VECTOR2D center( aVia->GetStart() );
761 
762     if( color == COLOR4D::CLEAR )
763         return;
764 
765     // Draw description layer
766     if( IsNetnameLayer( aLayer ) )
767     {
768         VECTOR2D position( center );
769 
770         // Is anything that we can display enabled?
771         if( !m_pcbSettings.m_netNamesOnVias || aVia->GetNetname().empty() )
772             return;
773 
774         double maxSize = PCB_RENDER_SETTINGS::MAX_FONT_SIZE;
775         double size = aVia->GetWidth();
776 
777         // Font size limits
778         if( size > maxSize )
779             size = maxSize;
780 
781         m_gal->Save();
782         m_gal->Translate( position );
783 
784         // Default font settings
785         m_gal->ResetTextAttributes();
786         m_gal->SetStrokeColor( m_pcbSettings.GetColor( nullptr, aLayer ) );
787 
788         // Set the text position to the pad shape position (the pad position is not the best place)
789         VECTOR2D textpos( 0.0, 0.0 );
790 
791         wxString netname = UnescapeString( aVia->GetShortNetname() );
792 
793         // approximate the size of net name text:
794         double tsize = 1.5 * size / std::max( PrintableCharCount( netname ), 1 );
795         tsize = std::min( tsize, size );
796 
797         // Use a smaller text size to handle interline, pen size..
798         tsize *= 0.7;
799         VECTOR2D namesize( tsize, tsize );
800 
801         m_gal->SetGlyphSize( namesize );
802         m_gal->SetLineWidth( namesize.x / 12.0 );
803         m_gal->BitmapText( netname, textpos, 0.0 );
804 
805         m_gal->Restore();
806 
807         return;
808     }
809     else if( aLayer == LAYER_VIA_HOLEWALLS )
810     {
811         m_gal->SetIsFill( false );
812         m_gal->SetIsStroke( true );
813         m_gal->SetStrokeColor( color );
814         m_gal->SetLineWidth( m_holePlatingThickness );
815 
816         m_gal->DrawCircle( center, ( getDrillSize( aVia ) + m_holePlatingThickness ) / 2.0 );
817 
818         return;
819     }
820 
821     bool sketchMode = false;
822 
823     switch( aVia->GetViaType() )
824     {
825     case VIATYPE::THROUGH:      sketchMode = m_pcbSettings.m_sketchMode[LAYER_VIA_THROUGH];  break;
826     case VIATYPE::BLIND_BURIED: sketchMode = m_pcbSettings.m_sketchMode[LAYER_VIA_BBLIND];   break;
827     case VIATYPE::MICROVIA:     sketchMode = m_pcbSettings.m_sketchMode[LAYER_VIA_MICROVIA]; break;
828     default:                    wxASSERT( false );                                           break;
829     }
830 
831     if( sketchMode )
832     {
833         // Outline mode
834         m_gal->SetIsStroke( true );
835         m_gal->SetIsFill( false );
836         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
837         m_gal->SetStrokeColor( color );
838     }
839     else
840     {
841         // Filled mode
842         m_gal->SetIsFill( true );
843         m_gal->SetIsStroke( false );
844         m_gal->SetFillColor( color );
845     }
846 
847     if( aLayer == LAYER_VIA_HOLES )
848     {
849         m_gal->DrawCircle( center, getDrillSize( aVia ) / 2.0 );
850     }
851     else if( aLayer == LAYER_VIA_THROUGH || m_pcbSettings.GetDrawIndividualViaLayers() )
852     {
853         m_gal->DrawCircle( center, aVia->GetWidth() / 2.0 );
854     }
855     else if( aLayer == LAYER_VIA_BBLIND || aLayer == LAYER_VIA_MICROVIA )
856     {
857         // Outer circles of blind/buried and micro-vias are drawn in a special way to indicate the
858         // top and bottom layers
859         PCB_LAYER_ID layerTop, layerBottom;
860         aVia->LayerPair( &layerTop, &layerBottom );
861 
862         double radius =  aVia->GetWidth() / 2.0;
863 
864         if( !sketchMode )
865             m_gal->SetLineWidth( ( aVia->GetWidth() - aVia->GetDrillValue() ) / 2.0 );
866 
867         m_gal->DrawArc( center, radius, M_PI * -0.375, M_PI * 0.375 );
868         m_gal->DrawArc( center, radius, M_PI * 0.625, M_PI * 1.375 );
869 
870         if( sketchMode )
871             m_gal->SetStrokeColor( m_pcbSettings.GetColor( aVia, layerTop ) );
872         else
873             m_gal->SetFillColor( m_pcbSettings.GetColor( aVia, layerTop ) );
874 
875         m_gal->DrawArc( center, radius, M_PI * 1.375, M_PI * 1.625 );
876 
877         if( sketchMode )
878             m_gal->SetStrokeColor( m_pcbSettings.GetColor( aVia, layerBottom ) );
879         else
880             m_gal->SetFillColor( m_pcbSettings.GetColor( aVia, layerBottom ) );
881 
882         m_gal->DrawArc( center, radius, M_PI * 0.375, M_PI * 0.625 );
883     }
884 
885     // Clearance lines
886     constexpr int clearanceFlags = PCB_RENDER_SETTINGS::CL_EXISTING | PCB_RENDER_SETTINGS::CL_VIAS;
887 
888     if( ( m_pcbSettings.m_clearanceDisplayFlags & clearanceFlags ) == clearanceFlags
889             && aLayer != LAYER_VIA_HOLES )
890     {
891         PCB_LAYER_ID activeLayer = m_pcbSettings.GetActiveLayer();
892         double       radius;
893 
894         if( aVia->FlashLayer( activeLayer ) )
895             radius = aVia->GetWidth() / 2.0;
896         else
897             radius = getDrillSize( aVia ) / 2.0 + m_holePlatingThickness;
898 
899         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
900         m_gal->SetIsFill( false );
901         m_gal->SetIsStroke( true );
902         m_gal->SetStrokeColor( color );
903         m_gal->DrawCircle( center, radius + aVia->GetOwnClearance( activeLayer ) );
904     }
905 }
906 
907 
draw(const PAD * aPad,int aLayer)908 void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
909 {
910     COLOR4D                color = m_pcbSettings.GetColor( aPad, aLayer );
911 
912     if( IsNetnameLayer( aLayer ) )
913     {
914         // Is anything that we can display enabled?
915         if( m_pcbSettings.m_netNamesOnPads || m_pcbSettings.m_padNumbers )
916         {
917             bool displayNetname = ( m_pcbSettings.m_netNamesOnPads && !aPad->GetNetname().empty() );
918             EDA_RECT padBBox = aPad->GetBoundingBox();
919             VECTOR2D position = padBBox.Centre();
920             VECTOR2D padsize = VECTOR2D( padBBox.GetSize() );
921 
922             if( aPad->GetShape() != PAD_SHAPE::CUSTOM )
923             {
924                 // Don't allow a 45º rotation to bloat a pad's bounding box unnecessarily
925                 double limit = std::min( aPad->GetSize().x, aPad->GetSize().y ) * 1.1;
926 
927                 if( padsize.x > limit && padsize.y > limit )
928                 {
929                     padsize.x = limit;
930                     padsize.y = limit;
931                 }
932             }
933 
934             double maxSize = PCB_RENDER_SETTINGS::MAX_FONT_SIZE;
935             double size = padsize.y;
936 
937             m_gal->Save();
938             m_gal->Translate( position );
939 
940             // Keep the size ratio for the font, but make it smaller
941             if( padsize.x < padsize.y )
942             {
943                 m_gal->Rotate( DECIDEG2RAD( -900.0 ) );
944                 size = padsize.x;
945                 std::swap( padsize.x, padsize.y );
946             }
947 
948             // Font size limits
949             if( size > maxSize )
950                 size = maxSize;
951 
952             // Default font settings
953             m_gal->SetHorizontalJustify( GR_TEXT_HJUSTIFY_CENTER );
954             m_gal->SetVerticalJustify( GR_TEXT_VJUSTIFY_CENTER );
955             m_gal->SetFontBold( false );
956             m_gal->SetFontItalic( false );
957             m_gal->SetFontUnderlined( false );
958             m_gal->SetTextMirrored( false );
959             m_gal->SetStrokeColor( m_pcbSettings.GetColor( aPad, aLayer ) );
960             m_gal->SetIsStroke( true );
961             m_gal->SetIsFill( false );
962 
963             // We have already translated the GAL to be centered at the center of the pad's
964             // bounding box
965             VECTOR2D textpos( 0.0, 0.0 );
966 
967             // Divide the space, to display both pad numbers and netnames and set the Y text
968             // position to display 2 lines
969             if( displayNetname && m_pcbSettings.m_padNumbers )
970             {
971                 size = size / 2.5;
972                 textpos.y = size / 1.7;
973             }
974 
975             if( displayNetname )
976             {
977                 wxString netname = UnescapeString( aPad->GetShortNetname() );
978                 wxString pinType = aPad->GetPinType();
979 
980                 // If the pad is actually not connected (unique pad in the net),
981                 // shorten the displayed netname (actual name not useful)
982                 // Can happen if the pad netname is edited inside the board editor, therefore
983                 // having a netname not coming from schematic
984                 if( netname.StartsWith( "unconnected-(" ) )
985                 {
986                     if( pinType == wxT( "no_connect" ) || pinType.EndsWith( wxT( "+no_connect" ) ) )
987                         netname = "x";
988                     else if( pinType == wxT( "free" ) )
989                         netname = "*";
990                 }
991 
992                 // approximate the size of net name text:
993                 double tsize = 1.5 * padsize.x / std::max( PrintableCharCount( netname ), 1 );
994                 tsize = std::min( tsize, size );
995 
996                 // Use a smaller text size to handle interline, pen size...
997                 tsize *= 0.7;
998                 VECTOR2D namesize( tsize, tsize );
999 
1000                 m_gal->SetGlyphSize( namesize );
1001                 m_gal->SetLineWidth( namesize.x / 12.0 );
1002                 m_gal->BitmapText( netname, textpos, 0.0 );
1003             }
1004 
1005             if( m_pcbSettings.m_padNumbers )
1006             {
1007                 const wxString& padNumber = aPad->GetNumber();
1008                 textpos.y = -textpos.y;
1009 
1010                 // approximate the size of the pad number text:
1011                 double tsize = 1.5 * padsize.x / std::max( PrintableCharCount( padNumber ), 1 );
1012                 tsize = std::min( tsize, size );
1013 
1014                 // Use a smaller text size to handle interline, pen size...
1015                 tsize *= 0.7;
1016                 tsize = std::min( tsize, size );
1017                 VECTOR2D numsize( tsize, tsize );
1018 
1019                 m_gal->SetGlyphSize( numsize );
1020                 m_gal->SetLineWidth( numsize.x / 12.0 );
1021                 m_gal->BitmapText( padNumber, textpos, 0.0 );
1022             }
1023 
1024             m_gal->Restore();
1025         }
1026 
1027         return;
1028     }
1029     else if( aLayer == LAYER_PAD_HOLEWALLS )
1030     {
1031         m_gal->SetIsFill( false );
1032         m_gal->SetIsStroke( true );
1033         m_gal->SetLineWidth( m_holePlatingThickness );
1034         m_gal->SetStrokeColor( color );
1035 
1036         const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
1037         int                  holeSize = seg->GetWidth() + m_holePlatingThickness;
1038 
1039         if( seg->GetSeg().A == seg->GetSeg().B )    // Circular hole
1040             m_gal->DrawCircle( seg->GetSeg().A, holeSize / 2 );
1041         else
1042             m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, holeSize );
1043 
1044         return;
1045     }
1046 
1047     if( m_pcbSettings.m_sketchMode[LAYER_PADS_TH] )
1048     {
1049         // Outline mode
1050         m_gal->SetIsFill( false );
1051         m_gal->SetIsStroke( true );
1052         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1053         m_gal->SetStrokeColor( color );
1054     }
1055     else
1056     {
1057         // Filled mode
1058         m_gal->SetIsFill( true );
1059         m_gal->SetIsStroke( false );
1060         m_gal->SetFillColor( color );
1061     }
1062 
1063     if( aLayer == LAYER_PAD_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES )
1064     {
1065         const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
1066 
1067         if( seg->GetSeg().A == seg->GetSeg().B )    // Circular hole
1068             m_gal->DrawCircle( seg->GetSeg().A, getDrillSize( aPad ).x / 2 );
1069         else
1070             m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() );
1071     }
1072     else
1073     {
1074         wxSize pad_size = aPad->GetSize();
1075         wxSize margin;
1076 
1077         switch( aLayer )
1078         {
1079         case F_Mask:
1080         case B_Mask:
1081             margin.x = margin.y = aPad->GetSolderMaskMargin();
1082             break;
1083 
1084         case F_Paste:
1085         case B_Paste:
1086             margin = aPad->GetSolderPasteMargin();
1087             break;
1088 
1089         default:
1090             margin.x = margin.y = 0;
1091             break;
1092         }
1093 
1094         std::unique_ptr<PAD>            dummyPad;
1095         std::shared_ptr<SHAPE_COMPOUND> shapes;
1096 
1097         // Drawing components of compound shapes in outline mode produces a mess.
1098         bool simpleShapes = !m_pcbSettings.m_sketchMode[LAYER_PADS_TH];
1099 
1100         if( simpleShapes )
1101         {
1102             if( ( margin.x != margin.y && aPad->GetShape() != PAD_SHAPE::CUSTOM )
1103                 || ( aPad->GetShape() == PAD_SHAPE::ROUNDRECT && ( margin.x < 0 || margin.y < 0 ) ) )
1104             {
1105                 // Our algorithms below (polygon inflation in particular) can't handle differential
1106                 // inflation along separate axes.  So for those cases we build a dummy pad instead,
1107                 // and inflate it.
1108 
1109                 // Margin is added to both sides.  If the total margin is larger than the pad
1110                 // then don't display this layer
1111                 if( pad_size.x + 2 * margin.x <= 0 || pad_size.y + 2 * margin.y <= 0 )
1112                     return;
1113 
1114                 dummyPad.reset( static_cast<PAD*>( aPad->Duplicate() ) );
1115                 int initial_radius = dummyPad->GetRoundRectCornerRadius();
1116 
1117                 dummyPad->SetSize( pad_size + margin + margin );
1118 
1119                 if( dummyPad->GetShape() == PAD_SHAPE::ROUNDRECT )
1120                 {
1121                     // To keep the right margin around the corners, we need to modify the corner radius.
1122                     // We must have only one radius correction, so use the smallest absolute margin.
1123                     int radius_margin = std::max( margin.x, margin.y );     // radius_margin is < 0
1124                     dummyPad->SetRoundRectCornerRadius( std::max( initial_radius + radius_margin, 0 ) );
1125                 }
1126 
1127                 shapes = std::dynamic_pointer_cast<SHAPE_COMPOUND>( dummyPad->GetEffectiveShape() );
1128                 margin.x = margin.y = 0;
1129             }
1130             else
1131             {
1132                 shapes = std::dynamic_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape() );
1133             }
1134 
1135             if( aPad->GetShape() == PAD_SHAPE::CUSTOM && ( margin.x || margin.y ) )
1136             {
1137                 // We can't draw as shapes because we don't know which edges are internal and which
1138                 // are external (so we don't know when to apply the margin and when not to).
1139                 simpleShapes = false;
1140             }
1141 
1142             for( const SHAPE* shape : shapes->Shapes() )
1143             {
1144                 if( !simpleShapes )
1145                     break;
1146 
1147                 switch( shape->Type() )
1148                 {
1149                 case SH_SEGMENT:
1150                 case SH_CIRCLE:
1151                 case SH_RECT:
1152                 case SH_SIMPLE:
1153                     // OK so far
1154                     break;
1155 
1156                 default:
1157                     // Not OK
1158                     simpleShapes = false;
1159                     break;
1160                 }
1161             }
1162         }
1163 
1164         if( simpleShapes )
1165         {
1166             for( const SHAPE* shape : shapes->Shapes() )
1167             {
1168                 switch( shape->Type() )
1169                 {
1170                 case SH_SEGMENT:
1171                 {
1172                     const SHAPE_SEGMENT* seg = (const SHAPE_SEGMENT*) shape;
1173                     int                  effectiveWidth = seg->GetWidth() + 2 * margin.x;
1174 
1175                     if( effectiveWidth > 0 )
1176                         m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, effectiveWidth );
1177 
1178                     break;
1179                 }
1180 
1181                 case SH_CIRCLE:
1182                 {
1183                     const SHAPE_CIRCLE* circle = (const SHAPE_CIRCLE*) shape;
1184                     int                 effectiveRadius = circle->GetRadius() + margin.x;
1185 
1186                     if( effectiveRadius > 0 )
1187                         m_gal->DrawCircle( circle->GetCenter(), effectiveRadius );
1188 
1189                     break;
1190                 }
1191 
1192                 case SH_RECT:
1193                 {
1194                     const SHAPE_RECT* r = (const SHAPE_RECT*) shape;
1195                     VECTOR2I          pos = r->GetPosition();
1196                     VECTOR2I          effectiveMargin = margin;
1197 
1198                     if( effectiveMargin.x < 0 )
1199                     {
1200                         // A negative margin just produces a smaller rect.
1201                         VECTOR2I effectiveSize = r->GetSize() + effectiveMargin;
1202 
1203                         if( effectiveSize.x > 0 && effectiveSize.y > 0 )
1204                             m_gal->DrawRectangle( pos - effectiveMargin, pos + effectiveSize );
1205                     }
1206                     else if( effectiveMargin.x > 0 )
1207                     {
1208                         // A positive margin produces a larger rect, but with rounded corners
1209                         m_gal->DrawRectangle( r->GetPosition(), r->GetPosition() + r->GetSize() );
1210 
1211                         // Use segments to produce the margin with rounded corners
1212                         m_gal->DrawSegment( pos,
1213                                             pos + VECTOR2I( r->GetWidth(), 0 ),
1214                                             effectiveMargin.x * 2 );
1215                         m_gal->DrawSegment( pos + VECTOR2I( r->GetWidth(), 0 ),
1216                                             pos + r->GetSize(),
1217                                             effectiveMargin.x * 2 );
1218                         m_gal->DrawSegment( pos + r->GetSize(),
1219                                             pos + VECTOR2I( 0, r->GetHeight() ),
1220                                             effectiveMargin.x * 2 );
1221                         m_gal->DrawSegment( pos + VECTOR2I( 0, r->GetHeight() ),
1222                                             pos,
1223                                             effectiveMargin.x * 2 );
1224                     }
1225                     else
1226                     {
1227                         m_gal->DrawRectangle( r->GetPosition(), r->GetPosition() + r->GetSize() );
1228                     }
1229 
1230                     break;
1231                 }
1232 
1233                 case SH_SIMPLE:
1234                 {
1235                     const SHAPE_SIMPLE* poly = static_cast<const SHAPE_SIMPLE*>( shape );
1236 
1237                     if( margin.x < 0 )  // The poly shape must be deflated
1238                     {
1239                         int numSegs = GetArcToSegmentCount( -margin.x, m_maxError, 360.0 );
1240                         SHAPE_POLY_SET outline;
1241                         outline.NewOutline();
1242 
1243                         for( int ii = 0; ii < poly->PointCount(); ++ii )
1244                             outline.Append( poly->CPoint( ii ) );
1245 
1246                         outline.Deflate( -margin.x, numSegs );
1247 
1248                         m_gal->DrawPolygon( outline );
1249                     }
1250                     else
1251                     {
1252                         m_gal->DrawPolygon( poly->Vertices() );
1253                     }
1254 
1255                     // Now add on a rounded margin (using segments) if the margin > 0
1256                     if( margin.x > 0 )
1257                     {
1258                         for( size_t ii = 0; ii < poly->GetSegmentCount(); ++ii )
1259                         {
1260                             SEG seg = poly->GetSegment( ii );
1261                             m_gal->DrawSegment( seg.A, seg.B, margin.x * 2 );
1262                         }
1263                     }
1264 
1265                     break;
1266                 }
1267 
1268                 default:
1269                     // Better not get here; we already pre-flighted the shapes...
1270                     break;
1271                 }
1272             }
1273         }
1274         else
1275         {
1276             // This is expensive.  Avoid if possible.
1277             SHAPE_POLY_SET polySet;
1278             aPad->TransformShapeWithClearanceToPolygon( polySet, ToLAYER_ID( aLayer ), margin.x,
1279                                                         m_maxError, ERROR_INSIDE );
1280             m_gal->DrawPolygon( polySet );
1281         }
1282     }
1283 
1284     constexpr int clearanceFlags = PCB_RENDER_SETTINGS::CL_PADS;
1285 
1286     if( ( m_pcbSettings.m_clearanceDisplayFlags & clearanceFlags ) == clearanceFlags
1287             && ( aLayer == LAYER_PAD_FR || aLayer == LAYER_PAD_BK || aLayer == LAYER_PADS_TH ) )
1288     {
1289         /* Showing the clearance area is not obvious.
1290          * - A pad can be removed from some copper layers.
1291          * - For non copper layers, what is the clearance area?
1292          * So for copper layers, the clearance area is the shape if the pad is flashed on this
1293          * layer and the hole clearance area for other copper layers.
1294          * For other layers, use the pad shape, although one can use an other criteria,
1295          * depending on the non copper layer.
1296          */
1297         int  activeLayer = m_pcbSettings.GetActiveLayer();
1298         bool flashActiveLayer = true;
1299 
1300         if( IsCopperLayer( activeLayer ) )
1301             flashActiveLayer = aPad->FlashLayer( activeLayer );
1302 
1303         if( flashActiveLayer || aPad->GetDrillSize().x )
1304         {
1305             m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1306             m_gal->SetIsStroke( true );
1307             m_gal->SetIsFill( false );
1308             m_gal->SetStrokeColor( color );
1309 
1310             int clearance = aPad->GetOwnClearance( m_pcbSettings.GetActiveLayer() );
1311 
1312             if( flashActiveLayer && clearance > 0 )
1313             {
1314                 auto shape = std::dynamic_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape() );
1315 
1316                 if( shape && shape->Size() == 1 && shape->Shapes()[0]->Type() == SH_SEGMENT )
1317                 {
1318                     const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shape->Shapes()[0];
1319                     m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B,
1320                                         seg->GetWidth() + 2 * clearance );
1321                 }
1322                 else if( shape && shape->Size() == 1 && shape->Shapes()[0]->Type() == SH_CIRCLE )
1323                 {
1324                     const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shape->Shapes()[0];
1325                     m_gal->DrawCircle( circle->GetCenter(), circle->GetRadius() + clearance );
1326                 }
1327                 else
1328                 {
1329                     SHAPE_POLY_SET polySet;
1330 
1331                     // Use ERROR_INSIDE because it avoids Clipper and is therefore much faster.
1332                     aPad->TransformShapeWithClearanceToPolygon( polySet, ToLAYER_ID( aLayer ),
1333                                                                 clearance, m_maxError, ERROR_INSIDE );
1334                     m_gal->DrawPolygon( polySet );
1335                 }
1336             }
1337             else if( aPad->GetEffectiveHoleShape() && clearance > 0 )
1338             {
1339                 clearance += m_holePlatingThickness;
1340 
1341                 const SHAPE_SEGMENT* seg = aPad->GetEffectiveHoleShape();
1342                 m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B,
1343                                     seg->GetWidth() + 2 * clearance );
1344             }
1345         }
1346     }
1347 }
1348 
1349 
draw(const PCB_SHAPE * aShape,int aLayer)1350 void PCB_PAINTER::draw( const PCB_SHAPE* aShape, int aLayer )
1351 {
1352     const COLOR4D& color = m_pcbSettings.GetColor( aShape, aShape->GetLayer() );
1353     bool           sketch = m_pcbSettings.m_sketchGraphics;
1354     int            thickness = getLineThickness( aShape->GetWidth() );
1355 
1356     if( sketch )
1357     {
1358         m_gal->SetIsFill( false );
1359         m_gal->SetIsStroke( true );
1360         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1361     }
1362 
1363     m_gal->SetFillColor( color );
1364     m_gal->SetStrokeColor( color );
1365 
1366     switch( aShape->GetShape() )
1367     {
1368     case SHAPE_T::SEGMENT:
1369         if( sketch )
1370         {
1371             m_gal->DrawSegment( aShape->GetStart(), aShape->GetEnd(), thickness );
1372         }
1373         else
1374         {
1375             m_gal->SetIsFill( true );
1376             m_gal->SetIsStroke( false );
1377 
1378             m_gal->DrawSegment( aShape->GetStart(), aShape->GetEnd(), thickness );
1379         }
1380 
1381         break;
1382 
1383     case SHAPE_T::RECT:
1384     {
1385         std::vector<wxPoint> pts = aShape->GetRectCorners();
1386 
1387         if( sketch )
1388         {
1389             m_gal->DrawSegment( pts[0], pts[1], thickness );
1390             m_gal->DrawSegment( pts[1], pts[2], thickness );
1391             m_gal->DrawSegment( pts[2], pts[3], thickness );
1392             m_gal->DrawSegment( pts[3], pts[0], thickness );
1393         }
1394         else
1395         {
1396             m_gal->SetIsFill( true );
1397             m_gal->SetIsStroke( false );
1398 
1399             if( thickness > 0 )
1400             {
1401                 m_gal->DrawSegment( pts[0], pts[1], thickness );
1402                 m_gal->DrawSegment( pts[1], pts[2], thickness );
1403                 m_gal->DrawSegment( pts[2], pts[3], thickness );
1404                 m_gal->DrawSegment( pts[3], pts[0], thickness );
1405             }
1406 
1407             if( aShape->IsFilled() )
1408             {
1409                 SHAPE_POLY_SET poly;
1410                 poly.NewOutline();
1411 
1412                 for( const wxPoint& pt : pts )
1413                     poly.Append( pt );
1414 
1415                 m_gal->DrawPolygon( poly );
1416             }
1417         }
1418 
1419         break;
1420     }
1421 
1422     case SHAPE_T::ARC:
1423     {
1424         double startAngle;
1425         double endAngle;
1426         aShape->CalcArcAngles( startAngle, endAngle );
1427 
1428         if( sketch )
1429         {
1430             m_gal->DrawArcSegment( aShape->GetCenter(), aShape->GetRadius(),
1431                                    DEG2RAD( startAngle ), DEG2RAD( endAngle ), thickness,
1432                                    m_maxError );
1433         }
1434         else
1435         {
1436             m_gal->SetIsFill( true );
1437             m_gal->SetIsStroke( false );
1438 
1439             m_gal->DrawArcSegment( aShape->GetCenter(), aShape->GetRadius(),
1440                                    DEG2RAD( startAngle ), DEG2RAD( endAngle ), thickness,
1441                                    m_maxError );
1442         }
1443         break;
1444     }
1445 
1446     case SHAPE_T::CIRCLE:
1447         if( sketch )
1448         {
1449             m_gal->DrawCircle( aShape->GetStart(), aShape->GetRadius() - thickness / 2 );
1450             m_gal->DrawCircle( aShape->GetStart(), aShape->GetRadius() + thickness / 2 );
1451         }
1452         else
1453         {
1454             m_gal->SetIsFill( aShape->IsFilled() );
1455             m_gal->SetIsStroke( thickness > 0 );
1456             m_gal->SetLineWidth( thickness );
1457 
1458             m_gal->DrawCircle( aShape->GetStart(), aShape->GetRadius() );
1459         }
1460         break;
1461 
1462     case SHAPE_T::POLY:
1463     {
1464         SHAPE_POLY_SET&  shape = const_cast<PCB_SHAPE*>( aShape )->GetPolyShape();
1465         const FOOTPRINT* parentFootprint = aShape->GetParentFootprint();
1466 
1467         if( shape.OutlineCount() == 0 )
1468             break;
1469 
1470         if( parentFootprint )
1471         {
1472             m_gal->Save();
1473             m_gal->Translate( parentFootprint->GetPosition() );
1474             m_gal->Rotate( -parentFootprint->GetOrientationRadians() );
1475         }
1476 
1477         if( sketch )
1478         {
1479             for( int ii = 0; ii < shape.Outline( 0 ).SegmentCount(); ++ii )
1480             {
1481                 SEG seg = shape.Outline( 0 ).Segment( ii );
1482                 m_gal->DrawSegment( seg.A, seg.B, thickness );
1483             }
1484         }
1485         else
1486         {
1487             m_gal->SetIsFill( true );
1488             m_gal->SetIsStroke( false );
1489 
1490             if( thickness > 0 )
1491             {
1492                 for( int ii = 0; ii < shape.Outline( 0 ).SegmentCount(); ++ii )
1493                 {
1494                     SEG seg = shape.Outline( 0 ).Segment( ii );
1495                     m_gal->DrawSegment( seg.A, seg.B, thickness );
1496                 }
1497             }
1498 
1499             if( aShape->IsFilled() )
1500             {
1501                 // On Opengl, a not convex filled polygon is usually drawn by using triangles
1502                 // as primitives. CacheTriangulation() can create basic triangle primitives to
1503                 // draw the polygon solid shape on Opengl.  GLU tessellation is much slower, so
1504                 // currently we are using our tessellation.
1505                 if( m_gal->IsOpenGlEngine() && !shape.IsTriangulationUpToDate() )
1506                     shape.CacheTriangulation();
1507 
1508                 m_gal->DrawPolygon( shape );
1509             }
1510         }
1511 
1512         if( parentFootprint )
1513             m_gal->Restore();
1514 
1515         break;
1516     }
1517 
1518     case SHAPE_T::BEZIER:
1519         if( sketch )
1520         {
1521             std::vector<VECTOR2D> output;
1522             std::vector<VECTOR2D> pointCtrl;
1523 
1524             pointCtrl.push_back( aShape->GetStart() );
1525             pointCtrl.push_back( aShape->GetBezierC1() );
1526             pointCtrl.push_back( aShape->GetBezierC2() );
1527             pointCtrl.push_back( aShape->GetEnd() );
1528 
1529             BEZIER_POLY converter( pointCtrl );
1530             converter.GetPoly( output, thickness );
1531 
1532             for( unsigned ii = 0; ii + 1 < output.size(); ++ii )
1533                 m_gal->DrawSegment( output[ii], output[ii+1], thickness );
1534         }
1535         else
1536         {
1537             m_gal->SetIsFill( aShape->IsFilled() );
1538             m_gal->SetIsStroke( thickness > 0 );
1539             m_gal->SetLineWidth( thickness );
1540 
1541             // Use thickness as filter value to convert the curve to polyline when the curve
1542             // is not supported
1543             m_gal->DrawCurve( VECTOR2D( aShape->GetStart() ),
1544                               VECTOR2D( aShape->GetBezierC1() ),
1545                               VECTOR2D( aShape->GetBezierC2() ),
1546                               VECTOR2D( aShape->GetEnd() ), thickness );
1547         }
1548 
1549         break;
1550 
1551     case SHAPE_T::LAST:
1552         break;
1553     }
1554 }
1555 
1556 
draw(const PCB_TEXT * aText,int aLayer)1557 void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer )
1558 {
1559     wxString shownText( aText->GetShownText() );
1560 
1561     if( shownText.Length() == 0 )
1562         return;
1563 
1564     const COLOR4D& color = m_pcbSettings.GetColor( aText, aText->GetLayer() );
1565     VECTOR2D       position( aText->GetTextPos().x, aText->GetTextPos().y );
1566 
1567     if( m_pcbSettings.m_sketchText || m_pcbSettings.m_sketchMode[aLayer] )
1568     {
1569         // Outline mode
1570         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1571     }
1572     else
1573     {
1574         // Filled mode
1575         m_gal->SetLineWidth( getLineThickness( aText->GetEffectiveTextPenWidth() ) );
1576     }
1577 
1578     m_gal->SetStrokeColor( color );
1579     m_gal->SetIsFill( false );
1580     m_gal->SetIsStroke( true );
1581     m_gal->SetTextAttributes( aText );
1582     m_gal->StrokeText( shownText, position, aText->GetTextAngleRadians() );
1583 }
1584 
1585 
draw(const FP_TEXT * aText,int aLayer)1586 void PCB_PAINTER::draw( const FP_TEXT* aText, int aLayer )
1587 {
1588     wxString shownText( aText->GetShownText() );
1589 
1590     if( shownText.Length() == 0 )
1591         return;
1592 
1593     const COLOR4D& color = m_pcbSettings.GetColor( aText, aLayer );
1594     VECTOR2D       position( aText->GetTextPos().x, aText->GetTextPos().y );
1595 
1596     if( m_pcbSettings.m_sketchText )
1597     {
1598         // Outline mode
1599         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1600     }
1601     else
1602     {
1603         // Filled mode
1604         m_gal->SetLineWidth( getLineThickness( aText->GetEffectiveTextPenWidth() ) );
1605     }
1606 
1607     m_gal->SetStrokeColor( color );
1608     m_gal->SetIsFill( false );
1609     m_gal->SetIsStroke( true );
1610     m_gal->SetTextAttributes( aText );
1611     m_gal->StrokeText( shownText, position, aText->GetDrawRotationRadians() );
1612 
1613     // Draw the umbilical line
1614     if( aText->IsSelected() )
1615     {
1616         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1617         m_gal->SetStrokeColor( m_pcbSettings.GetColor( nullptr, LAYER_ANCHOR ) );
1618         m_gal->DrawLine( position, aText->GetParent()->GetPosition() );
1619     }
1620 }
1621 
1622 
draw(const FOOTPRINT * aFootprint,int aLayer)1623 void PCB_PAINTER::draw( const FOOTPRINT* aFootprint, int aLayer )
1624 {
1625     if( aLayer == LAYER_ANCHOR )
1626     {
1627         const COLOR4D color = m_pcbSettings.GetColor( aFootprint, aLayer );
1628 
1629         // Keep the size and width constant, not related to the scale because the anchor
1630         // is just a marker on screen
1631         double anchorSize = 5.0 / m_gal->GetWorldScale();           // 5 pixels size
1632         double anchorThickness = 1.0 / m_gal->GetWorldScale();      // 1 pixels width
1633 
1634         // Draw anchor
1635         m_gal->SetIsFill( false );
1636         m_gal->SetIsStroke( true );
1637         m_gal->SetStrokeColor( color );
1638         m_gal->SetLineWidth( anchorThickness );
1639 
1640         VECTOR2D center = aFootprint->GetPosition();
1641         m_gal->DrawLine( center - VECTOR2D( anchorSize, 0 ), center + VECTOR2D( anchorSize, 0 ) );
1642         m_gal->DrawLine( center - VECTOR2D( 0, anchorSize ), center + VECTOR2D( 0, anchorSize ) );
1643     }
1644 }
1645 
1646 
draw(const PCB_GROUP * aGroup,int aLayer)1647 void PCB_PAINTER::draw( const PCB_GROUP* aGroup, int aLayer )
1648 {
1649     if( aLayer == LAYER_ANCHOR )
1650     {
1651         if( aGroup->IsSelected() && !( aGroup->GetParent() && aGroup->GetParent()->IsSelected() ) )
1652         {
1653             // Selected on our own; draw enclosing box
1654         }
1655         else if( aGroup->IsEntered() )
1656         {
1657             // Entered group; draw enclosing box
1658         }
1659         else
1660         {
1661             // Neither selected nor entered; draw nothing at the group level (ie: only draw
1662             // its members)
1663             return;
1664         }
1665 
1666         const COLOR4D color = m_pcbSettings.GetColor( aGroup, LAYER_ANCHOR );
1667 
1668         EDA_RECT bbox = aGroup->GetBoundingBox();
1669         m_gal->SetStrokeColor( color );
1670         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth * 2.0f );
1671         wxPoint topLeft = bbox.GetPosition();
1672         wxPoint width = wxPoint( bbox.GetWidth(), 0 );
1673         wxPoint height = wxPoint( 0, bbox.GetHeight() );
1674 
1675         m_gal->DrawLine( topLeft, topLeft + width );
1676         m_gal->DrawLine( topLeft + width, topLeft + width + height );
1677         m_gal->DrawLine( topLeft + width + height, topLeft + height );
1678         m_gal->DrawLine( topLeft + height, topLeft );
1679 
1680         wxString name = aGroup->GetName();
1681 
1682         if( name.IsEmpty() )
1683             return;
1684 
1685         int ptSize = 12;
1686         int scaledSize = abs( KiROUND( m_gal->GetScreenWorldMatrix().GetScale().x * ptSize ) );
1687         int unscaledSize = Mils2iu( ptSize );
1688 
1689         // Scale by zoom a bit, but not too much
1690         int     textSize = ( scaledSize + ( unscaledSize * 2 ) ) / 3;
1691         int     penWidth = textSize / 10;
1692         wxPoint textOffset = wxPoint( width.x / 2, - KiROUND( textSize * 0.5 ) );
1693         wxPoint titleHeight = wxPoint( 0, KiROUND( textSize * 2.0 ) );
1694 
1695         if( PrintableCharCount( name ) * textSize < bbox.GetWidth() )
1696         {
1697             m_gal->DrawLine( topLeft, topLeft - titleHeight );
1698             m_gal->DrawLine( topLeft - titleHeight, topLeft + width - titleHeight );
1699             m_gal->DrawLine( topLeft + width - titleHeight, topLeft + width );
1700 
1701             m_gal->SetFontBold( false );
1702             m_gal->SetFontItalic( true );
1703             m_gal->SetFontUnderlined( false );
1704             m_gal->SetTextMirrored( m_gal->IsFlippedX() );
1705             m_gal->SetHorizontalJustify( GR_TEXT_HJUSTIFY_CENTER );
1706             m_gal->SetVerticalJustify( GR_TEXT_VJUSTIFY_BOTTOM );
1707             m_gal->SetIsFill( false );
1708             m_gal->SetGlyphSize( VECTOR2D( textSize, textSize ) );
1709             m_gal->SetLineWidth( penWidth );
1710             m_gal->StrokeText( aGroup->GetName(), topLeft + textOffset, 0.0 );
1711         }
1712     }
1713 }
1714 
1715 
draw(const ZONE * aZone,int aLayer)1716 void PCB_PAINTER::draw( const ZONE* aZone, int aLayer )
1717 {
1718     /*
1719      * aLayer will be the virtual zone layer (LAYER_ZONE_START, ... in GAL_LAYER_ID)
1720      * This is used for draw ordering in the GAL.
1721      * The color for the zone comes from the associated copper layer ( aLayer - LAYER_ZONE_START )
1722      * and the visibility comes from the combination of that copper layer and LAYER_ZONES
1723      */
1724     wxASSERT( IsZoneLayer( aLayer ) );
1725     PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( aLayer - LAYER_ZONE_START );
1726 
1727     if( !aZone->IsOnLayer( layer ) )
1728         return;
1729 
1730     COLOR4D              color = m_pcbSettings.GetColor( aZone, layer );
1731     std::deque<VECTOR2D> corners;
1732     ZONE_DISPLAY_MODE    displayMode = m_pcbSettings.m_zoneDisplayMode;
1733 
1734     // Draw the outline
1735     const SHAPE_POLY_SET* outline = aZone->Outline();
1736 
1737     if( m_pcbSettings.m_zoneOutlines && outline && outline->OutlineCount() > 0 )
1738     {
1739         m_gal->SetStrokeColor( color.a > 0.0 ? color.WithAlpha( 1.0 ) : color );
1740         m_gal->SetIsFill( false );
1741         m_gal->SetIsStroke( true );
1742         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1743 
1744         // Draw each contour (main contour and holes)
1745 
1746         /* This line:
1747          * m_gal->DrawPolygon( *outline );
1748          * should be enough, but currently does not work to draw holes contours in a complex polygon
1749          * so each contour is draw as a simple polygon
1750          */
1751 
1752         // Draw the main contour
1753         m_gal->DrawPolyline( outline->COutline( 0 ) );
1754 
1755         // Draw holes
1756         int holes_count = outline->HoleCount( 0 );
1757 
1758         for( int ii = 0; ii < holes_count; ++ii )
1759             m_gal->DrawPolyline( outline->CHole( 0, ii ) );
1760 
1761         // Draw hatch lines
1762         for( const SEG& hatchLine : aZone->GetHatchLines() )
1763             m_gal->DrawLine( hatchLine.A, hatchLine.B );
1764     }
1765 
1766     // Draw the filling
1767     if( displayMode == ZONE_DISPLAY_MODE::SHOW_FILLED
1768             || displayMode == ZONE_DISPLAY_MODE::SHOW_FRACTURE_BORDERS
1769             || displayMode == ZONE_DISPLAY_MODE::SHOW_TRIANGULATION )
1770     {
1771         const SHAPE_POLY_SET& polySet = aZone->GetFilledPolysList( layer );
1772 
1773         if( polySet.OutlineCount() == 0 )  // Nothing to draw
1774             return;
1775 
1776         // Set up drawing options
1777         int outline_thickness = 0;
1778 
1779         if( aZone->GetFilledPolysUseThickness( layer ) )
1780             outline_thickness = aZone->GetMinThickness();
1781 
1782         m_gal->SetStrokeColor( color );
1783         m_gal->SetFillColor( color );
1784         m_gal->SetLineWidth( outline_thickness );
1785 
1786         if( displayMode == ZONE_DISPLAY_MODE::SHOW_FILLED )
1787         {
1788             m_gal->SetIsFill( true );
1789             m_gal->SetIsStroke( outline_thickness > 0 );
1790         }
1791         else
1792         {
1793             m_gal->SetIsFill( false );
1794             m_gal->SetIsStroke( true );
1795         }
1796 
1797         m_gal->DrawPolygon( polySet, displayMode == ZONE_DISPLAY_MODE::SHOW_TRIANGULATION );
1798     }
1799 }
1800 
1801 
draw(const PCB_DIMENSION_BASE * aDimension,int aLayer)1802 void PCB_PAINTER::draw( const PCB_DIMENSION_BASE* aDimension, int aLayer )
1803 {
1804     const COLOR4D& strokeColor = m_pcbSettings.GetColor( aDimension, aLayer );
1805 
1806     m_gal->SetStrokeColor( strokeColor );
1807     m_gal->SetIsFill( false );
1808     m_gal->SetIsStroke( true );
1809 
1810     if( m_pcbSettings.m_sketchGraphics )
1811     {
1812         // Outline mode
1813         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1814     }
1815     else
1816     {
1817         // Filled mode
1818         m_gal->SetLineWidth( getLineThickness( aDimension->GetLineThickness() ) );
1819     }
1820 
1821     // Draw dimension shapes
1822     // TODO(JE) lift this out
1823     for( const std::shared_ptr<SHAPE>& shape : aDimension->GetShapes() )
1824     {
1825         switch( shape->Type() )
1826         {
1827         case SH_SEGMENT:
1828         {
1829             const SEG& seg = static_cast<const SHAPE_SEGMENT*>( shape.get() )->GetSeg();
1830             m_gal->DrawLine( seg.A, seg.B );
1831             break;
1832         }
1833 
1834         case SH_CIRCLE:
1835         {
1836             int radius = static_cast<const SHAPE_CIRCLE*>( shape.get() )->GetRadius();
1837             m_gal->DrawCircle( shape->Centre(), radius );
1838             break;
1839         }
1840 
1841         default:
1842             break;
1843         }
1844     }
1845 
1846     // Draw text
1847     const PCB_TEXT& text = aDimension->Text();
1848     VECTOR2D position( text.GetTextPos().x, text.GetTextPos().y );
1849 
1850     if( m_pcbSettings.m_sketchText )
1851     {
1852         // Outline mode
1853         m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
1854     }
1855     else
1856     {
1857         // Filled mode
1858         m_gal->SetLineWidth( getLineThickness( text.GetEffectiveTextPenWidth() ) );
1859     }
1860 
1861     m_gal->SetTextAttributes( &text );
1862     m_gal->StrokeText( text.GetShownText(), position, text.GetTextAngleRadians() );
1863 }
1864 
1865 
draw(const PCB_TARGET * aTarget)1866 void PCB_PAINTER::draw( const PCB_TARGET* aTarget )
1867 {
1868     const COLOR4D& strokeColor = m_pcbSettings.GetColor( aTarget, aTarget->GetLayer() );
1869     VECTOR2D position( aTarget->GetPosition() );
1870     double   size, radius;
1871 
1872     m_gal->SetLineWidth( getLineThickness( aTarget->GetWidth() ) );
1873     m_gal->SetStrokeColor( strokeColor );
1874     m_gal->SetIsFill( false );
1875     m_gal->SetIsStroke( true );
1876 
1877     m_gal->Save();
1878     m_gal->Translate( position );
1879 
1880     if( aTarget->GetShape() )
1881     {
1882         // shape x
1883         m_gal->Rotate( M_PI / 4.0 );
1884         size   = 2.0 * aTarget->GetSize() / 3.0;
1885         radius = aTarget->GetSize() / 2.0;
1886     }
1887     else
1888     {
1889         // shape +
1890         size   = aTarget->GetSize() / 2.0;
1891         radius = aTarget->GetSize() / 3.0;
1892     }
1893 
1894     m_gal->DrawLine( VECTOR2D( -size, 0.0 ), VECTOR2D( size, 0.0 ) );
1895     m_gal->DrawLine( VECTOR2D( 0.0, -size ), VECTOR2D( 0.0,  size ) );
1896     m_gal->DrawCircle( VECTOR2D( 0.0, 0.0 ), radius );
1897 
1898     m_gal->Restore();
1899 }
1900 
1901 
draw(const PCB_MARKER * aMarker,int aLayer)1902 void PCB_PAINTER::draw( const PCB_MARKER* aMarker, int aLayer )
1903 {
1904     bool isShadow = aLayer == LAYER_MARKER_SHADOWS;
1905 
1906     // Don't paint shadows for invisible markers.
1907     // It would be nice to do this through layer dependencies but we can't do an "or" there today
1908     if( isShadow && aMarker->GetBoard()
1909             && !aMarker->GetBoard()->IsElementVisible( aMarker->GetColorLayer() ) )
1910     {
1911         return;
1912     }
1913 
1914     SHAPE_LINE_CHAIN polygon;
1915     aMarker->ShapeToPolygon( polygon );
1916 
1917     COLOR4D color = m_pcbSettings.GetColor( aMarker, isShadow ? LAYER_MARKER_SHADOWS
1918                                                               : aMarker->GetColorLayer() );
1919 
1920     m_gal->Save();
1921     m_gal->Translate( aMarker->GetPosition() );
1922 
1923     if( isShadow )
1924     {
1925         m_gal->SetStrokeColor( color );
1926         m_gal->SetIsStroke( true );
1927         m_gal->SetLineWidth( aMarker->MarkerScale() );
1928     }
1929     else
1930     {
1931         m_gal->SetFillColor( color );
1932         m_gal->SetIsFill( true );
1933     }
1934 
1935     m_gal->DrawPolygon( polygon );
1936     m_gal->Restore();
1937 }
1938 
1939 
1940 const double PCB_RENDER_SETTINGS::MAX_FONT_SIZE = Millimeter2iu( 10.0 );
1941