|
|
@ -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"
|
|
|
|