From dcf60f8e6fe536e6dd67159bba11a354fd444f8a Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Fri, 18 May 2018 18:14:54 +0200 Subject: [PATCH] Eagle PCB import: handle keepout zones Fixes: lp:1755886 * https://bugs.launchpad.net/kicad/+bug/1755886 --- pcbnew/eagle_plugin.cpp | 261 ++++++++++++++++++++++------------------ pcbnew/eagle_plugin.h | 8 ++ 2 files changed, 149 insertions(+), 120 deletions(-) diff --git a/pcbnew/eagle_plugin.cpp b/pcbnew/eagle_plugin.cpp index 2253bb955d..f8fe41405a 100644 --- a/pcbnew/eagle_plugin.cpp +++ b/pcbnew/eagle_plugin.cpp @@ -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 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 pts; @@ -1788,13 +1920,9 @@ void EAGLE_PLUGIN::deleteTemplates() } -/// non-owning container -typedef std::vector 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 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" diff --git a/pcbnew/eagle_plugin.h b/pcbnew/eagle_plugin.h index 14e257cc63..b945f5ea71 100644 --- a/pcbnew/eagle_plugin.h +++ b/pcbnew/eagle_plugin.h @@ -33,8 +33,10 @@ #include class D_PAD; +class TEXTE_MODULE; typedef std::map MODULE_MAP; +typedef std::vector ZONES; typedef std::map 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 );