|
|
/*
* This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2016 Cirilo Bernardo <cirilo.bernardo@gmail.com> * Copyright 2018-2021 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */
#include "kicadfootprint.h"
#include "3d_resolver.h"
#include "kicadcurve.h"
#include "kicadmodel.h"
#include "kicadpad.h"
#include "oce_utils.h"
#include <sexpr/sexpr.h>
#include <wx/log.h>
#include <wx/filename.h>
#include <iostream>
#include <limits>
#include <memory>
#include <sstream>
#include <Standard_Failure.hxx>
KICADFOOTPRINT::KICADFOOTPRINT( KICADPCB* aParent ){ m_parent = aParent; m_side = LAYER_NONE; m_rotation = 0.0; m_smd = false; m_tht = false; m_virtual = false;
return;}
KICADFOOTPRINT::~KICADFOOTPRINT(){ for( KICADPAD* i : m_pads ) delete i;
for( KICADCURVE* i : m_curves ) delete i;
for( KICADMODEL* i : m_models ) delete i;
return;}
bool KICADFOOTPRINT::Read( SEXPR::SEXPR* aEntry ){ if( !aEntry ) return false;
if( aEntry->IsList() ) { size_t nc = aEntry->GetNumberOfChildren(); SEXPR::SEXPR* child = aEntry->GetChild( 0 ); std::string name = child->GetSymbol();
if( name != "module" && name != "footprint" ) { std::ostringstream ostr; ostr << "* BUG: module parser invoked for type '" << name << "'\n"; wxLogMessage( "%s\n", ostr.str().c_str() ); return false; }
bool result = true;
for( size_t i = 2; i < nc && result; ++i ) { child = aEntry->GetChild( i ); std::string symname;
// skip the optional locked and/or placed attributes; due to the vagaries of the
// KiCad version of sexpr, the attribute may be a Symbol or a String
if( child->IsSymbol() || child->IsString() ) { if( child->IsSymbol() ) symname = child->GetSymbol(); else if( child->IsString() ) symname = child->GetString();
if( symname == "locked" || symname == "placed" ) continue;
wxLogMessage( "* module descr in PCB file at line %d: unexpected keyword '%s'\n", child->GetLineNumber(), symname.c_str() ); return false; }
if( !child->IsList() ) { wxLogMessage( "* corrupt module in PCB file at line %d\n", child->GetLineNumber() ); return false; }
symname = child->GetChild( 0 )->GetSymbol();
if( symname == "layer" ) result = parseLayer( child ); else if( symname == "at" ) result = parsePosition( child ); else if( symname == "attr" ) result = parseAttribute( child ); else if( symname == "fp_text" ) result = parseText( child ); else if( symname == "fp_arc" ) result = parseCurve( child, CURVE_ARC ); else if( symname == "fp_line" ) result = parseCurve( child, CURVE_LINE ); else if( symname == "fp_circle" ) result = parseCurve( child, CURVE_CIRCLE ); else if( symname == "fp_rect" ) result = parseRect( child ); else if( symname == "fp_curve" ) result = parseCurve( child, CURVE_BEZIER ); else if( symname == "pad" ) result = parsePad( child ); else if( symname == "model" ) result = parseModel( child ); }
if( !m_smd && !m_tht ) m_virtual = true;
return result; }
std::ostringstream ostr; ostr << "* data is not a valid PCB module\n"; wxLogMessage( "%s\n", ostr.str().c_str() );
return false;}
bool KICADFOOTPRINT::parseRect( SEXPR::SEXPR* data ){ std::unique_ptr<KICADCURVE> rect = std::make_unique<KICADCURVE>();
if( !rect->Read( data, CURVE_LINE ) ) return false;
// reject any curves not on the Edge.Cuts layer
if( rect->GetLayer() != LAYER_EDGE ) return true;
KICADCURVE* top = new KICADCURVE( *rect ); KICADCURVE* right = new KICADCURVE( *rect ); KICADCURVE* bottom = new KICADCURVE( *rect ); KICADCURVE* left = new KICADCURVE( *rect );
top->m_end.y = right->m_start.y; m_curves.push_back( top );
right->m_start.x = bottom->m_end.x; m_curves.push_back( right );
bottom->m_start.y = left->m_end.y; m_curves.push_back( bottom );
left->m_end.x = top->m_start.x; m_curves.push_back( left );
return true;}
bool KICADFOOTPRINT::parseModel( SEXPR::SEXPR* data ){ KICADMODEL* mp = new KICADMODEL();
if( !mp->Read( data ) ) { delete mp; return false; }
if( mp->Hide() ) delete mp; else m_models.push_back( mp );
return true;}
bool KICADFOOTPRINT::parseCurve( SEXPR::SEXPR* data, CURVE_TYPE aCurveType ){ KICADCURVE* mp = new KICADCURVE();
if( !mp->Read( data, aCurveType ) ) { delete mp; return false; }
// NOTE: for now we are only interested in glyphs on the outline layer
if( LAYER_EDGE != mp->GetLayer() ) { delete mp; return true; }
m_curves.push_back( mp ); return true;}
bool KICADFOOTPRINT::parseLayer( SEXPR::SEXPR* data ){ SEXPR::SEXPR* val = data->GetChild( 1 ); std::string layername;
if( val->IsSymbol() ) layername = val->GetSymbol(); else if( val->IsString() ) layername = val->GetString(); else { std::ostringstream ostr; ostr << "* corrupt module in PCB file (line "; ostr << val->GetLineNumber() << "); layer cannot be parsed\n"; wxLogMessage( "%s\n", ostr.str().c_str() ); return false; }
int layerId = m_parent->GetLayerId( layername );
if( layerId == 31 ) m_side = LAYER_BOTTOM; else m_side = LAYER_TOP;
return true;}
bool KICADFOOTPRINT::parsePosition( SEXPR::SEXPR* data ){ return Get2DPositionAndRotation( data, m_position, m_rotation );}
bool KICADFOOTPRINT::parseAttribute( SEXPR::SEXPR* data ){ if( data->GetNumberOfChildren() < 2 ) { std::ostringstream ostr; ostr << "* corrupt module in PCB file (line "; ostr << data->GetLineNumber() << "); attribute cannot be parsed\n"; wxLogMessage( "%s\n", ostr.str().c_str() ); return false; }
SEXPR::SEXPR* child = data->GetChild( 1 ); std::string text;
if( child->IsSymbol() ) text = child->GetSymbol(); else if( child->IsString() ) text = child->GetString();
if( text == "smd" ) m_smd = true; else if( text == "through_hole" ) m_tht = true; else if( text == "virtual" ) m_virtual = true;
return true;}
bool KICADFOOTPRINT::parseText( SEXPR::SEXPR* data ){ // we're only interested in the Reference Designator
if( data->GetNumberOfChildren() < 3 ) return true;
SEXPR::SEXPR* child = data->GetChild( 1 ); std::string text;
if( child->IsSymbol() ) text = child->GetSymbol(); else if( child->IsString() ) text = child->GetString();
if( text != "reference" ) return true;
child = data->GetChild( 2 );
if( child->IsSymbol() ) text = child->GetSymbol(); else if( child->IsString() ) text = child->GetString();
m_refdes = text; return true;}
bool KICADFOOTPRINT::parsePad( SEXPR::SEXPR* data ){ KICADPAD* mp = new KICADPAD();
if( !mp->Read( data ) ) { delete mp; return false; }
// NOTE: for now we only accept thru-hole pads
// for the MCAD description
if( !mp->IsThruHole() ) { delete mp; return true; }
m_pads.push_back( mp ); return true;}
bool KICADFOOTPRINT::ComposePCB( class PCBMODEL* aPCB, S3D_RESOLVER* resolver, DOUBLET aOrigin, bool aComposeVirtual, bool aSubstituteModels ){ // translate pads and curves to final position and append to PCB.
double dlim = (double)std::numeric_limits< float >::epsilon();
double vsin = sin( m_rotation ); double vcos = cos( m_rotation ); bool hasdata = false;
double posX = m_position.x - aOrigin.x; double posY = m_position.y - aOrigin.y;
for( KICADCURVE* i : m_curves ) { if( i->m_layer != LAYER_EDGE || CURVE_NONE == i->m_form ) continue;
KICADCURVE lcurve = *i;
lcurve.m_start.y = -lcurve.m_start.y; lcurve.m_end.y = -lcurve.m_end.y; lcurve.m_angle = -lcurve.m_angle;
if( m_rotation < -dlim || m_rotation > dlim ) { double x = lcurve.m_start.x * vcos - lcurve.m_start.y * vsin; double y = lcurve.m_start.x * vsin + lcurve.m_start.y * vcos; lcurve.m_start.x = x; lcurve.m_start.y = y; x = lcurve.m_end.x * vcos - lcurve.m_end.y * vsin; y = lcurve.m_end.x * vsin + lcurve.m_end.y * vcos; lcurve.m_end.x = x; lcurve.m_end.y = y; }
lcurve.m_start.x += posX; lcurve.m_start.y -= posY; lcurve.m_end.x += posX; lcurve.m_end.y -= posY;
if( aPCB->AddOutlineSegment( &lcurve ) ) hasdata = true;
}
for( KICADPAD* i : m_pads ) { if( !i->IsThruHole() ) continue;
KICADPAD lpad = *i; lpad.m_position.y = -lpad.m_position.y;
if( m_rotation < -dlim || m_rotation > dlim ) { double x = lpad.m_position.x * vcos - lpad.m_position.y * vsin; double y = lpad.m_position.x * vsin + lpad.m_position.y * vcos; lpad.m_position.x = x; lpad.m_position.y = y; }
lpad.m_position.x += posX; lpad.m_position.y -= posY;
if( aPCB->AddPadHole( &lpad ) ) hasdata = true;
}
if( m_virtual && !aComposeVirtual ) return hasdata;
DOUBLET newpos( posX, posY );
for( KICADMODEL* i : m_models ) { wxString mname = wxString::FromUTF8Unchecked( i->m_modelname.c_str() );
if( mname.empty() ) continue;
std::vector<wxString> searchedPaths; mname = resolver->ResolvePath( mname, searchedPaths );
if( !wxFileName::FileExists( mname ) ) { wxString paths;
for( const wxString& path : searchedPaths ) paths += " " + path + "\n";
ReportMessage( wxString::Format( "Could not add 3D model to %s.\n" "File not found: %s\n" "Searched paths:\n" "%s", m_refdes, mname, paths) ); continue; }
std::string fname( mname.ToUTF8() );
try { if( aPCB->AddComponent( fname, m_refdes, LAYER_BOTTOM == m_side ? true : false, newpos, m_rotation, i->m_offset, i->m_rotation, i->m_scale, aSubstituteModels ) ) { hasdata = true; } } catch( const Standard_Failure& e) { ReportMessage( wxString::Format( "Could not add 3D model to %s.\n" "OpenCASCADE error: %s\n", m_refdes, e.GetMessageString() ) ); } }
return hasdata;}
|