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:
parent
1d93effa14
commit
bbe7573d1c
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
|
@ -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 ) );
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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() );
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue