CADSTAR PCB: Fix Zone fill priorities
The algorithm now correctly determines fill priorities when the zone outlines are close together and within the minimum clearance between zones.
This commit is contained in:
parent
a1c1f60665
commit
e867a4fd27
|
@ -103,6 +103,9 @@ void CADSTAR_PCB_ARCHIVE_LOADER::Load( BOARD* aBoard, PROJECT* aProject )
|
||||||
// operations to join them together into a single polygon.
|
// operations to join them together into a single polygon.
|
||||||
long numSteps = Layout.Coppers.size();
|
long numSteps = Layout.Coppers.size();
|
||||||
|
|
||||||
|
// A large amount is also spent calculating zone priorities
|
||||||
|
numSteps += ( Layout.Templates.size() * Layout.Templates.size() ) / 2;
|
||||||
|
|
||||||
m_progressReporter->SetMaxProgress( numSteps );
|
m_progressReporter->SetMaxProgress( numSteps );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +123,19 @@ void CADSTAR_PCB_ARCHIVE_LOADER::Load( BOARD* aBoard, PROJECT* aProject )
|
||||||
loadDocumentationSymbols();
|
loadDocumentationSymbols();
|
||||||
loadTemplates();
|
loadTemplates();
|
||||||
loadCoppers(); // Progress reporting is here as significantly most amount of time spent
|
loadCoppers(); // Progress reporting is here as significantly most amount of time spent
|
||||||
calculateZonePriorities();
|
|
||||||
|
for( PCB_LAYER_ID id : LSET::AllCuMask( m_numCopperLayers ).Seq() )
|
||||||
|
{
|
||||||
|
if( !calculateZonePriorities( id ) )
|
||||||
|
{
|
||||||
|
wxLogError( wxString::Format( _( "Unable to determine zone fill priorities for layer "
|
||||||
|
"'%s'. A best attempt has been made but it is "
|
||||||
|
"possible that DRC errors exist and that manual "
|
||||||
|
"editing of the zone priorities is required." ),
|
||||||
|
m_board->GetLayerName( id ) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loadNets();
|
loadNets();
|
||||||
loadTextVariables();
|
loadTextVariables();
|
||||||
|
|
||||||
|
@ -3616,83 +3631,141 @@ void CADSTAR_PCB_ARCHIVE_LOADER::applyDimensionSettings( const DIMENSION& aCads
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CADSTAR_PCB_ARCHIVE_LOADER::calculateZonePriorities( PCB_LAYER_ID& aLayer )
|
||||||
void CADSTAR_PCB_ARCHIVE_LOADER::calculateZonePriorities()
|
|
||||||
{
|
{
|
||||||
std::map<TEMPLATE_ID, std::set<TEMPLATE_ID>> winningOverlaps;
|
std::map<TEMPLATE_ID, std::set<TEMPLATE_ID>> winningOverlaps;
|
||||||
std::set<std::pair<TEMPLATE_ID, TEMPLATE_ID>> scheduleInferPriorityFromOutline;
|
|
||||||
|
|
||||||
// Calculate the intersection between aPolygon and the outline of aZone
|
auto inflateValue =
|
||||||
auto intersectionArea = [&]( const SHAPE_POLY_SET& aPolygon, ZONE* aZone ) -> double
|
[&]( ZONE* aZoneA, ZONE* aZoneB )
|
||||||
{
|
{
|
||||||
SHAPE_POLY_SET intersectShape( *aZone->Outline() );
|
int extra = getKiCadLength( Assignments.Codedefs.SpacingCodes.at( "C_C" ).Spacing )
|
||||||
|
- m_board->GetDesignSettings().m_MinClearance;
|
||||||
|
|
||||||
intersectShape.BooleanIntersection( aPolygon,
|
int retval = std::max( aZoneA->GetLocalClearance(), aZoneB->GetLocalClearance() );
|
||||||
SHAPE_POLY_SET::PM_FAST );
|
|
||||||
return intersectShape.Area();
|
retval += extra;
|
||||||
};
|
|
||||||
|
return retval;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find the error in fill area when guessing that aHigherZone gets filled before aLowerZone
|
||||||
|
auto errorArea =
|
||||||
|
[&]( ZONE* aLowerZone, ZONE* aHigherZone ) -> double
|
||||||
|
{
|
||||||
|
SHAPE_POLY_SET intersectShape( *aHigherZone->Outline() );
|
||||||
|
intersectShape.Inflate( inflateValue( aLowerZone, aHigherZone ) , 32 );
|
||||||
|
|
||||||
|
SHAPE_POLY_SET lowerZoneFill( aLowerZone->GetFilledPolysList( aLayer ) );
|
||||||
|
|
||||||
|
SHAPE_POLY_SET lowerZoneOutline( *aLowerZone->Outline() );
|
||||||
|
|
||||||
|
lowerZoneOutline.BooleanSubtract( intersectShape,
|
||||||
|
SHAPE_POLY_SET::PM_FAST );
|
||||||
|
|
||||||
|
lowerZoneFill.BooleanSubtract( lowerZoneOutline,
|
||||||
|
SHAPE_POLY_SET::PM_FAST );
|
||||||
|
|
||||||
|
double leftOverArea = lowerZoneFill.Area();
|
||||||
|
|
||||||
|
return leftOverArea;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto intersectionAreaOfZoneOutlines =
|
||||||
|
[&]( ZONE* aZoneA, ZONE* aZoneB ) -> double
|
||||||
|
{
|
||||||
|
SHAPE_POLY_SET outLineA( *aZoneA->Outline() );
|
||||||
|
outLineA.Inflate( inflateValue( aZoneA, aZoneB ), 32 );
|
||||||
|
|
||||||
|
SHAPE_POLY_SET outLineB( *aZoneA->Outline() );
|
||||||
|
outLineB.Inflate( inflateValue( aZoneA, aZoneB ), 32 );
|
||||||
|
|
||||||
|
outLineA.BooleanIntersection( outLineB, SHAPE_POLY_SET::PM_FAST );
|
||||||
|
|
||||||
|
return outLineA.Area();
|
||||||
|
};
|
||||||
|
|
||||||
// Lambda to determine if the zone with template ID 'a' is lower priority than 'b'
|
// Lambda to determine if the zone with template ID 'a' is lower priority than 'b'
|
||||||
auto isLowerPriority = [&]( const TEMPLATE_ID& a, const TEMPLATE_ID& b ) -> bool
|
auto isLowerPriority =
|
||||||
{
|
[&]( const TEMPLATE_ID& a, const TEMPLATE_ID& b ) -> bool
|
||||||
return winningOverlaps[b].count( a ) > 0;
|
{
|
||||||
};
|
return winningOverlaps[b].count( a ) > 0;
|
||||||
|
};
|
||||||
|
|
||||||
for( std::map<TEMPLATE_ID, ZONE*>::iterator it1 = m_zonesMap.begin();
|
for( std::map<TEMPLATE_ID, ZONE*>::iterator it1 = m_zonesMap.begin();
|
||||||
it1 != m_zonesMap.end(); ++it1 )
|
it1 != m_zonesMap.end(); ++it1 )
|
||||||
{
|
{
|
||||||
TEMPLATE thisTemplate = Layout.Templates.at( it1->first );
|
TEMPLATE& thisTemplate = Layout.Templates.at( it1->first );
|
||||||
PCB_LAYER_ID thisLayer = getKiCadLayer( thisTemplate.LayerID );
|
ZONE* thisZone = it1->second;
|
||||||
ZONE* thisZone = it1->second;
|
|
||||||
|
if( !thisZone->GetLayerSet().Contains( aLayer ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
for( std::map<TEMPLATE_ID, ZONE*>::iterator it2 = it1;
|
for( std::map<TEMPLATE_ID, ZONE*>::iterator it2 = it1;
|
||||||
it2 != m_zonesMap.end(); ++it2 )
|
it2 != m_zonesMap.end(); ++it2 )
|
||||||
{
|
{
|
||||||
TEMPLATE otherTemplate = Layout.Templates.at( it2->first );
|
TEMPLATE& otherTemplate = Layout.Templates.at( it2->first );
|
||||||
PCB_LAYER_ID otherLayer = getKiCadLayer( otherTemplate.LayerID );
|
ZONE* otherZone = it2->second;
|
||||||
ZONE* otherZone = it2->second;
|
|
||||||
|
|
||||||
if( thisTemplate.ID == otherTemplate.ID )
|
if( thisTemplate.ID == otherTemplate.ID )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( thisLayer != otherLayer )
|
if( !otherZone->GetLayerSet().Contains( aLayer ) )
|
||||||
|
{
|
||||||
|
checkPoint();
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
SHAPE_POLY_SET thisZonePolyFill = thisZone->GetFilledPolysList( thisLayer );
|
if( intersectionAreaOfZoneOutlines( thisZone, otherZone ) == 0 )
|
||||||
SHAPE_POLY_SET otherZonePolyFill = otherZone->GetFilledPolysList( otherLayer );
|
{
|
||||||
|
checkPoint();
|
||||||
|
continue; // The zones do not interact in any way
|
||||||
|
}
|
||||||
|
|
||||||
|
SHAPE_POLY_SET thisZonePolyFill = thisZone->GetFilledPolysList( aLayer );
|
||||||
|
SHAPE_POLY_SET otherZonePolyFill = otherZone->GetFilledPolysList( aLayer );
|
||||||
|
|
||||||
if( thisZonePolyFill.Area() > 0.0 && otherZonePolyFill.Area() > 0.0 )
|
if( thisZonePolyFill.Area() > 0.0 && otherZonePolyFill.Area() > 0.0 )
|
||||||
{
|
{
|
||||||
// Intersect the filled polygons of thisZone with the *outline* of otherZone
|
// Test if this zone were lower priority than other zone, what is the error?
|
||||||
double areaThis = intersectionArea( thisZonePolyFill, otherZone );
|
double areaThis = errorArea( thisZone, otherZone );
|
||||||
// Viceversa
|
// Viceversa
|
||||||
double areaOther = intersectionArea( otherZonePolyFill, thisZone );
|
double areaOther = errorArea( otherZone, thisZone );
|
||||||
|
|
||||||
// Best effort: Compare Areas
|
|
||||||
// If thisZone's fill polygons overlap otherZone's outline *and* the opposite
|
|
||||||
// is true: otherZone's fill polygons overlap thisZone's outline then compare the
|
|
||||||
// intersection areas to decide which of the two zones should have higher priority
|
|
||||||
// There are some edge cases where this might not work, but it is in the minority.
|
|
||||||
if( areaThis > areaOther )
|
if( areaThis > areaOther )
|
||||||
{
|
{
|
||||||
|
// thisTemplate is filled before otherTemplate
|
||||||
winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
|
winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
|
||||||
}
|
}
|
||||||
else if( areaOther > 0.0 )
|
|
||||||
{
|
|
||||||
winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
scheduleInferPriorityFromOutline.insert(
|
// thisTemplate is filled AFTER otherTemplate
|
||||||
{ thisTemplate.ID, otherTemplate.ID } );
|
winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if( thisZonePolyFill.Area() > 0.0 )
|
||||||
|
{
|
||||||
|
// The other template is not filled, this one wins
|
||||||
|
winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
|
||||||
|
}
|
||||||
|
else if( otherZonePolyFill.Area() > 0.0 )
|
||||||
|
{
|
||||||
|
// This template is not filled, the other one wins
|
||||||
|
winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// One of the templates is not poured in the original CADSTAR design.
|
// Neither of the templates is poured - use zone outlines instead (bigger outlines
|
||||||
// Lets infer the priority based of the outlines instead
|
// get a lower priority)
|
||||||
scheduleInferPriorityFromOutline.insert( { thisTemplate.ID, otherTemplate.ID } );
|
if( intersectionAreaOfZoneOutlines( thisZone, otherZone ) != 0 )
|
||||||
|
{
|
||||||
|
if( thisZone->Outline()->Area() > otherZone->Outline()->Area() )
|
||||||
|
winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
|
||||||
|
else
|
||||||
|
winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkPoint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3738,6 +3811,22 @@ void CADSTAR_PCB_ARCHIVE_LOADER::calculateZonePriorities()
|
||||||
prevID = id;
|
prevID = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
for( const std::pair<TEMPLATE_ID, std::set<TEMPLATE_ID>>& idPair : winningOverlaps )
|
||||||
|
{
|
||||||
|
const TEMPLATE_ID& winningID = idPair.first;
|
||||||
|
|
||||||
|
for( const TEMPLATE_ID& losingID : idPair.second )
|
||||||
|
{
|
||||||
|
if( m_zonesMap.at( losingID )->GetPriority()
|
||||||
|
> m_zonesMap.at( winningID )->GetPriority() )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* @brief Tries to make a best guess as to the zone priorities based on the pour status.
|
* @brief Tries to make a best guess as to the zone priorities based on the pour status.
|
||||||
*/
|
*/
|
||||||
void calculateZonePriorities();
|
bool calculateZonePriorities( PCB_LAYER_ID& aLayer );
|
||||||
|
|
||||||
//Helper functions for drawing /loading objects onto screen:
|
//Helper functions for drawing /loading objects onto screen:
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue