Remove a long-standing hack to keep divots out of adjacent zones.

The new algorithm unions any adjacent zones before doing the
chamfer/fillet and then subtracts the other zones back out afterwards.

Fixes https://gitlab.com/kicad/code/kicad/issues/3812
This commit is contained in:
Jeff Young 2020-08-12 19:42:40 +01:00
parent a6d44676b3
commit 463100d67f
12 changed files with 82 additions and 143 deletions

View File

@ -774,7 +774,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
auto layerContainer = m_layers_poly.find( layer ); auto layerContainer = m_layers_poly.find( layer );
if( layerContainer != m_layers_poly.end() ) if( layerContainer != m_layers_poly.end() )
zone->TransformSolidAreasShapesToPolygonSet( layer, *layerContainer->second ); zone->TransformSolidAreasShapesToPolygon( layer, *layerContainer->second );
} }
} }
} }
@ -1050,7 +1050,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
if( !zone->IsOnLayer( curr_layer_id ) ) if( !zone->IsOnLayer( curr_layer_id ) )
continue; continue;
zone->TransformSolidAreasShapesToPolygonSet( curr_layer_id, *layerPoly ); zone->TransformSolidAreasShapesToPolygon( curr_layer_id, *layerPoly );
} }
} }

View File

@ -1151,11 +1151,9 @@ class SHAPE_POLY_SET : public SHAPE
* returns a chamfered version of the aIndex-th polygon. * returns a chamfered version of the aIndex-th polygon.
* @param aDistance is the chamfering distance. * @param aDistance is the chamfering distance.
* @param aIndex is the index of the polygon to be chamfered. * @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. * @return POLYGON - A polygon containing the chamfered version of the aIndex-th polygon.
*/ */
POLYGON ChamferPolygon( unsigned int aDistance, int aIndex, POLYGON ChamferPolygon( unsigned int aDistance, int aIndex );
std::set<VECTOR2I>* aPreserveCorners );
/** /**
* Function Fillet * Function Fillet
@ -1163,32 +1161,26 @@ class SHAPE_POLY_SET : public SHAPE
* @param aRadius is the fillet radius. * @param aRadius is the fillet radius.
* @param aErrorMax is the maximum allowable deviation of the polygon from the circle * @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 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. * @return POLYGON - A polygon containing the filleted version of the aIndex-th polygon.
*/ */
POLYGON FilletPolygon( unsigned int aRadius, int aErrorMax, int aIndex, POLYGON FilletPolygon( unsigned int aRadius, int aErrorMax, int aIndex );
std::set<VECTOR2I>* aPreserveCorners = nullptr );
/** /**
* Function Chamfer * Function Chamfer
* returns a chamfered version of the polygon set. * returns a chamfered version of the polygon set.
* @param aDistance is the chamfering distance. * @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. * @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 * Function Fillet
* returns a filleted version of the polygon set. * returns a filleted version of the polygon set.
* @param aRadius is the fillet radius. * @param aRadius is the fillet radius.
* @param aErrorMax is the maximum allowable deviation of the polygon from the circle * @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. * @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 * Function DistanceToPolygon
@ -1306,12 +1298,10 @@ class SHAPE_POLY_SET : public SHAPE
* @param aIndex is the index of the polygon that will be chamfered/filleted. * @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 * @param aErrorMax is the maximum allowable deviation of the polygon from the circle
* if aMode = FILLETED. If aMode = CHAMFERED, it is unused. * 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. * @return POLYGON - the chamfered/filleted version of the polygon.
*/ */
POLYGON chamferFilletPolygon( CORNER_MODE aMode, unsigned int aDistance, 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. ///> Returns true if the polygon set has any holes that touch share a vertex.
bool hasTouchingHoles( const POLYGON& aPoly ) const; bool hasTouchingHoles( const POLYGON& aPoly ) const;

View File

@ -1556,18 +1556,16 @@ 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( CHAMFERED, aDistance, aIndex, 0, aPreserveCorners ); return chamferFilletPolygon( CHAMFERED, aDistance, aIndex, 0 );
} }
SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::FilletPolygon( unsigned int aRadius, int aErrorMax, SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::FilletPolygon( unsigned int aRadius, int aErrorMax,
int aIndex, int aIndex )
std::set<VECTOR2I>* aPreserveCorners )
{ {
return chamferFilletPolygon( FILLETED, aRadius, aIndex, aErrorMax, aPreserveCorners ); return chamferFilletPolygon( FILLETED, aRadius, aIndex, aErrorMax );
} }
@ -1676,32 +1674,30 @@ bool SHAPE_POLY_SET::IsVertexInHole( int aGlobalIdx )
} }
SHAPE_POLY_SET SHAPE_POLY_SET::Chamfer( int aDistance, std::set<VECTOR2I>* aPreserveCorners ) SHAPE_POLY_SET SHAPE_POLY_SET::Chamfer( int aDistance )
{ {
SHAPE_POLY_SET chamfered; SHAPE_POLY_SET chamfered;
for( unsigned int idx = 0; idx < m_polys.size(); idx++ ) for( unsigned int idx = 0; idx < m_polys.size(); idx++ )
chamfered.m_polys.push_back( ChamferPolygon( aDistance, idx, aPreserveCorners ) ); chamfered.m_polys.push_back( ChamferPolygon( aDistance, idx ) );
return chamfered; 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; SHAPE_POLY_SET filleted;
for( size_t idx = 0; idx < m_polys.size(); idx++ ) for( size_t idx = 0; idx < m_polys.size(); idx++ )
filleted.m_polys.push_back( FilletPolygon( aRadius, aErrorMax, idx, aPreserveCorners ) ); filleted.m_polys.push_back( FilletPolygon( aRadius, aErrorMax, idx ) );
return filleted; return filleted;
} }
SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode, 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: // Null segments create serious issues in calculations. Remove them:
RemoveNullSegments(); RemoveNullSegments();
@ -1728,12 +1724,6 @@ SHAPE_POLY_SET::POLYGON SHAPE_POLY_SET::chamferFilletPolygon( CORNER_MODE aMode,
int x1 = currContour.CPoint( currVertex ).x; int x1 = currContour.CPoint( currVertex ).x;
int y1 = currContour.CPoint( currVertex ).y; int y1 = currContour.CPoint( currVertex ).y;
if( aPreserveCorners && aPreserveCorners->count( VECTOR2I( x1, y1 ) ) > 0 )
{
newContour.Append( x1, y1 );
continue;
}
// Indices for previous and next vertices. // Indices for previous and next vertices.
int prevVertex; int prevVertex;
int nextVertex; int nextVertex;

View File

@ -91,7 +91,7 @@ void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer, SHAPE_POLY_
ZONE_CONTAINER* zone = GetArea( ii ); ZONE_CONTAINER* zone = GetArea( ii );
if( zone->GetLayerSet().test( aLayer ) ) if( zone->GetLayerSet().test( aLayer ) )
zone->TransformSolidAreasShapesToPolygonSet( aLayer, aOutlines ); zone->TransformSolidAreasShapesToPolygon( aLayer, aOutlines );
} }
// convert graphic items on copper layers (texts) // convert graphic items on copper layers (texts)
@ -244,9 +244,9 @@ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet( PCB_LAYER_ID aLaye
} }
void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet( PCB_LAYER_ID aLayer, void ZONE_CONTAINER::TransformSolidAreasShapesToPolygon( PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aCornerBuffer, SHAPE_POLY_SET& aCornerBuffer,
int aError ) const int aError ) const
{ {
if( !m_FilledPolysList.count( aLayer ) || m_FilledPolysList.at( aLayer ).IsEmpty() ) if( !m_FilledPolysList.count( aLayer ) || m_FilledPolysList.at( aLayer ).IsEmpty() )
return; return;

View File

@ -1164,65 +1164,55 @@ bool ZONE_CONTAINER::IsIsland( PCB_LAYER_ID aLayer, int aPolyIdx )
} }
/* void ZONE_CONTAINER::GetInteractingZones( PCB_LAYER_ID aLayer,
* Some intersecting zones, despite being on the same layer with the same net, cannot be std::vector<ZONE_CONTAINER*>* aZones ) const
* 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 ); int epsilon = Millimeter2iu( 0.001 );
// Things get messy when zone of different nets intersect. To do it right we'd need to for( ZONE_CONTAINER* candidate : GetBoard()->Zones() )
// run our colinear test with the final filled regions rather than the outline regions.
// However, since there's no order dependance the only way to do that is to iterate
// through successive zone fills until the results are no longer changing -- and that's
// not going to happen. So we punt and ignore any "messy" corners.
std::set<VECTOR2I> colinearCorners;
std::set<VECTOR2I> messyCorners;
for( ZONE_CONTAINER* candidate : aBoard->Zones() )
{ {
if( candidate == this ) if( candidate == this )
continue; continue;
if( candidate->GetLayerSet() != GetLayerSet() ) if( !candidate->GetLayerSet().test( aLayer ) )
continue; continue;
if( candidate->GetIsKeepout() != GetIsKeepout() ) if( candidate->GetIsKeepout() )
continue;
if( candidate->GetNetCode() != GetNetCode() )
continue; continue;
for( auto iter = m_Poly->CIterate(); iter; iter++ ) for( auto iter = m_Poly->CIterate(); iter; iter++ )
{ {
if( candidate->m_Poly->Collide( iter.Get(), epsilon ) ) if( candidate->m_Poly->Collide( iter.Get(), epsilon ) )
{ {
if( candidate->GetNetCode() == GetNetCode() ) aZones->push_back( candidate );
colinearCorners.insert( VECTOR2I( iter.Get() ) ); break;
else
messyCorners.insert( VECTOR2I( iter.Get() ) );
} }
} }
} }
for( VECTOR2I corner : colinearCorners )
{
if( messyCorners.count( corner ) == 0 )
aCorners.insert( corner );
}
} }
bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER_ID aLayer ) const
std::set<VECTOR2I>* aPreserveCorners ) const
{ {
if( GetNumCorners() <= 2 ) // malformed zone. polygon calculations do not like it ... if( GetNumCorners() <= 2 ) // malformed zone. polygon calculations do not like it ...
return false; return false;
std::vector<ZONE_CONTAINER*> interactingZones;
GetInteractingZones( aLayer, &interactingZones );
aSmoothedPoly = *m_Poly;
for( ZONE_CONTAINER* zone : interactingZones )
aSmoothedPoly.BooleanAdd( *zone->Outline(), SHAPE_POLY_SET::PM_FAST );
// Make a smoothed polygon out of the user-drawn polygon if required // Make a smoothed polygon out of the user-drawn polygon if required
switch( m_cornerSmoothingType ) switch( m_cornerSmoothingType )
{ {
case ZONE_SETTINGS::SMOOTHING_CHAMFER: case ZONE_SETTINGS::SMOOTHING_CHAMFER:
aSmoothedPoly = m_Poly->Chamfer( m_cornerRadius, aPreserveCorners ); aSmoothedPoly = aSmoothedPoly.Chamfer( m_cornerRadius );
break; break;
case ZONE_SETTINGS::SMOOTHING_FILLET: case ZONE_SETTINGS::SMOOTHING_FILLET:
@ -1233,7 +1223,7 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly,
if( board ) if( board )
maxError = board->GetDesignSettings().m_MaxError; maxError = board->GetDesignSettings().m_MaxError;
aSmoothedPoly = m_Poly->Fillet( m_cornerRadius, maxError, aPreserveCorners ); aSmoothedPoly = aSmoothedPoly.Fillet( m_cornerRadius, maxError );
break; break;
} }
default: default:
@ -1242,10 +1232,13 @@ bool ZONE_CONTAINER::BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly,
// We can avoid issues by creating a very small chamfer which remove acute angles, // 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 // or left it without chamfer and use only CPOLYGONS_LIST::InflateOutline to create
// clearance areas // clearance areas
aSmoothedPoly = m_Poly->Chamfer( Millimeter2iu( 0.0 ), aPreserveCorners ); aSmoothedPoly = aSmoothedPoly.Chamfer( Millimeter2iu( 0.0 ) );
break; break;
} }
if( interactingZones.size() )
aSmoothedPoly.BooleanIntersection( *m_Poly, SHAPE_POLY_SET::PM_FAST );
return true; return true;
}; };
@ -1283,11 +1276,11 @@ double ZONE_CONTAINER::CalculateFilledArea()
* @param aPreserveCorners an optional set of corners which should not be chamfered/filleted * @param aPreserveCorners an optional set of corners which should not be chamfered/filleted
*/ */
void ZONE_CONTAINER::TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, void ZONE_CONTAINER::TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
int aClearance, std::set<VECTOR2I>* aPreserveCorners ) const int aClearance ) const
{ {
// Creates the zone outline polygon (with holes if any) // Creates the zone outline polygon (with holes if any)
SHAPE_POLY_SET polybuffer; SHAPE_POLY_SET polybuffer;
BuildSmoothedPoly( polybuffer, aPreserveCorners ); BuildSmoothedPoly( polybuffer, GetLayer() );
// Calculate the polygon with clearance // Calculate the polygon with clearance
// holes are linked to the main outline, so only one polygon is created. // holes are linked to the main outline, so only one polygon is created.

View File

@ -318,16 +318,15 @@ public:
return HitTestCutout( VECTOR2I( aRefPos.x, aRefPos.y ), aOutlineIdx, aHoleIdx ); return HitTestCutout( VECTOR2I( aRefPos.x, aRefPos.y ), aOutlineIdx, aHoleIdx );
} }
/** /**
* Some intersecting zones, despite being on the same layer with the same net, cannot be * 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 * 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. * effectively merged though, so we need to do some calculations with them in mind.
*/ */
void GetColinearCorners( BOARD* aBoard, std::set<VECTOR2I>& colinearCorners ); void GetInteractingZones( PCB_LAYER_ID aLayer, std::vector<ZONE_CONTAINER*>* aZones ) const;
/** /**
* Function TransformSolidAreasShapesToPolygonSet * Function TransformSolidAreasShapesToPolygon
* Convert solid areas full shapes to polygon set * Convert solid areas full shapes to polygon set
* (the full shape is the polygon area with a thick outline) * (the full shape is the polygon area with a thick outline)
* Used in 3D view * Used in 3D view
@ -336,8 +335,8 @@ public:
* @param aCornerBuffer = a buffer to store the polygons * @param aCornerBuffer = a buffer to store the polygons
* @param aError = Maximum error allowed between true arc and polygon approx * @param aError = Maximum error allowed between true arc and polygon approx
*/ */
void TransformSolidAreasShapesToPolygonSet( PCB_LAYER_ID aLayer, void TransformSolidAreasShapesToPolygon( PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aCornerBuffer,
SHAPE_POLY_SET& aCornerBuffer, int aError = ARC_HIGH_DEF ) const; int aError = ARC_HIGH_DEF ) const;
/** /**
* Function TransformOutlinesShapeWithClearanceToPolygon * Function TransformOutlinesShapeWithClearanceToPolygon
@ -348,14 +347,9 @@ public:
* Circles (vias) and arcs (ends of tracks) are approximated by segments * Circles (vias) and arcs (ends of tracks) are approximated by segments
* @param aCornerBuffer = a buffer to store the polygon * @param aCornerBuffer = a buffer to store the polygon
* @param aMinClearanceValue = the min clearance around outlines * @param aMinClearanceValue = the min clearance around outlines
* @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, void TransformOutlinesShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
int aMinClearanceValue, std::set<VECTOR2I>* aPreserveCorners = nullptr ) const; int aMinClearanceValue ) const;
/** /**
* Function TransformShapeWithClearanceToPolygon * Function TransformShapeWithClearanceToPolygon
@ -370,8 +364,8 @@ public:
* for visualization * for visualization
*/ */
void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
int aClearanceValue, int aError = ARC_HIGH_DEF, int aClearanceValue, int aError = ARC_HIGH_DEF,
bool ignoreLineWidth = false ) const override; bool ignoreLineWidth = false ) const override;
/** /**
* Function HitTestForCorner * Function HitTestForCorner
@ -652,12 +646,8 @@ public:
/** /**
* Function GetSmoothedPoly * 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, bool BuildSmoothedPoly( SHAPE_POLY_SET& aSmoothedPoly, PCB_LAYER_ID aLayer ) const;
std::set<VECTOR2I>* aPreserveCorners ) const;
void SetCornerSmoothingType( int aType ) { m_cornerSmoothingType = aType; }; void SetCornerSmoothingType( int aType ) { m_cornerSmoothingType = aType; };

View File

@ -684,11 +684,8 @@ void DRC::testZones( BOARD_COMMIT& aCommit )
} }
} }
ZONE_CONTAINER* zoneRef = m_pcb->GetArea( ii ); ZONE_CONTAINER* zoneRef = m_pcb->GetArea( ii );
std::set<VECTOR2I> colinearCorners; zoneRef->BuildSmoothedPoly( smoothed_polys[ii], zoneRef->GetLayer() );
zoneRef->GetColinearCorners( m_pcb, colinearCorners );
zoneRef->BuildSmoothedPoly( smoothed_polys[ii], &colinearCorners );
} }
// iterate through all areas // iterate through all areas

View File

@ -868,18 +868,10 @@ void PlotSolderMaskLayer( BOARD *aBoard, PLOTTER* aPlotter, LSET aLayerMask,
if( zone->GetLayer() != layer ) if( zone->GetLayer() != layer )
continue; continue;
// Some intersecting zones, despite being on the same layer, cannot be
// merged due to other parameters such as fillet radius. The filled areas 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 );
// add shapes inflated by aMinThickness/2 in areas // add shapes inflated by aMinThickness/2 in areas
zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate + zone_margin, zone->TransformOutlinesShapeWithClearanceToPolygon( areas, inflate + zone_margin );
&colinearCorners );
// add shapes with their exact mask layer size in initialPolys // add shapes with their exact mask layer size in initialPolys
zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin, zone->TransformOutlinesShapeWithClearanceToPolygon( initialPolys, zone_margin );
&colinearCorners );
} }
int maxError = aBoard->GetDesignSettings().m_MaxError; int maxError = aBoard->GetDesignSettings().m_MaxError;

View File

@ -736,36 +736,30 @@ bool PNS_KICAD_IFACE_BASE::syncZone( PNS::NODE* aWorld, ZONE_CONTAINER* aZone )
if( !aZone->GetIsKeepout() || !aZone->GetDoNotAllowTracks() ) if( !aZone->GetIsKeepout() || !aZone->GetDoNotAllowTracks() )
return false; return 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;
aZone->GetColinearCorners( m_board, colinearCorners );
aZone->BuildSmoothedPoly( poly, &colinearCorners );
poly.CacheTriangulation();
if( !poly.IsTriangulationUpToDate() )
{
KIDIALOG dlg( nullptr, wxString::Format( _( "Malformed keep-out zone at (%d, %d)" ),
aZone->GetPosition().x, aZone->GetPosition().y ), KIDIALOG::KD_WARNING );
dlg.ShowDetailedText(
wxString::Format( _( "%s\nThis zone cannot be handled by the track layout tool.\n"
"Please verify it is not a self-intersecting polygon." ),
aZone->GetSelectMenuText( EDA_UNITS::MILLIMETRES ) ) );
dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
dlg.ShowModal();
return false;
}
LSET layers = aZone->GetLayerSet(); LSET layers = aZone->GetLayerSet();
for( int layer = F_Cu; layer <= B_Cu; layer++ ) for( int layer = F_Cu; layer <= B_Cu; layer++ )
{ {
if ( ! layers[layer] ) if( ! layers[ layer ] )
continue; continue;
aZone->BuildSmoothedPoly( poly, ToLAYER_ID( layer ) );
poly.CacheTriangulation();
if( !poly.IsTriangulationUpToDate() )
{
KIDIALOG dlg( nullptr, wxString::Format( _( "Malformed keep-out zone at (%d, %d)" ),
aZone->GetPosition().x, aZone->GetPosition().y ), KIDIALOG::KD_WARNING );
dlg.ShowDetailedText(
wxString::Format( _( "%s\nThis zone cannot be handled by the track layout tool.\n"
"Please verify it is not a self-intersecting polygon." ),
aZone->GetSelectMenuText( EDA_UNITS::MILLIMETRES ) ) );
dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
dlg.ShowModal();
return false;
}
for( int outline = 0; outline < poly.OutlineCount(); outline++ ) for( int outline = 0; outline < poly.OutlineCount(); outline++ )
{ {
auto tri = poly.TriangulatedPolygon( outline ); auto tri = poly.TriangulatedPolygon( outline );

View File

@ -741,7 +741,6 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA
*/ */
void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer, void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
const SHAPE_POLY_SET& aSmoothedOutline, const SHAPE_POLY_SET& aSmoothedOutline,
std::set<VECTOR2I>* aPreserveCorners,
SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aRawPolys,
SHAPE_POLY_SET& aFinalPolys ) SHAPE_POLY_SET& aFinalPolys )
{ {
@ -928,15 +927,13 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys ) SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys )
{ {
SHAPE_POLY_SET smoothedPoly; 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) * 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 * m_Poly data is expected normalized, i.e. NormalizeAreaOutlines was used after building
* this zone * this zone
*/ */
if ( !aZone->BuildSmoothedPoly( smoothedPoly, &colinearCorners ) ) if ( !aZone->BuildSmoothedPoly( smoothedPoly, aLayer ) )
return false; return false;
if( m_progressReporter && m_progressReporter->IsCancelled() ) if( m_progressReporter && m_progressReporter->IsCancelled() )
@ -944,8 +941,7 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
if( aZone->IsOnCopperLayer() ) if( aZone->IsOnCopperLayer() )
{ {
computeRawFilledArea( aZone, aLayer, smoothedPoly, &colinearCorners, aRawPolys, computeRawFilledArea( aZone, aLayer, smoothedPoly, aRawPolys, aFinalPolys );
aFinalPolys );
} }
else else
{ {

View File

@ -70,7 +70,6 @@ private:
*/ */
void computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer, void computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
const SHAPE_POLY_SET& aSmoothedOutline, const SHAPE_POLY_SET& aSmoothedOutline,
std::set<VECTOR2I>* aPreserveCorners,
SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys ); SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys );
/** /**

View File

@ -669,11 +669,9 @@ void test::DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZones()
for( int ii = 0; ii < m_board->GetAreaCount(); ii++ ) for( int ii = 0; ii < m_board->GetAreaCount(); ii++ )
{ {
ZONE_CONTAINER* zone = m_board->GetArea( ii ); ZONE_CONTAINER* zone = m_board->GetArea( ii );
ZONE_CONTAINER* zoneRef = m_board->GetArea( ii ); ZONE_CONTAINER* zoneRef = m_board->GetArea( ii );
std::set<VECTOR2I> colinearCorners;
zoneRef->GetColinearCorners( m_board, colinearCorners ); zoneRef->BuildSmoothedPoly( smoothed_polys[ii], zoneRef->GetLayer() );
zoneRef->BuildSmoothedPoly( smoothed_polys[ii], &colinearCorners );
} }
// iterate through all areas // iterate through all areas