Implement preserved-corners to prevent divots when filling adjacent zones.
Fixes: lp:1460787 * https://bugs.launchpad.net/kicad/+bug/1460787
This commit is contained in:
parent
9259f3a7c9
commit
928d6c5dff
|
@ -562,14 +562,18 @@ void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount, bool aPrese
|
|||
|
||||
ClipperOffset c;
|
||||
|
||||
// N.B. using jtSquare here does not create square corners. They end up mitered by
|
||||
// aFactor. Setting jtMiter and forcing the limit to be aFactor creates sharp corners.
|
||||
JoinType type = aPreserveCorners ? jtMiter : jtRound;
|
||||
// N.B. using jtSquare here does not create square corners; they end up mitered by aFactor.
|
||||
// Setting jtMiter with a sufficiently high MiterLimit will preserve corners, but things
|
||||
// get ugly at very acute angles (and we don't really want to support those anyway for peeling
|
||||
// concerns). Setting a MiterLimit of 1.4145 preserves corners up to 90 degrees; we set the
|
||||
// limit a bit above that.
|
||||
JoinType joinType = aPreserveCorners ? jtMiter : jtRound;
|
||||
double miterLimit = 1.5;
|
||||
|
||||
for( const POLYGON& poly : m_polys )
|
||||
{
|
||||
for( size_t i = 0; i < poly.size(); i++ )
|
||||
c.AddPath( poly[i].convertToClipper( i == 0 ), type, etClosedPolygon );
|
||||
c.AddPath( poly[i].convertToClipper( i == 0 ), joinType, etClosedPolygon );
|
||||
}
|
||||
|
||||
PolyTree solution;
|
||||
|
@ -595,8 +599,7 @@ void SHAPE_POLY_SET::Inflate( int aFactor, int aCircleSegmentsCount, bool aPrese
|
|||
coeff = arc_tolerance_factor[aCircleSegmentsCount];
|
||||
|
||||
c.ArcTolerance = std::abs( aFactor ) * coeff;
|
||||
c.MiterLimit = std::abs( aFactor );
|
||||
|
||||
c.MiterLimit = miterLimit;
|
||||
c.Execute( solution, aFactor );
|
||||
|
||||
importTree( &solution );
|
||||
|
@ -1480,17 +1483,18 @@ int SHAPE_POLY_SET::TotalVertices() const
|
|||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::ChamferPolygon( unsigned int aDistance, int aIndex )
|
||||
SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::ChamferPolygon( unsigned int aDistance, int aIndex,
|
||||
std::set<VECTOR2I>* aPreserveCorners )
|
||||
{
|
||||
return chamferFilletPolygon( CORNER_MODE::CHAMFERED, aDistance, aIndex, 0 );
|
||||
return chamferFilletPolygon( CHAMFERED, aDistance, aIndex, 0, aPreserveCorners );
|
||||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::FilletPolygon( unsigned int aRadius,
|
||||
int aErrorMax,
|
||||
int aIndex )
|
||||
SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::FilletPolygon( unsigned int aRadius, int aErrorMax,
|
||||
int aIndex,
|
||||
std::set<VECTOR2I>* aPreserveCorners )
|
||||
{
|
||||
return chamferFilletPolygon( CORNER_MODE::FILLETED, aRadius, aIndex, aErrorMax );
|
||||
return chamferFilletPolygon( FILLETED, aRadius, aIndex, aErrorMax, aPreserveCorners );
|
||||
}
|
||||
|
||||
|
||||
|
@ -1604,32 +1608,32 @@ bool SHAPE_POLY_SET::IsVertexInHole( int aGlobalIdx )
|
|||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET SHAPE_POLY_SET::Chamfer( int aDistance )
|
||||
SHAPE_POLY_SET SHAPE_POLY_SET::Chamfer( int aDistance, std::set<VECTOR2I>* aPreserveCorners )
|
||||
{
|
||||
SHAPE_POLY_SET chamfered;
|
||||
|
||||
for( unsigned int polygonIdx = 0; polygonIdx < m_polys.size(); polygonIdx++ )
|
||||
chamfered.m_polys.push_back( ChamferPolygon( aDistance, polygonIdx ) );
|
||||
for( unsigned int idx = 0; idx < m_polys.size(); idx++ )
|
||||
chamfered.m_polys.push_back( ChamferPolygon( aDistance, idx, aPreserveCorners ) );
|
||||
|
||||
return chamfered;
|
||||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET SHAPE_POLY_SET::Fillet( int aRadius, int aErrorMax )
|
||||
SHAPE_POLY_SET SHAPE_POLY_SET::Fillet( int aRadius, int aErrorMax,
|
||||
std::set<VECTOR2I>* aPreserveCorners )
|
||||
{
|
||||
SHAPE_POLY_SET filleted;
|
||||
|
||||
for( size_t polygonIdx = 0; polygonIdx < m_polys.size(); polygonIdx++ )
|
||||
filleted.m_polys.push_back( FilletPolygon( aRadius, aErrorMax, polygonIdx ) );
|
||||
for( size_t idx = 0; idx < m_polys.size(); idx++ )
|
||||
filleted.m_polys.push_back( FilletPolygon( aRadius, aErrorMax, idx, aPreserveCorners ) );
|
||||
|
||||
return filleted;
|
||||
}
|
||||
|
||||
|
||||
SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode,
|
||||
unsigned int aDistance,
|
||||
int aIndex,
|
||||
int aErrorMax )
|
||||
unsigned int aDistance, int aIndex, int aErrorMax,
|
||||
std::set<VECTOR2I>* aPreserveCorners )
|
||||
{
|
||||
// Null segments create serious issues in calculations. Remove them:
|
||||
RemoveNullSegments();
|
||||
|
@ -1656,6 +1660,12 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode,
|
|||
int x1 = currContour.Point( currVertex ).x;
|
||||
int y1 = currContour.Point( currVertex ).y;
|
||||
|
||||
if( aPreserveCorners && aPreserveCorners->count( VECTOR2I( x1, y1 ) ) > 0 )
|
||||
{
|
||||
newContour.Append( x1, y1 );
|
||||
continue;
|
||||
}
|
||||
|
||||
// Indices for previous and next vertices.
|
||||
int prevVertex;
|
||||
int nextVertex;
|
||||
|
|
|
@ -1041,9 +1041,11 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
* returns a chamfered version of the aIndex-th polygon.
|
||||
* @param aDistance is the chamfering distance.
|
||||
* @param aIndex is the index of the polygon to be chamfered.
|
||||
* @param aPreserveCorners an optional set of corners which should not be chamfered.
|
||||
* @return POLYGON - A polygon containing the chamfered version of the aIndex-th polygon.
|
||||
*/
|
||||
POLYGON ChamferPolygon( unsigned int aDistance, int aIndex = 0 );
|
||||
POLYGON ChamferPolygon( unsigned int aDistance, int aIndex,
|
||||
std::set<VECTOR2I>* aPreserveCorners );
|
||||
|
||||
/**
|
||||
* Function Fillet
|
||||
|
@ -1051,26 +1053,32 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
* @param aRadius is the fillet radius.
|
||||
* @param aErrorMax is the maximum allowable deviation of the polygon from the circle
|
||||
* @param aIndex is the index of the polygon to be filleted
|
||||
* @param aPreserveCorners an optional set of corners which should not be filleted.
|
||||
* @return POLYGON - A polygon containing the filleted version of the aIndex-th polygon.
|
||||
*/
|
||||
POLYGON FilletPolygon( unsigned int aRadius, int aErrorMax, int aIndex = 0 );
|
||||
POLYGON FilletPolygon( unsigned int aRadius, int aErrorMax, int aIndex,
|
||||
std::set<VECTOR2I>* aPreserveCorners = nullptr );
|
||||
|
||||
/**
|
||||
* Function Chamfer
|
||||
* returns a chamfered version of the polygon set.
|
||||
* @param aDistance is the chamfering distance.
|
||||
* @param aPreserveCorners an optional set of corners which should not be chamfered.
|
||||
* @return SHAPE_POLY_SET - A set containing the chamfered version of this set.
|
||||
*/
|
||||
SHAPE_POLY_SET Chamfer( int aDistance );
|
||||
SHAPE_POLY_SET Chamfer( int aDistance,
|
||||
std::set<VECTOR2I>* aPreserveCorners = nullptr );
|
||||
|
||||
/**
|
||||
* Function Fillet
|
||||
* returns a filleted version of the polygon set.
|
||||
* @param aRadius is the fillet radius.
|
||||
* @param aErrorMax is the maximum allowable deviation of the polygon from the circle
|
||||
* @param aPreserveCorners an optional set of corners which should not be filleted.
|
||||
* @return SHAPE_POLY_SET - A set containing the filleted version of this set.
|
||||
*/
|
||||
SHAPE_POLY_SET Fillet( int aRadius, int aErrorMax );
|
||||
SHAPE_POLY_SET Fillet( int aRadius, int aErrorMax,
|
||||
std::set<VECTOR2I>* aPreserveCorners = nullptr );
|
||||
|
||||
/**
|
||||
* Function DistanceToPolygon
|
||||
|
@ -1145,6 +1153,9 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
void booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aShape,
|
||||
const SHAPE_POLY_SET& aOtherShape, POLYGON_MODE aFastMode );
|
||||
|
||||
bool pointInPolygon( const VECTOR2I& aP, const SHAPE_LINE_CHAIN& aPath,
|
||||
bool aIgnoreEdges, bool aUseBBoxCaches = false ) const;
|
||||
|
||||
/**
|
||||
* containsSingle function
|
||||
* Checks whether the point aP is inside the aSubpolyIndex-th polygon of the polyset. If
|
||||
|
@ -1186,10 +1197,12 @@ class SHAPE_POLY_SET : public SHAPE
|
|||
* @param aIndex is the index of the polygon that will be chamfered/filleted.
|
||||
* @param aErrorMax is the maximum allowable deviation of the polygon from the circle
|
||||
* if aMode = FILLETED. If aMode = CHAMFERED, it is unused.
|
||||
* @param aPreserveCorners an optional set of corners which should be skipped.
|
||||
* @return POLYGON - the chamfered/filleted version of the polygon.
|
||||
*/
|
||||
POLYGON chamferFilletPolygon( CORNER_MODE aMode, unsigned int aDistance,
|
||||
int aIndex, int aErrorMax );
|
||||
int aIndex, int aErrorMax,
|
||||
std::set<VECTOR2I>* aPreserveCorners );
|
||||
|
||||
///> Returns true if the polygon set has any holes that touch share a vertex.
|
||||
bool hasTouchingHoles( const POLYGON& aPoly ) const;
|
||||
|
|
|
@ -1158,7 +1158,34 @@ void ZONE_CONTAINER::CacheTriangulation()
|
|||
}
|
||||
|
||||
|
||||
bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly ) const
|
||||
/*
|
||||
* Some intersecting zones, despite being on the same layer with the same net, cannot be
|
||||
* merged due to other parameters such as fillet radius. The copper pour will end up
|
||||
* effectively merged though, so we want to keep the corners of such intersections sharp.
|
||||
*/
|
||||
void ZONE_CONTAINER::GetColinearCorners( BOARD* aBoard, std::set<VECTOR2I>& aCorners )
|
||||
{
|
||||
int epsilon = Millimeter2iu( 0.001 );
|
||||
|
||||
for( ZONE_CONTAINER* candidate : aBoard->Zones() )
|
||||
{
|
||||
if( candidate != this
|
||||
&& candidate->GetNetCode() == GetNetCode()
|
||||
&& candidate->GetLayerSet() == GetLayerSet()
|
||||
&& candidate->GetIsKeepout() == GetIsKeepout() )
|
||||
{
|
||||
for( auto iter = m_Poly->CIterate(); iter; iter++ )
|
||||
{
|
||||
if( candidate->m_Poly->Collide( iter.Get(), epsilon ) )
|
||||
aCorners.insert( VECTOR2I( iter.Get() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly,
|
||||
std::set<VECTOR2I>* aPreserveCorners ) const
|
||||
{
|
||||
if( GetNumCorners() <= 2 ) // malformed zone. polygon calculations do not like it ...
|
||||
return false;
|
||||
|
@ -1167,7 +1194,7 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly ) const
|
|||
switch( m_cornerSmoothingType )
|
||||
{
|
||||
case ZONE_SETTINGS::SMOOTHING_CHAMFER:
|
||||
aSmoothedPoly = m_Poly->Chamfer( m_cornerRadius );
|
||||
aSmoothedPoly = m_Poly->Chamfer( m_cornerRadius, aPreserveCorners );
|
||||
break;
|
||||
|
||||
case ZONE_SETTINGS::SMOOTHING_FILLET:
|
||||
|
@ -1178,7 +1205,7 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly ) const
|
|||
if( board )
|
||||
maxError = board->GetDesignSettings().m_MaxError;
|
||||
|
||||
aSmoothedPoly = m_Poly->Fillet( m_cornerRadius, maxError );
|
||||
aSmoothedPoly = m_Poly->Fillet( m_cornerRadius, maxError, aPreserveCorners );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -1187,7 +1214,7 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly ) const
|
|||
// We can avoid issues by creating a very small chamfer which remove acute angles,
|
||||
// or left it without chamfer and use only CPOLYGONS_LIST::InflateOutline to create
|
||||
// clearance areas
|
||||
aSmoothedPoly = m_Poly->Chamfer( Millimeter2iu( 0.0 ) );
|
||||
aSmoothedPoly = m_Poly->Chamfer( Millimeter2iu( 0.0 ), aPreserveCorners );
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1202,13 +1229,16 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly ) const
|
|||
* @param aUseNetClearance true to use a clearance which is the max value between
|
||||
* aMinClearanceValue and the net clearance
|
||||
* false to use aMinClearanceValue only
|
||||
* @param aPreserveCorners an optional set of corners which should not be chamfered/filleted
|
||||
*/
|
||||
void ZONE_CONTAINER::TransformOutlinesShapeWithClearanceToPolygon(
|
||||
SHAPE_POLY_SET& aCornerBuffer, int aMinClearanceValue, bool aUseNetClearance ) const
|
||||
void ZONE_CONTAINER::TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
int aMinClearanceValue,
|
||||
bool aUseNetClearance,
|
||||
std::set<VECTOR2I>* aPreserveCorners ) const
|
||||
{
|
||||
// Creates the zone outline polygon (with holes if any)
|
||||
SHAPE_POLY_SET polybuffer;
|
||||
BuildSmoothedPoly( polybuffer );
|
||||
BuildSmoothedPoly( polybuffer, aPreserveCorners );
|
||||
|
||||
// add clearance to outline
|
||||
int clearance = aMinClearanceValue;
|
||||
|
|
|
@ -255,6 +255,13 @@ public:
|
|||
*/
|
||||
bool HitTestFilledArea( const wxPoint& aRefPos ) const;
|
||||
|
||||
/**
|
||||
* Some intersecting zones, despite being on the same layer with the same net, cannot be
|
||||
* merged due to other parameters such as fillet radius. The copper pour will end up
|
||||
* effectively merged though, so we want to keep the corners of such intersections sharp.
|
||||
*/
|
||||
void GetColinearCorners( BOARD* aBoard, std::set<VECTOR2I>& aCorners );
|
||||
|
||||
/**
|
||||
* Function TransformSolidAreasShapesToPolygonSet
|
||||
* Convert solid areas full shapes to polygon set
|
||||
|
@ -279,11 +286,12 @@ public:
|
|||
* @param aUseNetClearance = true to use a clearance which is the max value between
|
||||
* aMinClearanceValue and the net clearance
|
||||
* false to use aMinClearanceValue only
|
||||
* @param aPreserveCorners an optional set of corners which should not be smoothed.
|
||||
* if both aMinClearanceValue = 0 and aUseNetClearance = false: create the zone outline polygon.
|
||||
*/
|
||||
void TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
|
||||
int aMinClearanceValue,
|
||||
bool aUseNetClearance ) const;
|
||||
int aMinClearanceValue, bool aUseNetClearance,
|
||||
std::set<VECTOR2I>* aPreserveCorners = nullptr ) const;
|
||||
|
||||
/**
|
||||
* Function TransformShapeWithClearanceToPolygon
|
||||
|
@ -564,9 +572,11 @@ public:
|
|||
/**
|
||||
* Function GetSmoothedPoly
|
||||
* returns a pointer to the corner-smoothed version of m_Poly.
|
||||
* @param aPreserveCorners - set of corners which should /not/ be smoothed
|
||||
* @return SHAPE_POLY_SET* - pointer to the polygon.
|
||||
*/
|
||||
bool BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly ) const;
|
||||
bool BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly,
|
||||
std::set<VECTOR2I>* aPreserveCorners ) const;
|
||||
|
||||
void SetCornerSmoothingType( int aType ) { m_cornerSmoothingType = aType; };
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ public:
|
|||
|
||||
void PlotDimension( DIMENSION* Dimension );
|
||||
void PlotPcbTarget( PCB_TARGET* PtMire );
|
||||
void PlotFilledAreas( ZONE_CONTAINER* aZone );
|
||||
void PlotFilledAreas( ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aPolysList );
|
||||
void PlotTextePcb( TEXTE_PCB* pt_texte );
|
||||
void PlotDrawSegment( DRAWSEGMENT* PtSegm );
|
||||
|
||||
|
|
|
@ -53,18 +53,16 @@
|
|||
#include <pcbplot.h>
|
||||
#include <gbr_metadata.h>
|
||||
|
||||
// Local
|
||||
/* Plot a solder mask layer.
|
||||
* Solder mask layers have a minimum thickness value and cannot be drawn like standard layers,
|
||||
* unless the minimum thickness is 0.
|
||||
/*
|
||||
* Plot a solder mask layer. Solder mask layers have a minimum thickness value and cannot be
|
||||
* drawn like standard layers, unless the minimum thickness is 0.
|
||||
*/
|
||||
static void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
||||
LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt,
|
||||
int aMinThickness );
|
||||
static void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
|
||||
const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness );
|
||||
|
||||
/* Creates the plot for silkscreen layers
|
||||
* Silkscreen layers have specific requirement for pads (not filled) and texts
|
||||
* (with option to remove them from some copper areas (pads...)
|
||||
/*
|
||||
* Creates the plot for silkscreen layers. Silkscreen layers have specific requirement for
|
||||
* pads (not filled) and texts (with option to remove them from some copper areas (pads...)
|
||||
*/
|
||||
void PlotSilkScreen( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
|
||||
const PCB_PLOT_PARAMS& aPlotOpt )
|
||||
|
@ -120,23 +118,31 @@ void PlotSilkScreen( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
|
|||
if( ! itemplotter.PlotAllTextsModule( module ) )
|
||||
{
|
||||
wxLogMessage( _( "Your BOARD has a bad layer number for footprint %s" ),
|
||||
GetChars( module->GetReference() ) );
|
||||
module->GetReference() );
|
||||
}
|
||||
}
|
||||
|
||||
// Plot filled areas
|
||||
aPlotter->StartBlock( NULL );
|
||||
|
||||
for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ )
|
||||
{
|
||||
ZONE_CONTAINER* edge_zone = aBoard->GetArea( ii );
|
||||
// Plot all zones together so we don't end up with divots where zones touch each other.
|
||||
ZONE_CONTAINER* zone = nullptr;
|
||||
SHAPE_POLY_SET aggregateArea;
|
||||
|
||||
if( !aLayerMask[ edge_zone->GetLayer() ] )
|
||||
for( ZONE_CONTAINER* candidate : aBoard->Zones() )
|
||||
{
|
||||
if( !aLayerMask[ candidate->GetLayer() ] )
|
||||
continue;
|
||||
|
||||
itemplotter.PlotFilledAreas( edge_zone );
|
||||
if( !zone )
|
||||
zone = candidate;
|
||||
|
||||
aggregateArea.BooleanAdd( candidate->GetFilledPolysList(), SHAPE_POLY_SET::PM_FAST );
|
||||
}
|
||||
|
||||
aggregateArea.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
itemplotter.PlotFilledAreas( zone, aggregateArea );
|
||||
|
||||
aPlotter->EndBlock( NULL );
|
||||
}
|
||||
|
||||
|
@ -150,8 +156,8 @@ void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, PCB_LAYER_ID aLayer,
|
|||
aPlotter->SetColor( aPlotOpt.GetColor() );
|
||||
aPlotter->SetTextMode( aPlotOpt.GetTextMode() );
|
||||
|
||||
// Specify that the contents of the "Edges Pcb" layer are to be plotted
|
||||
// in addition to the contents of the currently specified layer.
|
||||
// Specify that the contents of the "Edges Pcb" layer are to be plotted in addition to the
|
||||
// contents of the currently specified layer.
|
||||
LSET layer_mask( aLayer );
|
||||
|
||||
if( !aPlotOpt.GetExcludeEdgeLayer() )
|
||||
|
@ -160,8 +166,7 @@ void PlotOneBoardLayer( BOARD *aBoard, PLOTTER* aPlotter, PCB_LAYER_ID aLayer,
|
|||
if( IsCopperLayer( aLayer ) )
|
||||
{
|
||||
// Skip NPTH pads on copper layers ( only if hole size == pad size ):
|
||||
// Drill mark will be plotted,
|
||||
// if drill mark is SMALL_DRILL_SHAPE or FULL_DRILL_SHAPE
|
||||
// Drill mark will be plotted if drill mark is SMALL_DRILL_SHAPE or FULL_DRILL_SHAPE
|
||||
if( plotOpt.GetFormat() == PLOT_FORMAT_DXF )
|
||||
{
|
||||
plotOpt.SetSkipPlotNPTH_Pads( false );
|
||||
|
@ -312,18 +317,8 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
{
|
||||
for( auto item : module->GraphicalItems() )
|
||||
{
|
||||
if( !aLayerMask[ item->GetLayer() ] )
|
||||
continue;
|
||||
|
||||
switch( item->Type() )
|
||||
{
|
||||
case PCB_MODULE_EDGE_T:
|
||||
if( item->Type() == PCB_MODULE_EDGE_T && aLayerMask[ item->GetLayer() ] )
|
||||
itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -439,31 +434,29 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
break;
|
||||
|
||||
case PAD_SHAPE_CUSTOM:
|
||||
{
|
||||
// inflate/deflate a custom shape is a bit complex.
|
||||
// so build a similar pad shape, and inflate/deflate the polygonal shape
|
||||
{
|
||||
D_PAD dummy( *pad );
|
||||
SHAPE_POLY_SET shape;
|
||||
pad->MergePrimitivesAsPolygon( &shape );
|
||||
// shape polygon can have holes linked to the main outline.
|
||||
// So use InflateWithLinkedHoles(), not Inflate() that can create
|
||||
// bad shapes if margin.x is < 0
|
||||
int numSegs = std::max(
|
||||
GetArcToSegmentCount( margin.x, aBoard->GetDesignSettings().m_MaxError,
|
||||
360.0 ), 6 );
|
||||
// Shape polygon can have holes so use InflateWithLinkedHoles(), not Inflate()
|
||||
// which can create bad shapes if margin.x is < 0
|
||||
int maxError = aBoard->GetDesignSettings().m_MaxError;
|
||||
int numSegs = std::max( GetArcToSegmentCount( margin.x, maxError, 360.0 ), 6 );
|
||||
shape.InflateWithLinkedHoles( margin.x, numSegs, SHAPE_POLY_SET::PM_FAST );
|
||||
dummy.DeletePrimitivesList();
|
||||
dummy.AddPrimitive( shape, 0 );
|
||||
dummy.MergePrimitivesAsPolygon();
|
||||
|
||||
// Be sure the anchor pad is not bigger than the deflated shape
|
||||
// because this anchor will be added to the pad shape when plotting
|
||||
// the pad. So now the polygonal shape is built, we can clamp the anchor size
|
||||
// Be sure the anchor pad is not bigger than the deflated shape because this
|
||||
// anchor will be added to the pad shape when plotting the pad. So now the
|
||||
// polygonal shape is built, we can clamp the anchor size
|
||||
if( margin.x < 0 ) // we expect margin.x = margin.y for custom pads
|
||||
dummy.SetSize( padPlotsSize );
|
||||
|
||||
itemplotter.PlotPad( &dummy, color, plotMode );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -496,9 +489,9 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
if( !Via )
|
||||
continue;
|
||||
|
||||
// vias are not plotted if not on selected layer, but if layer
|
||||
// is SOLDERMASK_LAYER_BACK or SOLDERMASK_LAYER_FRONT,vias are drawn,
|
||||
// only if they are on the corresponding external copper layer
|
||||
// vias are not plotted if not on selected layer, but if layer is SOLDERMASK_LAYER_BACK
|
||||
// or SOLDERMASK_LAYER_FRONT, vias are drawn only if they are on the corresponding
|
||||
// external copper layer
|
||||
LSET via_mask_layer = Via->GetLayerSet();
|
||||
|
||||
if( aPlotOpt.GetPlotViaOnMaskLayer() )
|
||||
|
@ -516,8 +509,7 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
int via_margin = 0;
|
||||
double width_adj = 0;
|
||||
|
||||
// If the current layer is a solder mask, use the global mask
|
||||
// clearance for vias
|
||||
// If the current layer is a solder mask, use the global mask clearance for vias
|
||||
if( aLayerMask[B_Mask] || aLayerMask[F_Mask] )
|
||||
via_margin = aBoard->GetDesignSettings().m_SolderMaskMargin;
|
||||
|
||||
|
@ -537,8 +529,8 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
gbr_metadata.SetNetName( Via->GetNetname() );
|
||||
|
||||
COLOR4D color = aBoard->Colors().GetItemColor( LAYER_VIAS + Via->GetViaType() );
|
||||
// Set plot color (change WHITE to LIGHTGRAY because
|
||||
// the white items are not seen on a white paper or screen
|
||||
// Set plot color (change WHITE to LIGHTGRAY because the white items are not seen on a
|
||||
// white paper or screen
|
||||
aPlotter->SetColor( color != WHITE ? color : LIGHTGRAY);
|
||||
aPlotter->FlashPadCircle( Via->GetStart(), diameter, plotMode, &gbr_metadata );
|
||||
}
|
||||
|
@ -570,14 +562,34 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
|
||||
// Plot filled ares
|
||||
aPlotter->StartBlock( NULL );
|
||||
for( int ii = 0; ii < aBoard->GetAreaCount(); ii++ )
|
||||
{
|
||||
ZONE_CONTAINER* zone = aBoard->GetArea( ii );
|
||||
|
||||
if( !aLayerMask[zone->GetLayer()] )
|
||||
// Plot all zones of the same layer & net together so we don't end up with divots where
|
||||
// zones touch each other.
|
||||
std::set<ZONE_CONTAINER*> plotted;
|
||||
|
||||
for( ZONE_CONTAINER* zone : aBoard->Zones() )
|
||||
{
|
||||
if( !aLayerMask[ zone->GetLayer() ] || plotted.count( zone ) )
|
||||
continue;
|
||||
|
||||
itemplotter.PlotFilledAreas( zone );
|
||||
plotted.insert( zone );
|
||||
|
||||
SHAPE_POLY_SET aggregateArea = zone->GetFilledPolysList();
|
||||
|
||||
for( ZONE_CONTAINER* candidate : aBoard->Zones() )
|
||||
{
|
||||
if( !aLayerMask[ candidate->GetLayer() ] || plotted.count( candidate ) )
|
||||
continue;
|
||||
|
||||
if( candidate->GetNetCode() != zone->GetNetCode() )
|
||||
continue;
|
||||
|
||||
plotted.insert( candidate );
|
||||
aggregateArea.BooleanAdd( candidate->GetFilledPolysList(), SHAPE_POLY_SET::PM_FAST );
|
||||
}
|
||||
|
||||
aggregateArea.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
itemplotter.PlotFilledAreas( zone, aggregateArea );
|
||||
}
|
||||
aPlotter->EndBlock( NULL );
|
||||
|
||||
|
@ -646,10 +658,11 @@ static const PCB_LAYER_ID plot_seq[] = {
|
|||
};
|
||||
|
||||
|
||||
/* Plot outlines of copper, for copper layer
|
||||
/*
|
||||
* Plot outlines of copper, for copper layer
|
||||
*/
|
||||
void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter,
|
||||
LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt )
|
||||
void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter, LSET aLayerMask,
|
||||
const PCB_PLOT_PARAMS& aPlotOpt )
|
||||
{
|
||||
|
||||
BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
|
||||
|
@ -678,8 +691,7 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter,
|
|||
const SHAPE_LINE_CHAIN& path = (kk == 0) ? outlines.COutline( ii ) : outlines.CHole( ii, kk - 1 );
|
||||
|
||||
for( int jj = 0; jj < path.PointCount(); jj++ )
|
||||
cornerList.push_back( wxPoint( path.CPoint( jj ).x , path.CPoint( jj ).y ) );
|
||||
|
||||
cornerList.emplace_back( (wxPoint) path.CPoint( jj ) );
|
||||
|
||||
// Ensure the polygon is closed
|
||||
if( cornerList[0] != cornerList[cornerList.size() - 1] )
|
||||
|
@ -716,7 +728,7 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter,
|
|||
int width;
|
||||
pad->GetOblongDrillGeometry( drl_start, drl_end, width );
|
||||
aPlotter->ThickSegment( pad->GetPosition() + drl_start,
|
||||
pad->GetPosition() + drl_end, width, SKETCH, NULL );
|
||||
pad->GetPosition() + drl_end, width, SKETCH, NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -754,9 +766,8 @@ void PlotLayerOutlines( BOARD* aBoard, PLOTTER* aPlotter,
|
|||
* plot all other shapes by flashing the basing shape
|
||||
* (shapes will be better, and calculations faster)
|
||||
*/
|
||||
void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
||||
LSET aLayerMask, const PCB_PLOT_PARAMS& aPlotOpt,
|
||||
int aMinThickness )
|
||||
void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
|
||||
const PCB_PLOT_PARAMS& aPlotOpt, int aMinThickness )
|
||||
{
|
||||
PCB_LAYER_ID layer = aLayerMask[B_Mask] ? B_Mask : F_Mask;
|
||||
|
||||
|
@ -768,38 +779,23 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
BRDITEMS_PLOTTER itemplotter( aPlotter, aBoard, aPlotOpt );
|
||||
itemplotter.SetLayerSet( aLayerMask );
|
||||
|
||||
// Plot edge layer and graphic items
|
||||
// They do not have a solder Mask margin, because they are only graphic items
|
||||
// on this layer (like logos), not actually areas around pads.
|
||||
// Plot edge layer and graphic items. They do not have a solder Mask margin, because they
|
||||
// are only graphic items on this layer (like logos), not actually areas around pads.
|
||||
itemplotter.PlotBoardGraphicItems();
|
||||
|
||||
for( auto module : aBoard->Modules() )
|
||||
{
|
||||
for( auto item : module->GraphicalItems() )
|
||||
{
|
||||
if( layer != item->GetLayer() )
|
||||
continue;
|
||||
|
||||
switch( item->Type() )
|
||||
{
|
||||
case PCB_MODULE_EDGE_T:
|
||||
if( item->Type() == PCB_MODULE_EDGE_T && item->GetLayer() == layer )
|
||||
itemplotter.Plot_1_EdgeModule( (EDGE_MODULE*) item );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build polygons for each pad shape.
|
||||
// the size of the shape on solder mask should be:
|
||||
// size of pad + clearance around the pad.
|
||||
// clearance = solder mask clearance + extra margin
|
||||
// extra margin is half the min width for solder mask
|
||||
// This extra margin is used to merge too close shapes
|
||||
// (distance < aMinThickness), and will be removed when creating
|
||||
// the actual shapes
|
||||
// Build polygons for each pad shape. The size of the shape on solder mask should be size
|
||||
// of pad + clearance around the pad, where clearance = solder mask clearance + extra margin.
|
||||
// Extra margin is half the min width for solder mask, which is used to merge too-close shapes
|
||||
// (distance < aMinThickness), and will be removed when creating the actual shapes.
|
||||
SHAPE_POLY_SET areas; // Contains shapes to plot
|
||||
SHAPE_POLY_SET initialPolys; // Contains exact shapes to plot
|
||||
|
||||
|
@ -815,8 +811,7 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
// Plot vias on solder masks, if aPlotOpt.GetPlotViaOnMaskLayer() is true,
|
||||
if( aPlotOpt.GetPlotViaOnMaskLayer() )
|
||||
{
|
||||
// The current layer is a solder mask,
|
||||
// use the global mask clearance for vias
|
||||
// The current layer is a solder mask, use the global mask clearance for vias
|
||||
int via_clearance = aBoard->GetDesignSettings().m_SolderMaskMargin;
|
||||
int via_margin = via_clearance + inflate;
|
||||
|
||||
|
@ -827,8 +822,7 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
if( !via )
|
||||
continue;
|
||||
|
||||
// vias are plotted only if they are on the corresponding
|
||||
// external copper layer
|
||||
// vias are plotted only if they are on the corresponding external copper layer
|
||||
LSET via_set = via->GetLayerSet();
|
||||
|
||||
if( via_set[B_Cu] )
|
||||
|
@ -857,38 +851,43 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter,
|
|||
if( zone->GetLayer() != layer )
|
||||
continue;
|
||||
|
||||
zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate + zone_margin, false );
|
||||
zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin, false );
|
||||
// Some intersecting zones, despite being on the same layer with the same net, cannot be
|
||||
// merged due to other parameters such as fillet radius. The copper pour will end up
|
||||
// effectively merged though, so we want to keep the corners of such intersections sharp.
|
||||
std::set<VECTOR2I> colinearCorners;
|
||||
zone->GetColinearCorners( aBoard, colinearCorners );
|
||||
|
||||
zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate + zone_margin, false,
|
||||
&colinearCorners );
|
||||
zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin, false,
|
||||
&colinearCorners );
|
||||
}
|
||||
|
||||
// To avoid a lot of code, use a ZONE_CONTAINER
|
||||
// to handle and plot polygons, because our polygons look exactly like
|
||||
// filled areas in zones
|
||||
// Note, also this code is not optimized: it creates a lot of copy/duplicate data
|
||||
// However it is not complex, and fast enough for plot purposes (copy/convert data
|
||||
// is only a very small calculation time for these calculations)
|
||||
// To avoid a lot of code, use a ZONE_CONTAINER to handle and plot polygons, because our
|
||||
// polygons look exactly like filled areas in zones.
|
||||
// Note, also this code is not optimized: it creates a lot of copy/duplicate data.
|
||||
// However it is not complex, and fast enough for plot purposes (copy/convert data is only a
|
||||
// very small calculation time for these calculations).
|
||||
ZONE_CONTAINER zone( aBoard );
|
||||
zone.SetMinThickness( 0 ); // trace polygons only
|
||||
zone.SetLayer( layer );
|
||||
int numSegs = std::max(
|
||||
GetArcToSegmentCount( inflate, aBoard->GetDesignSettings().m_MaxError, 360.0 ), 6 );
|
||||
int maxError = aBoard->GetDesignSettings().m_MaxError;
|
||||
int numSegs = std::max( GetArcToSegmentCount( inflate, maxError, 360.0 ), 6 );
|
||||
|
||||
areas.BooleanAdd( initialPolys, SHAPE_POLY_SET::PM_FAST );
|
||||
areas.Inflate( -inflate, numSegs );
|
||||
areas.Deflate( inflate, numSegs );
|
||||
|
||||
// Combine the current areas to initial areas. This is mandatory because
|
||||
// inflate/deflate transform is not perfect, and we want the initial areas perfectly kept
|
||||
// Combine the current areas to initial areas. This is mandatory because inflate/deflate
|
||||
// transform is not perfect, and we want the initial areas perfectly kept
|
||||
areas.BooleanAdd( initialPolys, SHAPE_POLY_SET::PM_FAST );
|
||||
areas.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
|
||||
zone.SetFilledPolysList( areas );
|
||||
|
||||
itemplotter.PlotFilledAreas( &zone );
|
||||
itemplotter.PlotFilledAreas( &zone, areas );
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Set up most plot options for plotting a board (especially the viewport)
|
||||
/**
|
||||
* Set up most plot options for plotting a board (especially the viewport)
|
||||
* Important thing:
|
||||
* page size is the 'drawing' page size,
|
||||
* paper size is the physical page size
|
||||
|
@ -904,14 +903,12 @@ static void initializePlotter( PLOTTER *aPlotter, BOARD * aBoard,
|
|||
wxSize pageSizeIU( pageInfo.GetSizeIU() );
|
||||
bool autocenter = false;
|
||||
|
||||
/* Special options: to fit the sheet to an A4 sheet replace
|
||||
the paper size. However there is a difference between
|
||||
the autoscale and the a4paper option:
|
||||
- Autoscale fits the board to the paper size
|
||||
- A4paper fits the original paper size to an A4 sheet
|
||||
- Both of them fit the board to an A4 sheet
|
||||
*/
|
||||
if( aPlotOpts->GetA4Output() ) // Fit paper to A4
|
||||
// Special options: to fit the sheet to an A4 sheet replace the paper size. However there
|
||||
// is a difference between the autoscale and the a4paper option:
|
||||
// - Autoscale fits the board to the paper size
|
||||
// - A4paper fits the original paper size to an A4 sheet
|
||||
// - Both of them fit the board to an A4 sheet
|
||||
if( aPlotOpts->GetA4Output() )
|
||||
{
|
||||
sheet_info = &pageA4;
|
||||
paperSizeIU = pageA4.GetSizeIU();
|
||||
|
@ -934,8 +931,8 @@ static void initializePlotter( PLOTTER *aPlotter, BOARD * aBoard,
|
|||
|
||||
double compound_scale;
|
||||
|
||||
/* Fit to 80% of the page if asked; it could be that the board is empty,
|
||||
* in this case regress to 1:1 scale */
|
||||
// Fit to 80% of the page if asked; it could be that the board is empty, in this case
|
||||
// regress to 1:1 scale
|
||||
if( aPlotOpts->GetAutoScale() && boardSize.x > 0 && boardSize.y > 0 )
|
||||
{
|
||||
double xscale = (paperSizeIU.x * 0.8) / boardSize.x;
|
||||
|
@ -947,9 +944,8 @@ static void initializePlotter( PLOTTER *aPlotter, BOARD * aBoard,
|
|||
compound_scale = aPlotOpts->GetScale() * paperscale;
|
||||
|
||||
|
||||
/* For the plot offset we have to keep in mind the auxiliary origin
|
||||
too: if autoscaling is off we check that plot option (i.e. autoscaling
|
||||
overrides auxiliary origin) */
|
||||
// For the plot offset we have to keep in mind the auxiliary origin too: if autoscaling is
|
||||
// off we check that plot option (i.e. autoscaling overrides auxiliary origin)
|
||||
wxPoint offset( 0, 0);
|
||||
|
||||
if( autocenter )
|
||||
|
@ -963,13 +959,10 @@ static void initializePlotter( PLOTTER *aPlotter, BOARD * aBoard,
|
|||
offset = aBoard->GetAuxOrigin();
|
||||
}
|
||||
|
||||
/* Configure the plotter object with all the stuff computed and
|
||||
most of that taken from the options */
|
||||
aPlotter->SetPageSettings( *sheet_info );
|
||||
|
||||
aPlotter->SetViewport( offset, IU_PER_MILS/10, compound_scale,
|
||||
aPlotOpts->GetMirror() );
|
||||
// has meaning only for gerber plotter. Must be called only after SetViewport
|
||||
aPlotter->SetViewport( offset, IU_PER_MILS/10, compound_scale, aPlotOpts->GetMirror() );
|
||||
// Has meaning only for gerber plotter. Must be called only after SetViewport
|
||||
aPlotter->SetGerberCoordinatesFormat( aPlotOpts->GetGerberPrecision() );
|
||||
|
||||
aPlotter->SetDefaultLineWidth( aPlotOpts->GetLineWidth() );
|
||||
|
@ -978,48 +971,47 @@ static void initializePlotter( PLOTTER *aPlotter, BOARD * aBoard,
|
|||
aPlotter->SetTextMode( aPlotOpts->GetTextMode() );
|
||||
}
|
||||
|
||||
/** Prefill in black an area a little bigger than the board to prepare for the
|
||||
* negative plot */
|
||||
|
||||
/**
|
||||
* Prefill in black an area a little bigger than the board to prepare for the negative plot
|
||||
*/
|
||||
static void FillNegativeKnockout( PLOTTER *aPlotter, const EDA_RECT &aBbbox )
|
||||
{
|
||||
const int margin = 5 * IU_PER_MM; // Add a 5 mm margin around the board
|
||||
aPlotter->SetNegative( true );
|
||||
aPlotter->SetColor( WHITE ); // Which will be plotted as black
|
||||
aPlotter->SetColor( WHITE ); // Which will be plotted as black
|
||||
EDA_RECT area = aBbbox;
|
||||
area.Inflate( margin );
|
||||
aPlotter->Rect( area.GetOrigin(), area.GetEnd(), FILLED_SHAPE );
|
||||
aPlotter->SetColor( BLACK );
|
||||
}
|
||||
|
||||
/** Calculate the effective size of HPGL pens and set them in the
|
||||
* plotter object */
|
||||
static void ConfigureHPGLPenSizes( HPGL_PLOTTER *aPlotter,
|
||||
PCB_PLOT_PARAMS *aPlotOpts )
|
||||
|
||||
/**
|
||||
* Calculate the effective size of HPGL pens and set them in the plotter object
|
||||
*/
|
||||
static void ConfigureHPGLPenSizes( HPGL_PLOTTER *aPlotter, PCB_PLOT_PARAMS *aPlotOpts )
|
||||
{
|
||||
/* Compute pen_dim (the value is given in mils) in pcb units,
|
||||
with plot scale (if Scale is 2, pen diameter value is always m_HPGLPenDiam
|
||||
so apparent pen diam is actually pen diam / Scale */
|
||||
int pen_diam = KiROUND( aPlotOpts->GetHPGLPenDiameter() * IU_PER_MILS /
|
||||
aPlotOpts->GetScale() );
|
||||
// Compute penDiam (the value is given in mils) in pcb units, with plot scale (if Scale is 2,
|
||||
// penDiam value is always m_HPGLPenDiam so apparent penDiam is actually penDiam / Scale
|
||||
int penDiam = KiROUND( aPlotOpts->GetHPGLPenDiameter() * IU_PER_MILS / aPlotOpts->GetScale() );
|
||||
|
||||
// Set HPGL-specific options and start
|
||||
aPlotter->SetPenSpeed( aPlotOpts->GetHPGLPenSpeed() );
|
||||
aPlotter->SetPenNumber( aPlotOpts->GetHPGLPenNum() );
|
||||
aPlotter->SetPenDiameter( pen_diam );
|
||||
aPlotter->SetPenDiameter( penDiam );
|
||||
}
|
||||
|
||||
/** Open a new plotfile using the options (and especially the format)
|
||||
* specified in the options and prepare the page for plotting.
|
||||
* Return the plotter object if OK, NULL if the file is not created
|
||||
* (or has a problem)
|
||||
|
||||
/**
|
||||
* Open a new plotfile using the options (and especially the format) specified in the options
|
||||
* and prepare the page for plotting.
|
||||
* Return the plotter object if OK, NULL if the file is not created (or has a problem)
|
||||
*/
|
||||
PLOTTER* StartPlotBoard( BOARD *aBoard, PCB_PLOT_PARAMS *aPlotOpts,
|
||||
int aLayer,
|
||||
const wxString& aFullFileName,
|
||||
const wxString& aSheetDesc )
|
||||
PLOTTER* StartPlotBoard( BOARD *aBoard, PCB_PLOT_PARAMS *aPlotOpts, int aLayer,
|
||||
const wxString& aFullFileName, const wxString& aSheetDesc )
|
||||
{
|
||||
// Create the plotter driver and set the few plotter specific
|
||||
// options
|
||||
// Create the plotter driver and set the few plotter specific options
|
||||
PLOTTER* plotter = NULL;
|
||||
|
||||
switch( aPlotOpts->GetFormat() )
|
||||
|
@ -1049,8 +1041,7 @@ PLOTTER* StartPlotBoard( BOARD *aBoard, PCB_PLOT_PARAMS *aPlotOpts,
|
|||
HPGL_PLOTTER* HPGL_plotter;
|
||||
HPGL_plotter = new HPGL_PLOTTER();
|
||||
|
||||
/* HPGL options are a little more convoluted to compute, so
|
||||
they're split in another function */
|
||||
// HPGL options are a little more convoluted to compute, so they get their own function
|
||||
ConfigureHPGLPenSizes( HPGL_plotter, aPlotOpts );
|
||||
plotter = HPGL_plotter;
|
||||
break;
|
||||
|
@ -1070,8 +1061,7 @@ PLOTTER* StartPlotBoard( BOARD *aBoard, PCB_PLOT_PARAMS *aPlotOpts,
|
|||
|
||||
// Compute the viewport and set the other options
|
||||
|
||||
// page layout is not mirrored, so temporary change mirror option
|
||||
// just to plot the page layout
|
||||
// page layout is not mirrored, so temporarily change mirror option for the page layout
|
||||
PCB_PLOT_PARAMS plotOpts = *aPlotOpts;
|
||||
|
||||
if( plotOpts.GetPlotFrameRef() && plotOpts.GetMirror() )
|
||||
|
@ -1101,19 +1091,16 @@ PLOTTER* StartPlotBoard( BOARD *aBoard, PCB_PLOT_PARAMS *aPlotOpts,
|
|||
// Plot the frame reference if requested
|
||||
if( aPlotOpts->GetPlotFrameRef() )
|
||||
{
|
||||
PlotWorkSheet( plotter, aBoard->GetTitleBlock(),
|
||||
aBoard->GetPageSettings(),
|
||||
1, 1, // Only one page
|
||||
aSheetDesc, aBoard->GetFileName() );
|
||||
PlotWorkSheet( plotter, aBoard->GetTitleBlock(), aBoard->GetPageSettings(),
|
||||
1, 1, aSheetDesc, aBoard->GetFileName() );
|
||||
|
||||
if( aPlotOpts->GetMirror() )
|
||||
initializePlotter( plotter, aBoard, aPlotOpts );
|
||||
}
|
||||
|
||||
/* When plotting a negative board: draw a black rectangle
|
||||
* (background for plot board in white) and switch the current
|
||||
* color to WHITE; note the color inversion is actually done
|
||||
* in the driver (if supported) */
|
||||
// When plotting a negative board: draw a black rectangle (background for plot board
|
||||
// in white) and switch the current color to WHITE; note the color inversion is actually
|
||||
// done in the driver (if supported)
|
||||
if( aPlotOpts->GetNegative() )
|
||||
{
|
||||
EDA_RECT bbox = aBoard->ComputeBoundingBox();
|
||||
|
|
|
@ -611,11 +611,8 @@ void BRDITEMS_PLOTTER::PlotTextePcb( TEXTE_PCB* pt_texte )
|
|||
}
|
||||
|
||||
|
||||
void BRDITEMS_PLOTTER::PlotFilledAreas( ZONE_CONTAINER* aZone )
|
||||
void BRDITEMS_PLOTTER::PlotFilledAreas( ZONE_CONTAINER* aZone, SHAPE_POLY_SET& polysList )
|
||||
{
|
||||
//Plot areas (given by .m_FilledPolysList member) in a zone
|
||||
const SHAPE_POLY_SET& polysList = aZone->GetFilledPolysList();
|
||||
|
||||
if( polysList.IsEmpty() )
|
||||
return;
|
||||
|
||||
|
|
|
@ -781,7 +781,13 @@ bool PNS_KICAD_IFACE::syncZone( PNS::NODE* aWorld, ZONE_CONTAINER* aZone )
|
|||
if( !aZone->GetIsKeepout() || !aZone->GetDoNotAllowTracks() )
|
||||
return false;
|
||||
|
||||
aZone->BuildSmoothedPoly( poly );
|
||||
// Some intersecting zones, despite being on the same layer with the same net, cannot be
|
||||
// merged due to other parameters such as fillet radius. The copper pour will end up
|
||||
// effectively merged though, so we want to keep the corners of such intersections sharp.
|
||||
std::set<VECTOR2I> colinearCorners;
|
||||
aZone->GetColinearCorners( m_board, colinearCorners );
|
||||
|
||||
aZone->BuildSmoothedPoly( poly, &colinearCorners );
|
||||
poly.CacheTriangulation();
|
||||
|
||||
if( !poly.IsTriangulationUpToDate() )
|
||||
|
|
|
@ -192,8 +192,11 @@ int DRC::TestZoneToZoneOutline( ZONE_CONTAINER* aZone, bool aCreateMarkers )
|
|||
|
||||
for( int ia = 0; ia < board->GetAreaCount(); ia++ )
|
||||
{
|
||||
ZONE_CONTAINER* zoneRef = board->GetArea( ia );
|
||||
zoneRef->BuildSmoothedPoly( smoothed_polys[ia] );
|
||||
ZONE_CONTAINER* zoneRef = board->GetArea( ia );
|
||||
std::set<VECTOR2I> colinearCorners;
|
||||
zoneRef->GetColinearCorners( board, colinearCorners );
|
||||
|
||||
zoneRef->BuildSmoothedPoly( smoothed_polys[ia], &colinearCorners );
|
||||
}
|
||||
|
||||
// iterate through all areas
|
||||
|
|
|
@ -665,6 +665,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_
|
|||
*/
|
||||
void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
|
||||
const SHAPE_POLY_SET& aSmoothedOutline,
|
||||
std::set<VECTOR2I>* aPreserveCorners,
|
||||
SHAPE_POLY_SET& aRawPolys,
|
||||
SHAPE_POLY_SET& aFinalPolys )
|
||||
{
|
||||
|
@ -744,7 +745,7 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
|
|||
|
||||
aRawPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
|
||||
// Prune features that don't meet minimum-width criteria
|
||||
aRawPolys.Deflate( half_min_width - epsilon, numSegs );
|
||||
aRawPolys.Deflate( half_min_width - epsilon, numSegs, true );
|
||||
|
||||
if( s_DumpZonesWhenFilling )
|
||||
dumper->Write( &aRawPolys, "solid-areas-before-hatching" );
|
||||
|
@ -759,13 +760,13 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
|
|||
// Re-inflate after pruning of areas that don't meet minimum-width criteria
|
||||
if( aZone->GetFilledPolysUseThickness() )
|
||||
{
|
||||
// if we're stroking the zone with a min-width stroke then this will naturally
|
||||
// inflate the zone
|
||||
// If we're stroking the zone with a min_width stroke then this will naturally
|
||||
// inflate the zone by half_min_width
|
||||
}
|
||||
else if( half_min_width - epsilon > epsilon ) // avoid very small outline thickness
|
||||
{
|
||||
aRawPolys.Simplify( SHAPE_POLY_SET::PM_FAST );
|
||||
aRawPolys.Inflate( half_min_width - epsilon, 16 );
|
||||
aRawPolys.Inflate( half_min_width - epsilon, numSegs, true );
|
||||
}
|
||||
|
||||
aRawPolys.Fracture( SHAPE_POLY_SET::PM_FAST );
|
||||
|
@ -789,18 +790,20 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aRawPol
|
|||
SHAPE_POLY_SET& aFinalPolys )
|
||||
{
|
||||
SHAPE_POLY_SET smoothedPoly;
|
||||
std::set<VECTOR2I> colinearCorners;
|
||||
aZone->GetColinearCorners( m_board, colinearCorners );
|
||||
|
||||
/*
|
||||
* convert outlines + holes to outlines without holes (adding extra segments if necessary)
|
||||
* m_Poly data is expected normalized, i.e. NormalizeAreaOutlines was used after building
|
||||
* this zone
|
||||
*/
|
||||
if ( !aZone->BuildSmoothedPoly( smoothedPoly ) )
|
||||
if ( !aZone->BuildSmoothedPoly( smoothedPoly, &colinearCorners ) )
|
||||
return false;
|
||||
|
||||
if( aZone->IsOnCopperLayer() )
|
||||
{
|
||||
computeRawFilledArea( aZone, smoothedPoly, aRawPolys, aFinalPolys );
|
||||
computeRawFilledArea( aZone, smoothedPoly, &colinearCorners, aRawPolys, aFinalPolys );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -67,7 +67,9 @@ private:
|
|||
* filled copper area polygon (without clearance areas
|
||||
* @param aPcb: the current board
|
||||
*/
|
||||
void computeRawFilledArea( const ZONE_CONTAINER* aZone, const SHAPE_POLY_SET& aSmoothedOutline,
|
||||
void computeRawFilledArea( const ZONE_CONTAINER* aZone,
|
||||
const SHAPE_POLY_SET& aSmoothedOutline,
|
||||
std::set<VECTOR2I>* aPreserveCorners,
|
||||
SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys );
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue