Performance enhancements to roundrect pads and clearance outlines.

Aka: avoid Clipper at all costs.

Fixes https://gitlab.com/kicad/code/kicad/issues/5900
This commit is contained in:
Jeff Young 2020-10-14 15:55:06 +01:00
parent 1d93effa14
commit bbe7573d1c
6 changed files with 249 additions and 150 deletions

View File

@ -80,20 +80,6 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo
int aWidth, int aError, ERROR_LOC aErrorLoc ); int aWidth, int aError, ERROR_LOC aErrorLoc );
/**
* Helper function GetRoundRectCornerCenters
* Has meaning only for rounded rect
* Returns the centers of the rounded corners.
* @param aCenters is the buffer to store the 4 coordinates.
* @param aRadius = the radius of the of the rounded corners.
* @param aPosition = position of the round rect
* @param aSize = size of the of the round rect.
* @param aRotation = rotation of the of the round rect
*/
void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius, const wxPoint& aPosition,
const wxSize& aSize, double aRotation );
/** /**
* convert a rectangle with rounded corners and/or chamfered corners to a polygon * convert a rectangle with rounded corners and/or chamfered corners to a polygon
* Convert rounded corners arcs to multiple straight lines. This will generate at least * Convert rounded corners arcs to multiple straight lines. This will generate at least

View File

@ -188,54 +188,24 @@ void TransformOvalToPolygon( SHAPE_POLY_SET& aCornerBuffer, wxPoint aStart, wxPo
} }
void GetRoundRectCornerCenters( wxPoint aCenters[4], int aRadius, const wxPoint& aPosition, // Return a polygon representing a round rect centered at {0,0}
const wxSize& aSize, double aRotation ) void TransformRoundRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxSize& aSize,
int aCornerRadius, int aError, ERROR_LOC aErrorLoc )
{ {
wxSize size( aSize/2 ); wxPoint centers[4];
wxSize size( aSize / 2 );
size.x -= aRadius; size.x -= aCornerRadius;
size.y -= aRadius; size.y -= aCornerRadius;
// Ensure size is > 0, to avoid generating unusable shapes // Ensure size is > 0, to avoid generating unusable shapes which can crash kicad.
// which can crash kicad.
size.x = std::max( 1, size.x ); size.x = std::max( 1, size.x );
size.y = std::max( 1, size.y ); size.y = std::max( 1, size.y );
aCenters[0] = wxPoint( -size.x, size.y ); centers[0] = wxPoint( -size.x, size.y );
aCenters[1] = wxPoint( size.x, size.y ); centers[1] = wxPoint( size.x, size.y );
aCenters[2] = wxPoint( size.x, -size.y ); centers[2] = wxPoint( size.x, -size.y );
aCenters[3] = wxPoint( -size.x, -size.y ); centers[3] = wxPoint( -size.x, -size.y );
// Rotate the polygon
if( aRotation != 0.0 )
{
for( int ii = 0; ii < 4; ii++ )
RotatePoint( &aCenters[ii], aRotation );
}
// move the polygon to the position
for( int ii = 0; ii < 4; ii++ )
aCenters[ii] += aPosition;
}
void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition,
const wxSize& aSize, double aRotation,
int aCornerRadius, double aChamferRatio,
int aChamferCorners, int aError, ERROR_LOC aErrorLoc )
{
// Build the basic shape in orientation 0.0, position 0,0 for chamfered corners
// or in actual position/orientation for round rect only
wxPoint corners[4];
GetRoundRectCornerCenters( corners, aCornerRadius,
aChamferCorners ? wxPoint( 0, 0 ) : aPosition,
aSize, aChamferCorners ? 0.0 : aRotation );
SHAPE_POLY_SET outline;
outline.NewOutline();
for( const wxPoint& corner : corners)
outline.Append( corner );
int numSegs = GetArcToSegmentCount( aCornerRadius, aError, 360.0 ); int numSegs = GetArcToSegmentCount( aCornerRadius, aError, 360.0 );
@ -244,71 +214,101 @@ void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const
if( numSegs < 16 ) if( numSegs < 16 )
numSegs = 16; numSegs = 16;
// To build the polygonal shape outside the actual shape, we use a bigger int delta = 3600 / numSegs; // rotate angle in 0.1 degree
// radius to build rounded corners.
int correction = GetCircleToPolyCorrection( aError );
int radius = aCornerRadius; int radius = aCornerRadius;
if( aErrorLoc == ERROR_OUTSIDE ) if( aErrorLoc == ERROR_OUTSIDE )
radius += correction; radius += GetCircleToPolyCorrection( aError );
outline.Inflate( radius, numSegs ); auto genArc =
[&]( const wxPoint& aCenter, int aStart, int aEnd )
if( aChamferCorners == RECT_NO_CHAMFER ) // no chamfer
{ {
// Add the outline: for( int angle = aStart + delta; angle < aEnd; angle += delta )
aCornerBuffer.Append( outline );
return;
}
// Now we have the round rect outline, in position 0,0 orientation 0.0.
// Chamfer the corner(s).
int chamfer_value = aChamferRatio * std::min( aSize.x, aSize.y );
SHAPE_POLY_SET chamfered_corner; // corner shape for the current corner to chamfer
int corner_id[4] =
{
RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT,
RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT
};
// Depending on the corner position, signX[] and signY[] give the sign of chamfer
// coordinates relative to the corner position
// The first corner is the top left corner, then top right, bottom left and bottom right
int signX[4] = {1, -1, 1,-1 };
int signY[4] = {1, 1, -1,-1 };
for( int ii = 0; ii < 4; ii++ )
{
if( (corner_id[ii] & aChamferCorners) == 0 )
continue;
VECTOR2I corner_pos( -signX[ii]*aSize.x/2, -signY[ii]*aSize.y/2 );
if( aCornerRadius )
{ {
// We recreate a rectangular area covering the full rounded corner (max size = aSize/2) wxPoint pt( -radius, 0 );
// to rebuild the corner before chamfering, to be sure the rounded corner shape does not RotatePoint( &pt, angle );
// overlap the chamfered corner shape: pt += aCenter;
aCornerBuffer.Append( pt.x, pt.y );
}
};
aCornerBuffer.NewOutline();
aCornerBuffer.Append( centers[0] + wxPoint( -radius, 0 ) );
genArc( centers[0], 0, 900 );
aCornerBuffer.Append( centers[0] + wxPoint( 0, radius ) );
aCornerBuffer.Append( centers[1] + wxPoint( 0, radius ) );
genArc( centers[1], 900, 1800 );
aCornerBuffer.Append( centers[1] + wxPoint( radius, 0 ) );
aCornerBuffer.Append( centers[2] + wxPoint( radius, 0 ) );
genArc( centers[2], 1800, 2700 );
aCornerBuffer.Append( centers[2] + wxPoint( 0, -radius ) );
aCornerBuffer.Append( centers[3] + wxPoint( 0, -radius ) );
genArc( centers[3], 2700, 3600 );
aCornerBuffer.Append( centers[3] + wxPoint( -radius, 0 ) );
aCornerBuffer.Outline( 0 ).SetClosed( true );
}
void TransformRoundChamferedRectToPolygon( SHAPE_POLY_SET& aCornerBuffer, const wxPoint& aPosition,
const wxSize& aSize, double aRotation,
int aCornerRadius, double aChamferRatio,
int aChamferCorners, int aError, ERROR_LOC aErrorLoc )
{
SHAPE_POLY_SET outline;
TransformRoundRectToPolygon( outline, aSize, aCornerRadius, aError, aErrorLoc );
if( aChamferCorners )
{
// Now we have the round rect outline, in position 0,0 orientation 0.0.
// Chamfer the corner(s).
int chamfer_value = aChamferRatio * std::min( aSize.x, aSize.y );
SHAPE_POLY_SET chamfered_corner; // corner shape for the current corner to chamfer
int corner_id[4] =
{
RECT_CHAMFER_TOP_LEFT, RECT_CHAMFER_TOP_RIGHT,
RECT_CHAMFER_BOTTOM_LEFT, RECT_CHAMFER_BOTTOM_RIGHT
};
// Depending on the corner position, signX[] and signY[] give the sign of chamfer
// coordinates relative to the corner position
// The first corner is the top left corner, then top right, bottom left and bottom right
int signX[4] = {1, -1, 1,-1 };
int signY[4] = {1, 1, -1,-1 };
for( int ii = 0; ii < 4; ii++ )
{
if( (corner_id[ii] & aChamferCorners) == 0 )
continue;
VECTOR2I corner_pos( -signX[ii]*aSize.x/2, -signY[ii]*aSize.y/2 );
if( aCornerRadius )
{
// We recreate a rectangular area covering the full rounded corner
// (max size = aSize/2) to rebuild the corner before chamfering, to be sure
// the rounded corner shape does not overlap the chamfered corner shape:
chamfered_corner.RemoveAllContours();
chamfered_corner.NewOutline();
chamfered_corner.Append( 0, 0 );
chamfered_corner.Append( 0, signY[ii] * aSize.y / 2 );
chamfered_corner.Append( signX[ii] * aSize.x / 2, signY[ii] * aSize.y / 2 );
chamfered_corner.Append( signX[ii] * aSize.x / 2, 0 );
chamfered_corner.Move( corner_pos );
outline.BooleanAdd( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
}
// Now chamfer this corner
chamfered_corner.RemoveAllContours(); chamfered_corner.RemoveAllContours();
chamfered_corner.NewOutline(); chamfered_corner.NewOutline();
chamfered_corner.Append( 0, 0 ); chamfered_corner.Append( 0, 0 );
chamfered_corner.Append( 0, signY[ii] * aSize.y / 2 ); chamfered_corner.Append( 0, signY[ii] * chamfer_value );
chamfered_corner.Append( signX[ii] * aSize.x / 2, signY[ii] * aSize.y / 2 ); chamfered_corner.Append( signX[ii] * chamfer_value, 0 );
chamfered_corner.Append( signX[ii] * aSize.x / 2, 0 );
chamfered_corner.Move( corner_pos ); chamfered_corner.Move( corner_pos );
outline.BooleanAdd( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE ); outline.BooleanSubtract( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
} }
// Now chamfer this corner
chamfered_corner.RemoveAllContours();
chamfered_corner.NewOutline();
chamfered_corner.Append( 0, 0 );
chamfered_corner.Append( 0, signY[ii] * chamfer_value );
chamfered_corner.Append( signX[ii] * chamfer_value, 0 );
chamfered_corner.Move( corner_pos );
outline.BooleanSubtract( chamfered_corner, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
} }
// Rotate and move the outline: // Rotate and move the outline:

View File

@ -641,9 +641,6 @@ void D_PAD::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
pad_min_seg_per_circle_count ); pad_min_seg_per_circle_count );
int clearance = aClearanceValue + GetCircleToPolyCorrection( aError ); int clearance = aClearanceValue + GetCircleToPolyCorrection( aError );
outline.Inflate( clearance, numSegs ); outline.Inflate( clearance, numSegs );
// TODO: clamp the inflated polygon, because it is slightly too big:
// it was inflated by a value slightly too big to keep rounded corners
// ouside the pad area.
} }
aCornerBuffer.Append( outline ); aCornerBuffer.Append( outline );

View File

@ -324,29 +324,14 @@ void D_PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
break; break;
case PAD_SHAPE_RECT: case PAD_SHAPE_RECT:
if( m_orient == 0 || m_orient == 1800 )
{
add( new SHAPE_RECT( shapePos - m_size / 2, m_size.x, m_size.y ) );
break;
}
else if( m_orient == 900 || m_orient == -900 )
{
wxSize rot_size( m_size.y, m_size.x );
add( new SHAPE_RECT( shapePos - rot_size / 2, rot_size.x, rot_size.y ) );
break;
}
// Not at a cartesian angle; fall through to general case
KI_FALLTHROUGH;
case PAD_SHAPE_TRAPEZOID: case PAD_SHAPE_TRAPEZOID:
case PAD_SHAPE_ROUNDRECT: case PAD_SHAPE_ROUNDRECT:
{ {
int r = GetRoundRectCornerRadius(); int r = ( effectiveShape == PAD_SHAPE_ROUNDRECT ) ? GetRoundRectCornerRadius() : 0;
wxPoint half_size( m_size.x / 2, m_size.y / 2 ); wxPoint half_size( m_size.x / 2, m_size.y / 2 );
wxSize trap_delta( 0, 0 ); wxSize trap_delta( 0, 0 );
if( effectiveShape == PAD_SHAPE_ROUNDRECT ) if( r )
{ {
half_size -= wxPoint( r, r ); half_size -= wxPoint( r, r );
@ -361,7 +346,9 @@ void D_PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
} }
} }
else if( effectiveShape == PAD_SHAPE_TRAPEZOID ) else if( effectiveShape == PAD_SHAPE_TRAPEZOID )
{
trap_delta = m_deltaSize / 2; trap_delta = m_deltaSize / 2;
}
SHAPE_LINE_CHAIN corners; SHAPE_LINE_CHAIN corners;
@ -373,9 +360,23 @@ void D_PAD::BuildEffectiveShapes( PCB_LAYER_ID aLayer ) const
corners.Rotate( -DECIDEG2RAD( m_orient ) ); corners.Rotate( -DECIDEG2RAD( m_orient ) );
corners.Move( shapePos ); corners.Move( shapePos );
add( new SHAPE_SIMPLE( corners ) ); // GAL renders rectangles faster than 4-point polygons so it's worth checking if our
// body shape is a rectangle.
if( corners.PointCount() == 4
&& corners.CPoint( 0 ).y == corners.CPoint( 1 ).y
&& corners.CPoint( 1 ).x == corners.CPoint( 2 ).x
&& corners.CPoint( 2 ).y == corners.CPoint( 3 ).y
&& corners.CPoint( 4 ).x == corners.CPoint( 0 ).x )
{
VECTOR2I size = corners.CPoint( 2 ) - corners.CPoint( 0 );
add( new SHAPE_RECT( corners.CPoint( 0 ), size.x, size.y ) );
}
else
{
add( new SHAPE_SIMPLE( corners ) );
}
if( effectiveShape == PAD_SHAPE_ROUNDRECT ) if( r )
{ {
add( new SHAPE_SEGMENT( corners.CPoint( 0 ), corners.CPoint( 1 ), r * 2 ) ); add( new SHAPE_SEGMENT( corners.CPoint( 0 ), corners.CPoint( 1 ), r * 2 ) );
add( new SHAPE_SEGMENT( corners.CPoint( 1 ), corners.CPoint( 2 ), r * 2 ) ); add( new SHAPE_SEGMENT( corners.CPoint( 1 ), corners.CPoint( 2 ), r * 2 ) );

View File

@ -969,7 +969,9 @@ void PCB_EDIT_FRAME::SetGridColor( COLOR4D aColor )
void PCB_EDIT_FRAME::SetActiveLayer( PCB_LAYER_ID aLayer ) void PCB_EDIT_FRAME::SetActiveLayer( PCB_LAYER_ID aLayer )
{ {
if( GetActiveLayer() == aLayer ) PCB_LAYER_ID oldLayer = GetActiveLayer();
if( oldLayer == aLayer )
return; return;
PCB_BASE_FRAME::SetActiveLayer( aLayer ); PCB_BASE_FRAME::SetActiveLayer( aLayer );
@ -985,30 +987,56 @@ void PCB_EDIT_FRAME::SetActiveLayer( PCB_LAYER_ID aLayer )
[]( KIGFX::VIEW_ITEM* aItem ) -> bool []( KIGFX::VIEW_ITEM* aItem ) -> bool
{ {
if( VIA* via = dynamic_cast<VIA*>( aItem ) ) if( VIA* via = dynamic_cast<VIA*>( aItem ) )
{
return ( via->GetViaType() == VIATYPE::BLIND_BURIED || return ( via->GetViaType() == VIATYPE::BLIND_BURIED ||
via->GetViaType() == VIATYPE::MICROVIA ); via->GetViaType() == VIATYPE::MICROVIA );
}
return false; return false;
} ); } );
// Clearances could be layer-dependent so redraw them when the active layer is changed // Clearances could be layer-dependent so redraw them when the active layer is changed
if( GetDisplayOptions().m_DisplayPadIsol ) if( GetDisplayOptions().m_DisplayPadIsol )
{ {
GetCanvas()->GetView()->UpdateAllItemsConditionally( KIGFX::REPAINT, GetCanvas()->GetView()->UpdateAllItemsConditionally( KIGFX::REPAINT,
[]( KIGFX::VIEW_ITEM* aItem ) -> bool [&]( KIGFX::VIEW_ITEM* aItem ) -> bool
{ {
return dynamic_cast<D_PAD*>( aItem ) != nullptr; if( D_PAD* pad = dynamic_cast<D_PAD*>( aItem ) )
}); {
// Round-corner rects are expensive to draw, but are mostly found on
// SMD pads which only need redrawing on an active-to-not-active
// switch.
if( pad->GetAttribute() == PAD_ATTRIB_SMD )
{
if( ( oldLayer == F_Cu || aLayer == F_Cu ) && pad->IsOnLayer( F_Cu ) )
return true;
if( ( oldLayer == B_Cu || aLayer == B_Cu ) && pad->IsOnLayer( B_Cu ) )
return true;
}
return true;
}
return false;
} );
} }
// Clearances could be layer-dependent so redraw them when the active layer is changed if( GetDisplayOptions().m_ShowTrackClearanceMode )
if( GetDisplayOptions().m_ShowTrackClearanceMode == PCB_DISPLAY_OPTIONS::SHOW_CLEARANCE_ALWAYS )
{ {
GetCanvas()->GetView()->UpdateAllItemsConditionally( KIGFX::REPAINT, GetCanvas()->GetView()->UpdateAllItemsConditionally( KIGFX::REPAINT,
[]( KIGFX::VIEW_ITEM* aItem ) -> bool [&]( KIGFX::VIEW_ITEM* aItem ) -> bool
{ {
return dynamic_cast<TRACK*>( aItem ) != nullptr; if( TRACK* track = dynamic_cast<TRACK*>( aItem ) )
}); {
// Tracks aren't particularly expensive to draw, but it's an easy
// check.
return track->IsOnLayer( oldLayer ) || track->IsOnLayer( aLayer );
}
return false;
} );
} }
GetCanvas()->Refresh(); GetCanvas()->Refresh();

View File

@ -45,7 +45,9 @@
#include <gal/graphics_abstraction_layer.h> #include <gal/graphics_abstraction_layer.h>
#include <geometry/geometry_utils.h> #include <geometry/geometry_utils.h>
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
#include <geometry/shape_rect.h>
#include <geometry/shape_segment.h> #include <geometry/shape_segment.h>
#include <geometry/shape_simple.h>
#include <geometry/shape_circle.h> #include <geometry/shape_circle.h>
using namespace KIGFX; using namespace KIGFX;
@ -936,19 +938,104 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
} }
auto shapes = std::dynamic_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape() ); auto shapes = std::dynamic_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape() );
bool simpleShapes = true;
if( shapes && shapes->Size() == 1 && shapes->Shapes()[0]->Type() == SH_SEGMENT ) for( SHAPE* shape : shapes->Shapes() )
{ {
const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shapes->Shapes()[0]; // Drawing components of compound shapes in outline mode produces a mess.
m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B, seg->GetWidth() + 2 * margin.x ); if( m_pcbSettings.m_sketchMode[LAYER_PADS_TH] )
simpleShapes = false;
if( !simpleShapes )
break;
switch( shape->Type() )
{
case SH_SEGMENT:
case SH_CIRCLE:
case SH_RECT:
case SH_SIMPLE:
// OK so far
break;
default:
// Not OK
simpleShapes = false;
break;
}
} }
else if( shapes && shapes->Size() == 1 && shapes->Shapes()[0]->Type() == SH_CIRCLE )
if( simpleShapes )
{ {
const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shapes->Shapes()[0]; for( SHAPE* shape : shapes->Shapes() )
m_gal->DrawCircle( circle->GetCenter(), circle->GetRadius() + margin.x ); {
switch( shape->Type() )
{
case SH_SEGMENT:
{
const SHAPE_SEGMENT* seg = (SHAPE_SEGMENT*) shape;
m_gal->DrawSegment( seg->GetSeg().A, seg->GetSeg().B,
seg->GetWidth() + 2 * margin.x );
}
break;
case SH_CIRCLE:
{
const SHAPE_CIRCLE* circle = (SHAPE_CIRCLE*) shape;
m_gal->DrawCircle( circle->GetCenter(), circle->GetRadius() + margin.x );
}
break;
case SH_RECT:
{
const SHAPE_RECT* r = (SHAPE_RECT*) shape;
m_gal->DrawRectangle( r->GetPosition(), r->GetPosition() + r->GetSize() );
if( margin.x > 0 )
{
m_gal->DrawSegment( r->GetPosition(),
r->GetPosition() + VECTOR2I( r->GetWidth(), 0 ),
margin.x * 2 );
m_gal->DrawSegment( r->GetPosition() + VECTOR2I( r->GetWidth(), 0 ),
r->GetPosition() + r->GetSize(),
margin.x * 2 );
m_gal->DrawSegment( r->GetPosition() + r->GetSize(),
r->GetPosition() + VECTOR2I( 0, r->GetHeight() ),
margin.x * 2 );
m_gal->DrawSegment( r->GetPosition() + VECTOR2I( 0, r->GetHeight() ),
r->GetPosition(),
margin.x * 2 );
}
}
break;
case SH_SIMPLE:
{
const SHAPE_SIMPLE* poly = static_cast<const SHAPE_SIMPLE*>( shape );
m_gal->DrawPolygon( poly->Vertices() );
if( margin.x > 0 )
{
for( size_t ii = 0; ii < poly->GetSegmentCount(); ++ii )
{
SEG seg = poly->GetSegment( ii );
m_gal->DrawSegment( seg.A, seg.B, margin.x * 2 );
}
}
}
break;
default:
// Better not get here; we already pre-flighted the shapes...
break;
}
}
} }
else else
{ {
// This is expensive. Avoid if possible.
SHAPE_POLY_SET polySet; SHAPE_POLY_SET polySet;
aPad->TransformShapeWithClearanceToPolygon( polySet, ToLAYER_ID( aLayer ), margin.x, aPad->TransformShapeWithClearanceToPolygon( polySet, ToLAYER_ID( aLayer ), margin.x,
bds.m_MaxError, ERROR_INSIDE ); bds.m_MaxError, ERROR_INSIDE );
@ -965,9 +1052,9 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
if( ( m_pcbSettings.m_clearance & clearanceFlags ) == clearanceFlags if( ( m_pcbSettings.m_clearance & clearanceFlags ) == clearanceFlags
&& ( aLayer == LAYER_PAD_FR || aLayer == LAYER_PAD_BK || aLayer == LAYER_PADS_TH ) ) && ( aLayer == LAYER_PAD_FR || aLayer == LAYER_PAD_BK || aLayer == LAYER_PADS_TH ) )
{ {
bool flashLayer = aPad->FlashLayer( m_pcbSettings.GetActiveLayer() ); bool flashActiveLayer = aPad->FlashLayer( m_pcbSettings.GetActiveLayer() );
if( flashLayer || aPad->GetDrillSize().x ) if( flashActiveLayer || aPad->GetDrillSize().x )
{ {
m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth ); m_gal->SetLineWidth( m_pcbSettings.m_outlineWidth );
m_gal->SetIsStroke( true ); m_gal->SetIsStroke( true );
@ -976,7 +1063,7 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
int clearance = aPad->GetOwnClearance( m_pcbSettings.GetActiveLayer() ); int clearance = aPad->GetOwnClearance( m_pcbSettings.GetActiveLayer() );
if( flashLayer ) if( flashActiveLayer )
{ {
auto shape = std::dynamic_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape() ); auto shape = std::dynamic_pointer_cast<SHAPE_COMPOUND>( aPad->GetEffectiveShape() );