Eagle PCB import: handle keepout zones

Fixes: lp:1755886
* https://bugs.launchpad.net/kicad/+bug/1755886
This commit is contained in:
Maciej Suminski 2018-05-18 18:14:54 +02:00
parent 2bd0a027a2
commit dcf60f8e6f
2 changed files with 149 additions and 120 deletions

View File

@ -706,8 +706,9 @@ void EAGLE_PLUGIN::loadPlain( wxXmlNode* aGraphics )
}
else if( grName == "polygon" )
{
// could be on a copper layer, could be on another layer.
// copper layer would be done using netCode=0 type of ZONE_CONTAINER.
m_xpath->push( "polygon" );
loadPolygon( gr );
m_xpath->pop(); // "polygon"
}
else if( grName == "dimension" )
{
@ -1074,6 +1075,139 @@ void EAGLE_PLUGIN::loadElements( wxXmlNode* aElements )
}
ZONE_CONTAINER* EAGLE_PLUGIN::loadPolygon( wxXmlNode* aPolyNode )
{
EPOLYGON p( aPolyNode );
PCB_LAYER_ID layer = kicad_layer( p.layer );
ZONE_CONTAINER* zone = nullptr;
// Handle copper and keepout layers
if( IsCopperLayer( layer )
|| p.layer == EAGLE_LAYER::TRESTRICT || p.layer == EAGLE_LAYER::BRESTRICT )
{
// use a "netcode = 0" type ZONE:
zone = new ZONE_CONTAINER( m_board );
zone->SetTimeStamp( EagleTimeStamp( aPolyNode ) );
m_board->Add( zone, ADD_APPEND );
if( p.layer == EAGLE_LAYER::TRESTRICT )
{
zone->SetIsKeepout( true );
zone->SetLayer( F_Cu );
}
else if( p.layer == EAGLE_LAYER::BRESTRICT )
{
zone->SetIsKeepout( true );
zone->SetLayer( B_Cu );
}
else
{
zone->SetLayer( layer );
}
// Get the first vertex and iterate
wxXmlNode* vertex = aPolyNode->GetChildren();
std::vector<EVERTEX> vertices;
// Create a circular vector of vertices
// The "curve" parameter indicates a curve from the current
// to the next vertex, so we keep the first at the end as well
// to allow the curve to link back
while( vertex )
{
if( vertex->GetName() == "vertex" )
vertices.push_back( EVERTEX( vertex ) );
vertex = vertex->GetNext();
}
vertices.push_back( vertices[0] );
for( size_t i = 0; i < vertices.size() - 1; i++ )
{
EVERTEX v1 = vertices[i];
// Append the corner
zone->AppendCorner( wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ), -1 );
if( v1.curve )
{
EVERTEX v2 = vertices[i + 1];
wxPoint center = ConvertArcCenter(
wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ),
wxPoint( kicad_x( v2.x ), kicad_y( v2.y ) ), *v1.curve );
double angle = DEG2RAD( *v1.curve );
double end_angle = atan2( kicad_y( v2.y ) - center.y,
kicad_x( v2.x ) - center.x );
double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
+ pow( center.y - kicad_y( v1.y ), 2 ) );
// If we are curving, we need at least 2 segments otherwise
// delta_angle == angle
double delta_angle = angle / std::max(
2, GetArcToSegmentCount( KiROUND( radius ),
ARC_HIGH_DEF, *v1.curve ) - 1 );
for( double a = end_angle + angle;
fabs( a - end_angle ) > fabs( delta_angle );
a -= delta_angle )
{
zone->AppendCorner(
wxPoint( KiROUND( radius * cos( a ) ),
KiROUND( radius * sin( a ) ) ) + center,
-1 );
}
}
}
// If the pour is a cutout it needs to be set to a keepout
if( p.pour == EPOLYGON::CUTOUT )
{
zone->SetIsKeepout( true );
zone->SetDoNotAllowCopperPour( true );
zone->SetHatchStyle( ZONE_CONTAINER::NO_HATCH );
}
// if spacing is set the zone should be hatched
// However, use the default hatch step, p.spacing value has no meaning for Kicad
// TODO: see if this parameter is related to a grid fill option.
if( p.spacing )
zone->SetHatch( ZONE_CONTAINER::DIAGONAL_EDGE, zone->GetDefaultHatchPitch(), true );
// clearances, etc.
zone->SetArcSegmentCount( 32 ); // @todo: should be a constructor default?
zone->SetMinThickness( p.width.ToPcbUnits() );
// FIXME: KiCad zones have very rounded corners compared to eagle.
// This means that isolation amounts that work well in eagle
// tend to make copper intrude in soldermask free areas around pads.
if( p.isolate )
zone->SetZoneClearance( p.isolate->ToPcbUnits() );
else
zone->SetZoneClearance( 0 );
// missing == yes per DTD.
bool thermals = !p.thermals || *p.thermals;
zone->SetPadConnection( thermals ? PAD_ZONE_CONN_THERMAL : PAD_ZONE_CONN_FULL );
if( thermals )
{
// FIXME: eagle calculates dimensions for thermal spokes
// based on what the zone is connecting to.
// (i.e. width of spoke is half of the smaller side of an smd pad)
// This is a basic workaround
zone->SetThermalReliefGap( p.width.ToPcbUnits() + 50000 ); // 50000nm == 0.05mm
zone->SetThermalReliefCopperBridge( p.width.ToPcbUnits() + 50000 );
}
int rank = p.rank ? (p.max_priority - *p.rank) : p.max_priority;
zone->SetPriority( rank );
}
return zone;
}
void EAGLE_PLUGIN::orientModuleAndText( MODULE* m, const EELEMENT& e,
const EATTR* nameAttr, const EATTR* valueAttr )
{
@ -1546,9 +1680,7 @@ void EAGLE_PLUGIN::packagePolygon( MODULE* aModule, wxXmlNode* aTree ) const
aModule->GraphicalItemsList().PushBack( dwg );
dwg->SetWidth( 0 ); // it's filled, no need for boundary width
dwg->SetLayer( layer );
dwg->SetTimeStamp( EagleTimeStamp( aTree ) );
std::vector<wxPoint> pts;
@ -1788,13 +1920,9 @@ void EAGLE_PLUGIN::deleteTemplates()
}
/// non-owning container
typedef std::vector<ZONE_CONTAINER*> ZONES;
void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals )
{
ZONES zones; // per net
ZONES zones; // per net
m_xpath->push( "signals.signal", "name" );
@ -1993,121 +2121,14 @@ void EAGLE_PLUGIN::loadSignals( wxXmlNode* aSignals )
else if( itemName == "polygon" )
{
m_xpath->push( "polygon" );
auto* zone = loadPolygon( netItem );
EPOLYGON p( netItem );
PCB_LAYER_ID layer = kicad_layer( p.layer );
if( IsCopperLayer( layer ) )
if( zone )
{
// use a "netcode = 0" type ZONE:
ZONE_CONTAINER* zone = new ZONE_CONTAINER( m_board );
m_board->Add( zone, ADD_APPEND );
zones.push_back( zone );
zone->SetTimeStamp( EagleTimeStamp( netItem ) );
zone->SetLayer( layer );
zone->SetNetCode( netCode );
// Get the first vertex and iterate
wxXmlNode* vertex = netItem->GetChildren();
std::vector<EVERTEX> vertices;
// Create a circular vector of vertices
// The "curve" parameter indicates a curve from the current
// to the next vertex, so we keep the first at the end as well
// to allow the curve to link back
while( vertex )
{
if( vertex->GetName() == "vertex" )
vertices.push_back( EVERTEX( vertex ) );
vertex = vertex->GetNext();
}
vertices.push_back( vertices[0] );
for( size_t i = 0; i < vertices.size() - 1; i++ )
{
EVERTEX v1 = vertices[i];
// Append the corner
zone->AppendCorner( wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ), -1 );
if( v1.curve )
{
EVERTEX v2 = vertices[i + 1];
wxPoint center = ConvertArcCenter(
wxPoint( kicad_x( v1.x ), kicad_y( v1.y ) ),
wxPoint( kicad_x( v2.x ), kicad_y( v2.y ) ), *v1.curve );
double angle = DEG2RAD( *v1.curve );
double end_angle = atan2( kicad_y( v2.y ) - center.y,
kicad_x( v2.x ) - center.x );
double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
+ pow( center.y - kicad_y( v1.y ), 2 ) );
// If we are curving, we need at least 2 segments otherwise
// delta_angle == angle
double delta_angle = angle / std::max(
2, GetArcToSegmentCount( KiROUND( radius ),
ARC_HIGH_DEF, *v1.curve ) - 1 );
for( double a = end_angle + angle;
fabs( a - end_angle ) > fabs( delta_angle );
a -= delta_angle )
{
zone->AppendCorner(
wxPoint( KiROUND( radius * cos( a ) ),
KiROUND( radius * sin( a ) ) ) + center,
-1 );
}
}
}
// If the pour is a cutout it needs to be set to a keepout
if( p.pour == EPOLYGON::CUTOUT )
{
zone->SetIsKeepout( true );
zone->SetDoNotAllowCopperPour( true );
zone->SetHatchStyle( ZONE_CONTAINER::NO_HATCH );
}
// if spacing is set the zone should be hatched
// However, use the default hatch step, p.spacing value has no meaning for Kicad
// TODO: see if this parameter is related to a grid fill option.
if( p.spacing )
zone->SetHatch( ZONE_CONTAINER::DIAGONAL_EDGE, zone->GetDefaultHatchPitch(), true );
// clearances, etc.
zone->SetArcSegmentCount( 32 ); // @todo: should be a constructor default?
zone->SetMinThickness( p.width.ToPcbUnits() );
// FIXME: KiCad zones have very rounded corners compared to eagle.
// This means that isolation amounts that work well in eagle
// tend to make copper intrude in soldermask free areas around pads.
if( p.isolate )
{
zone->SetZoneClearance( p.isolate->ToPcbUnits() );
} else
{
zone->SetZoneClearance( 0 );
}
// missing == yes per DTD.
bool thermals = !p.thermals || *p.thermals;
zone->SetPadConnection( thermals ? PAD_ZONE_CONN_THERMAL : PAD_ZONE_CONN_FULL );
if( thermals )
{
// FIXME: eagle calculates dimensions for thermal spokes
// based on what the zone is connecting to.
// (i.e. width of spoke is half of the smaller side of an smd pad)
// This is a basic workaround
zone->SetThermalReliefGap( p.width.ToPcbUnits() + 50000 ); // 50000nm == 0.05mm
zone->SetThermalReliefCopperBridge( p.width.ToPcbUnits() + 50000 );
}
int rank = p.rank ? (p.max_priority - *p.rank) : p.max_priority;
zone->SetPriority( rank );
if( !zone->GetIsKeepout() )
zone->SetNetCode( netCode );
}
m_xpath->pop(); // "polygon"

View File

@ -33,8 +33,10 @@
#include <wx/xml/xml.h>
class D_PAD;
class TEXTE_MODULE;
typedef std::map<wxString, MODULE*> MODULE_MAP;
typedef std::vector<ZONE_CONTAINER*> ZONES;
typedef std::map<wxString, ENET> NET_MAP;
typedef NET_MAP::const_iterator NET_MAP_CITER;
@ -231,6 +233,12 @@ private:
void loadLibraries( wxXmlNode* aLibs );
void loadElements( wxXmlNode* aElements );
/** Loads a copper or keepout polygon and adds it to the board.
*
* @return The loaded zone or nullptr if was not processed.
*/
ZONE_CONTAINER* loadPolygon( wxXmlNode* aPolyNode );
void orientModuleAndText( MODULE* m, const EELEMENT& e, const EATTR* nameAttr, const EATTR* valueAttr );
void orientModuleText( MODULE* m, const EELEMENT& e, TEXTE_MODULE* txt, const EATTR* a );