CADSTAR PCB Archive Importer: More improvements to loading of zones

Adds a new function Area() to SHAPE_POLY_SET
This commit is contained in:
Roberto Fernandez Bautista 2021-02-08 10:40:32 +00:00 committed by Wayne Stambaugh
parent 7c06bdbd2e
commit 2d66abdc89
3 changed files with 182 additions and 56 deletions

View File

@ -547,6 +547,9 @@ public:
///< Adds a new hole to the given outline (default: last) and returns its index
int AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline = -1 );
///< Return the area of this poly set
double Area();
///< Appends a vertex at the end of the given outline/hole (default: the last outline)
/**
* Add a new vertex to the contour indexed by \p aOutline and \p aHole (defaults to the

View File

@ -455,6 +455,22 @@ int SHAPE_POLY_SET::AddHole( const SHAPE_LINE_CHAIN& aHole, int aOutline )
}
double SHAPE_POLY_SET::Area()
{
double area = 0.0;
for( int i = 0; i < OutlineCount(); i++ )
{
area += Outline( i ).Area();
for( int j = 0; j < HoleCount( i ); j++ )
area -= Hole( i, j ).Area();
}
return area;
}
void SHAPE_POLY_SET::booleanOp( ClipperLib::ClipType aType, const SHAPE_POLY_SET& aOtherShape,
POLYGON_MODE aFastMode )
{

View File

@ -1573,50 +1573,87 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadTemplates()
zone->SetNet( getKiCadNet( csTemplate.NetID ) );
if( csTemplate.Pouring.AllowInNoRouting )
wxLogError( wxString::Format(
{
wxLogWarning( wxString::Format(
_( "The CADSTAR template '%s' has the setting 'Allow in No Routing Areas' "
"enabled. This setting has no KiCad equivalent, so it has been ignored." ),
csTemplate.Name ) );
}
if( csTemplate.Pouring.BoxIsolatedPins )
wxLogError( wxString::Format(
{
wxLogWarning( wxString::Format(
_( "The CADSTAR template '%s' has the setting 'Box Isolated Pins' "
"enabled. This setting has no KiCad equivalent, so it has been ignored." ),
csTemplate.Name ) );
}
if( csTemplate.Pouring.AutomaticRepour )
{
wxLogWarning( wxString::Format(
_( "The CADSTAR template '%s' has the setting 'Automatic Repour' "
"enabled. This setting has no KiCad equivalent, so it has been ignored." ),
csTemplate.Name ) );
}
// Sliver width has different behaviour to KiCad Zone's minimum thickness
// In Cadstar 'Sliver width' has to be greater than the Copper thickness, whereas in
// Kicad it is the opposite.
if( csTemplate.Pouring.SliverWidth != 0 )
wxLogError( wxString::Format(
{
wxLogWarning( wxString::Format(
_( "The CADSTAR template '%s' has a non-zero value defined for the "
"'Sliver Width' setting. There is no KiCad equivalent for "
"this, so this setting was ignored." ),
csTemplate.Name ) );
}
if( csTemplate.Pouring.MinIsolatedCopper != csTemplate.Pouring.MinDisjointCopper )
wxLogError( wxString::Format(
{
wxLogWarning( wxString::Format(
_( "The CADSTAR template '%s' has different settings for 'Retain Poured Copper "
"- Disjoint' and 'Retain Poured Copper - Isolated'. KiCad does not "
"distinguish between these two settings. The setting for disjoint copper "
"has been applied as the minimum island area of the KiCad Zone." ),
csTemplate.Name ) );
}
if( csTemplate.Pouring.MinDisjointCopper < 0 )
zone->SetMinIslandArea( -1 );
long long minIslandArea = -1;
if( csTemplate.Pouring.MinDisjointCopper != UNDEFINED_VALUE )
{
minIslandArea = (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper )
* (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper );
zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::AREA );
}
else
zone->SetMinIslandArea(
(long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper )
* (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper ) );
{
zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::ALWAYS );
}
zone->SetLocalClearance( getKiCadLength( csTemplate.Pouring.AdditionalIsolation ) );
zone->SetMinIslandArea( minIslandArea );
// In cadstar zone clearance is in addition to the design rule "copper to copper"
int clearance = getKiCadLength( csTemplate.Pouring.AdditionalIsolation );
if( Assignments.Codedefs.SpacingCodes.find( wxT( "C_C" ) )
!= Assignments.Codedefs.SpacingCodes.end() )
{
int copperToCopper = Assignments.Codedefs.SpacingCodes.at( wxT( "C_C" ) ).Spacing;
clearance += getKiCadLength( copperToCopper );
}
else
{
clearance += mBoard->GetDesignSettings().m_MinClearance;
}
zone->SetLocalClearance( clearance );
COPPERCODE pouringCopperCode = getCopperCode( csTemplate.Pouring.CopperCodeID );
int minThickness = getKiCadLength( pouringCopperCode.CopperWidth );
zone->SetMinThickness( minThickness );
if( csTemplate.Pouring.FillType == TEMPLATE::POURING::COPPER_FILL_TYPE::HATCHED )
{
@ -1631,23 +1668,42 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadTemplates()
}
if( csTemplate.Pouring.ThermalReliefOnPads != csTemplate.Pouring.ThermalReliefOnVias
|| csTemplate.Pouring.ThermalReliefPadsAngle
!= csTemplate.Pouring.ThermalReliefViasAngle )
|| csTemplate.Pouring.ThermalReliefPadsAngle
!= csTemplate.Pouring.ThermalReliefViasAngle )
{
wxLogWarning( wxString::Format(
_( "The CADSTAR template '%s' has different settings for thermal relief "
"in pads and vias. KiCad only supports one single setting for both. The "
"setting for pads has been applied." ),
csTemplate.Name ) );
}
if( csTemplate.Pouring.ThermalReliefOnPads )
COPPERCODE reliefCopperCode = getCopperCode( csTemplate.Pouring.ReliefCopperCodeID );
int spokeWidth = getKiCadLength( reliefCopperCode.CopperWidth );
int reliefWidth = getKiCadLength( csTemplate.Pouring.ClearanceWidth );
// Cadstar supports having a spoke width thinner than the minimum thickness of the zone, but
// this is not permitted in KiCad. We load it as solid fill instead.
if( csTemplate.Pouring.ThermalReliefOnPads && reliefWidth > 0 && spokeWidth > minThickness )
{
zone->SetThermalReliefGap( getKiCadLength( csTemplate.Pouring.ClearanceWidth ) );
zone->SetThermalReliefSpokeWidth( getKiCadLength(
getCopperCode( csTemplate.Pouring.ReliefCopperCodeID ).CopperWidth ) );
zone->SetThermalReliefGap( reliefWidth );
zone->SetThermalReliefSpokeWidth( spokeWidth );
zone->SetPadConnection( ZONE_CONNECTION::THERMAL );
}
else
{
if( csTemplate.Pouring.ThermalReliefOnPads && spokeWidth > minThickness )
{
wxLogWarning( wxString::Format(
_( "The CADSTAR template '%s' has thermal reliefs in the original design "
"but there is no KiCad equivalent to the original CADSTAR settings. "
"Solid fill has been applied instead. When the template is re-filled "
"the thermal reliefs will be removed." ),
csTemplate.Name ) );
}
zone->SetPadConnection( ZONE_CONNECTION::FULL );
}
mLoadedTemplates.insert( { csTemplate.ID, zone } );
}
@ -1715,25 +1771,56 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadCoppers()
if( !csCopper.PouredTemplateID.IsEmpty() )
{
ZONE* pouredZone = mLoadedTemplates.at( csCopper.PouredTemplateID );
SHAPE_POLY_SET rawPolys = getPolySetFromCadstarShape( csCopper.Shape, -1 );
SHAPE_POLY_SET rawPolys;
int copperWidth = getKiCadLength( getCopperCode( csCopper.CopperCodeID ).CopperWidth );
int zoneWidth = pouredZone->GetMinThickness();
if( zoneWidth > copperWidth )
rawPolys.Deflate( ( zoneWidth - copperWidth ) / 2, 32 );
if( csCopper.Shape.Type == SHAPE_TYPE::OPENSHAPE )
{
// This is usually for themal reliefs. They are lines of copper with a thickness.
// We convert them to an oval in most cases, but handle also the possibility of
// encountering arcs in here.
std::vector<PCB_SHAPE*> outlineSegments =
getDrawSegmentsFromVertices( csCopper.Shape.Vertices );
for( auto& seg : outlineSegments )
{
SHAPE_POLY_SET segment;
if( seg->GetShape() == PCB_SHAPE_TYPE_T::S_ARC )
{
TransformArcToPolygon( segment, seg->GetStart(), seg->GetArcMid(),
seg->GetEnd(), copperWidth, ARC_HIGH_DEF,
ERROR_LOC::ERROR_INSIDE );
}
else
{
TransformOvalToPolygon( segment, seg->GetStart(), seg->GetEnd(),
copperWidth, ARC_HIGH_DEF, ERROR_LOC::ERROR_INSIDE );
}
rawPolys.BooleanAdd( segment, SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
}
}
else
rawPolys.Inflate( ( copperWidth - zoneWidth ) / 2, 32 );
{
rawPolys = getPolySetFromCadstarShape( csCopper.Shape, -1 );
rawPolys.Inflate( copperWidth / 2, 32 );
}
if( pouredZone->HasFilledPolysForLayer( getKiCadLayer( csCopper.LayerID ) ) )
{
rawPolys.BooleanAdd( pouredZone->RawPolysList( getKiCadLayer( csCopper.LayerID )),
SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
}
SHAPE_POLY_SET finalPolys = rawPolys;
finalPolys.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
pouredZone->SetFillVersion( 6 );
pouredZone->SetRawPolysList( getKiCadLayer( csCopper.LayerID ), rawPolys );
pouredZone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), finalPolys );
pouredZone->SetIsFilled( true );
@ -1793,6 +1880,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadCoppers()
zone->SetZoneName( csCopper.ID );
zone->SetLayer( getKiCadLayer( csCopper.LayerID ) );
zone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::NO_HATCH );
if( csCopper.Shape.Type == SHAPE_TYPE::HATCHED )
{
@ -1806,6 +1894,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadCoppers()
zone->SetFillMode( ZONE_FILL_MODE::POLYGONS );
}
zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
zone->SetPadConnection( ZONE_CONNECTION::FULL );
zone->SetNet( getKiCadNet( csCopper.NetRef.NetID ) );
zone->SetPriority( mLoadedTemplates.size() + 1 ); // Highest priority (always fill first)
@ -1814,6 +1903,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadCoppers()
SHAPE_POLY_SET fillePolys( *zone->Outline() );
fillePolys.Fracture( SHAPE_POLY_SET::POLYGON_MODE::PM_STRICTLY_SIMPLE );
zone->SetFillVersion( 6 );
zone->SetFilledPolysList( getKiCadLayer( csCopper.LayerID ), fillePolys );
}
}
@ -2571,10 +2661,17 @@ SHAPE_LINE_CHAIN CADSTAR_PCB_ARCHIVE_LOADER::getLineChainFromDrawsegments( const
}
}
lineChain.SetClosed( true ); //todo check if it is closed
// Shouldn't have less than 3 points to make a closed shape!
wxASSERT( lineChain.PointCount() > 2 );
// Check if it is closed
if( lineChain.GetPoint( 0 ) != lineChain.GetPoint( lineChain.PointCount() - 1 ) )
{
lineChain.Append( lineChain.GetPoint( 0 ) );
}
lineChain.SetClosed( true );
return lineChain;
}
@ -3094,25 +3191,22 @@ void CADSTAR_PCB_ARCHIVE_LOADER::applyDimensionSettings( const DIMENSION& aCads
void CADSTAR_PCB_ARCHIVE_LOADER::calculateZonePriorities()
{
std::map<TEMPLATE_ID, std::set<TEMPLATE_ID>> winningOverlaps;
std::set<std::pair<TEMPLATE_ID, TEMPLATE_ID>> scheduleInferPriorityFromOutline;
// Calculate the intesection between aPolygon and the outline of aZone
auto intersectionArea = []( SHAPE_POLY_SET aPolygon, ZONE* aZone ) -> double
auto intersectionArea = [&]( SHAPE_POLY_SET aPolygon, ZONE* aZone ) -> double
{
SHAPE_POLY_SET intersectShape( *aZone->Outline() );
intersectShape.BooleanIntersection( aPolygon,
SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
double intersectionArea = 0.0;
SHAPE_POLY_SET::PM_FAST );
return intersectShape.Area();
};
for( int i = 0; i < intersectShape.OutlineCount(); i++ )
{
intersectionArea += intersectShape.Outline( i ).Area();
for( int j = 0; j < intersectShape.HoleCount( i ); j++ )
intersectionArea -= intersectShape.Hole( i, j ).Area();
}
return intersectionArea;
// 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
{
return winningOverlaps[b].count( a ) > 0;
};
for( std::map<TEMPLATE_ID, ZONE*>::iterator it1 = mLoadedTemplates.begin();
@ -3135,22 +3229,41 @@ void CADSTAR_PCB_ARCHIVE_LOADER::calculateZonePriorities()
if( thisLayer != otherLayer )
continue;
// Intersect the filled polygons of thisZone with the *outline* of otherZone
SHAPE_POLY_SET thisZonePolyFill = thisZone->GetFilledPolysList( thisLayer );
double areaThis = intersectionArea( thisZonePolyFill, otherZone );
// Viceversa
SHAPE_POLY_SET otherZonePolyFill = otherZone->GetFilledPolysList( otherLayer );
double areaOther = intersectionArea( otherZonePolyFill, 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
if( areaThis > areaOther )
winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
else if( areaOther > 0 )
winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
if( thisZonePolyFill.Area() > 0.0 && otherZonePolyFill.Area() > 0.0 )
{
// Intersect the filled polygons of thisZone with the *outline* of otherZone
double areaThis = intersectionArea( thisZonePolyFill, otherZone );
// Viceversa
double areaOther = intersectionArea( otherZonePolyFill, 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 )
{
winningOverlaps[thisTemplate.ID].insert( otherTemplate.ID );
}
else if( areaOther > 0.0 )
{
winningOverlaps[otherTemplate.ID].insert( thisTemplate.ID );
}
else
{
scheduleInferPriorityFromOutline.insert(
{ thisTemplate.ID, otherTemplate.ID } );
}
}
else
{
// One of the templates is not poured in the original CADSTAR design.
// Lets infer the priority based of the outlines instead
scheduleInferPriorityFromOutline.insert( { thisTemplate.ID, otherTemplate.ID } );
}
}
}
@ -3171,12 +3284,6 @@ void CADSTAR_PCB_ARCHIVE_LOADER::calculateZonePriorities()
sortedIDs.push_back( id );
}
// 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
{
return winningOverlaps[b].count( a ) > 0;
};
// sort by priority
std::sort( sortedIDs.begin(), sortedIDs.end(), isLowerPriority );