Commit 47c1edd7 authored by unknown's avatar unknown Committed by jean-pierre charras

Add patch about idf export (from cirilo_bernardo), with a fix for Windows.

parents b9246dd4 6652bcdc
include_directories(
"${CMAKE_SOURCE_DIR}/include"
"${CMAKE_SOURCE_DIR}/lib_dxf"
"${CMAKE_SOURCE_DIR}/pcbnew/exporters"
"${CMAKE_SOURCE_DIR}/utils/idftools"
)
link_directories(
"${CMAKE_BINARY_DIR}/lib_dxf"
)
)
add_executable( idfcyl idf_cylinder.cpp )
add_library( idf3 STATIC
idf_helpers.cpp idf_common.cpp idf_outlines.cpp
idf_parser.cpp vrml_layer.cpp )
add_executable( idfcyl idf_cylinder.cpp )
add_executable( idfrect idf_rect.cpp )
add_executable( dxf2idf dxf2idfmain.cpp dxf2idf.cpp )
add_executable( idf2vrml idf2vrml.cpp )
add_executable( dxf2idf dxf2idfmain.cpp dxf2idf.cpp
"${CMAKE_SOURCE_DIR}/pcbnew/exporters/idf_common.cpp"
"${CMAKE_SOURCE_DIR}/common/richio.cpp"
)
target_link_libraries( dxf2idf lib_dxf idf3 ${wxWidgets_LIBRARIES} )
if( WIN32 )
set ( LIB_GLU glu32 )
else()
set ( LIB_GLU GLU )
endif()
add_dependencies( idfcyl lib-dependencies )
add_dependencies( idfrect lib-dependencies )
add_dependencies( dxf2idf lib-dependencies )
target_link_libraries( idf2vrml idf3 ${LIB_GLU} ${wxWidgets_LIBRARIES} )
target_link_libraries( dxf2idf lib_dxf ${wxWidgets_LIBRARIES} )
install( TARGETS idfcyl idfrect dxf2idf
install( TARGETS idfcyl idfrect dxf2idf idf2vrml
DESTINATION ${KICAD_BIN}
COMPONENT binary )
......@@ -123,7 +123,8 @@ int main( int argc, char **argv )
tstr.clear();
tstr.str( line );
if( (tstr >> height ) && height > 0.001 )
tstr >> height;
if( !tstr.fail() && height > 0.001 )
ok = true;
}
......
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 Cirilo Bernardo
*
* 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
*/
/*
* This program takes an IDF base name, loads the board outline
* and component outine files, and creates a single VRML file.
* The VRML file can be used to visually verify the IDF files
* before sending them to a mechanical designer. The output scale
* is 10:1; this scale was chosen because VRML was originally
* intended to describe large virtual worlds and rounding errors
* would be more likely if we used a 1:1 scale.
*/
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <sstream>
#include <cmath>
#include <cstdio>
#include <cerrno>
#include <list>
#include <utility>
#include <clocale>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <libgen.h>
#include <unistd.h>
#include <idf_helpers.h>
#include <idf_common.h>
#include <idf_parser.h>
#include <vrml_layer.h>
#ifndef MIN_ANG
#define MIN_ANG 0.01
#endif
extern char* optarg;
extern int optopt;
using namespace std;
#define CLEANUP do { \
setlocale( LC_ALL, "C" ); \
} while( 0 );
// define colors
struct VRML_COLOR
{
double diff[3];
double emis[3];
double spec[3];
double ambi;
double tran;
double shin;
};
struct VRML_IDS
{
int colorIndex;
std::string objectName;
bool used;
bool bottom;
double dX, dY, dZ, dA;
VRML_IDS()
{
colorIndex = 0;
used = false;
bottom = false;
dX = 0.0;
dY = 0.0;
dZ = 0.0;
dA = 0.0;
}
};
#define NCOLORS 7
VRML_COLOR colors[NCOLORS] = { \
{ 0, 0.82, 0.247, 0, 0, 0, 0, 0.82, 0.247, 0.9, 0, 0.1}, \
{ 1, 0, 0, 1, 0, 0, 1, 0, 0, 0.9, 0, 0.1}, \
{ 0.659, 0, 0.463, 0, 0, 0, 0.659, 0, 0.463, 0.9, 0, 0.1}, \
{ 0.659, 0.294, 0, 0, 0, 0, 0.659, 0.294, 0, 0.9, 0, 0.1}, \
{ 0, 0.918, 0.659, 0, 0, 0, 0, 0.918, 0.659, 0.9, 0, 0.1}, \
{ 0.808, 0.733 , 0.071, 0, 0, 0, 0.808, 0.733 , 0.071, 0.9, 0, 0.1}, \
{ 0.102, 1, 0.984, 0, 0, 0, 0.102, 1, 0.984, 0.9, 0, 0.1}
};
bool WriteHeader( IDF3_BOARD& board, std::ofstream& file );
bool MakeBoard( IDF3_BOARD& board, std::ofstream& file );
bool MakeComponents( IDF3_BOARD& board, std::ofstream& file, bool compact );
bool PopulateVRML( VRML_LAYER& model, const std::list< IDF_OUTLINE* >* items, bool bottom,
double scale, double dX = 0.0, double dY = 0.0, double angle = 0.0 );
bool AddSegment( VRML_LAYER& model, IDF_SEGMENT* seg, int icont, int iseg );
bool WriteTriangles( std::ofstream& file, VRML_IDS* vID, VRML_LAYER* layer, bool plane,
bool top, double top_z, double bottom_z, int precision, bool compact );
inline void TransformPoint( IDF_SEGMENT& seg, double frac, bool bottom,
double dX, double dY, double angle );
VRML_IDS* GetColor( std::map<std::string, VRML_IDS*>& cmap,
int& index, const std::string& uid );
void PrintUsage( void )
{
cout << "-\nUsage: idf2vrml -f input_file.emn -s scale_factor -k\n";
cout << "flags: -k: produce KiCad-firendly VRML output; default is compact VRML\n-\n";
cout << "example to produce a model for use by KiCad: idf2vrml -f input.emn -s 0.3937008 -k\n\n";
return;
}
int main( int argc, char **argv )
{
// IDF implicitly requires the C locale
setlocale( LC_ALL, "C" );
// Essential inputs:
// 1. IDF file
// 2. Output scale: internal IDF units are mm, so 1 = 1mm per VRML unit,
// 0.1 = 1cm per VRML unit, 0.01 = 1m per VRML unit,
// 1/25.4 = 1in per VRML unit, 1/2.54 = 0.1in per VRML unit (KiCad model)
// 3. KiCad-friendly output (do not reuse features via DEF+USE)
// Render each component to VRML; if the user wants
// a KiCad friendly output then we must avoid DEF+USE;
// otherwise we employ DEF+USE to minimize file size
std::string inputFilename;
double scaleFactor = 1.0;
bool compact = true;
int ichar;
while( ( ichar = getopt( argc, argv, ":f:s:k" ) ) != -1 )
{
switch( ichar )
{
case 'f':
inputFilename = optarg;
break;
case 's':
do
{
errno = 0;
char* cp = NULL;
scaleFactor = strtod( optarg, &cp );
if( errno || cp == optarg )
{
cerr << "* invalid scale factor: '" << optarg << "'\n";
return -1;
}
if( scaleFactor < 0.001 || scaleFactor > 10 )
{
cerr << "* scale factor out of range (" << scaleFactor << "); range is 0.001 to 10.0\n";
return -1;
}
} while( 0 );
break;
case 'k':
compact = false;
break;
case ':':
cerr << "* Missing parameter to option '-" << ((char) optopt) << "'\n";
PrintUsage();
return -1;
break;
default:
cerr << "* Unexpected option: '-";
if( ichar == '?' )
cerr << ((char) optopt) << "'\n";
else
cerr << ((char) ichar) << "'\n";
PrintUsage();
return -1;
break;
}
}
if( inputFilename.empty() )
{
cerr << "* no IDF filename supplied\n";
PrintUsage();
return -1;
}
IDF3_BOARD pcb( IDF3::CAD_ELEC );
cout << "** Reading file: " << inputFilename << "\n";
if( !pcb.ReadFile( FROM_UTF8( inputFilename.c_str() ) ) )
{
CLEANUP;
cerr << "* Could not read file: " << inputFilename << "\n";
return -1;
}
// set the scale and output precision ( scale 1 == precision 5)
pcb.SetUserScale( scaleFactor );
if( scaleFactor < 0.01 )
pcb.SetUserPrecision( 8 );
else if( scaleFactor < 0.1 )
pcb.SetUserPrecision( 7 );
else if( scaleFactor < 1.0 )
pcb.SetUserPrecision( 6 );
else if( scaleFactor < 10.0 )
pcb.SetUserPrecision( 5 );
else
pcb.SetUserPrecision( 4 );
// Create the VRML file and write the header
char* bnp = (char*) malloc( inputFilename.size() + 1 );
strcpy( bnp, inputFilename.c_str() );
std::string fname = basename( bnp );
free( bnp );
std::string::iterator itf = fname.end();
*(--itf) = 'l';
*(--itf) = 'r';
*(--itf) = 'w';
cout << "Writing file: '" << fname << "'\n";
std::ofstream ofile;
ofile.open( fname.c_str(), std::ios_base::out );
ofile << fixed; // do not use exponents in VRML output
WriteHeader( pcb, ofile );
// STEP 1: Render the PCB alone
MakeBoard( pcb, ofile );
// STEP 2: Render the components
MakeComponents( pcb, ofile, compact );
ofile << "]\n}\n";
ofile.close();
// restore the locale
setlocale( LC_ALL, "" );
return 0;
}
bool WriteHeader( IDF3_BOARD& board, std::ofstream& file )
{
std::string bname = board.GetBoardName();
if( bname.empty() )
{
bname = "BoardWithNoName";
}
else
{
std::string::iterator ss = bname.begin();
std::string::iterator se = bname.end();
while( ss != se )
{
if( *ss == '/' || *ss == ' ' || *ss == ':' )
*ss = '_';
++ss;
}
}
file << "#VRML V2.0 utf8\n\n";
file << "WorldInfo {\n";
file << " title \"" << bname << "\"\n}\n\n";
file << "Transform {\n";
file << "children [\n";
return !file.fail();
}
bool MakeBoard( IDF3_BOARD& board, std::ofstream& file )
{
VRML_LAYER vpcb;
if( board.GetBoardOutlinesSize() < 1 )
{
ERROR_IDF << "\n";
cerr << "* Cannot proceed; no board outline in IDF object\n";
return false;
}
double scale = board.GetUserScale();
// set the arc parameters according to output scale
int tI;
double tMin, tMax;
vpcb.GetArcParams( tI, tMin, tMax );
vpcb.SetArcParams( tI, tMin * scale, tMax * scale );
if( !PopulateVRML( vpcb, board.GetBoardOutline()->GetOutlines(), false, board.GetUserScale() ) )
{
return false;
}
vpcb.EnsureWinding( 0, false );
int nvcont = vpcb.GetNContours();
while( nvcont > 0 )
vpcb.EnsureWinding( nvcont--, true );
// Add the drill holes
const std::list<IDF_DRILL_DATA*>* drills = &board.GetBoardDrills();
std::list<IDF_DRILL_DATA*>::const_iterator sd = drills->begin();
std::list<IDF_DRILL_DATA*>::const_iterator ed = drills->end();
while( sd != ed )
{
vpcb.AddCircle( (*sd)->GetDrillXPos() * scale, (*sd)->GetDrillYPos() * scale,
(*sd)->GetDrillDia() * scale / 2.0, true );
++sd;
}
std::map< std::string, IDF3_COMPONENT* >*const comp = board.GetComponents();
std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
while( sc != ec )
{
drills = sc->second->GetDrills();
sd = drills->begin();
ed = drills->end();
while( sd != ed )
{
vpcb.AddCircle( (*sd)->GetDrillXPos() * scale, (*sd)->GetDrillYPos() * scale,
(*sd)->GetDrillDia() * scale / 2.0, true );
++sd;
}
++sc;
}
// tesselate and write out
vpcb.Tesselate( NULL );
double thick = board.GetBoardThickness() / 2.0 * scale;
VRML_IDS tvid;
tvid.colorIndex = 0;
WriteTriangles( file, &tvid, &vpcb, false, false,
thick, -thick, board.GetUserPrecision(), false );
return true;
}
bool PopulateVRML( VRML_LAYER& model, const std::list< IDF_OUTLINE* >* items, bool bottom, double scale,
double dX, double dY, double angle )
{
// empty outlines are not unusual so we fail quietly
if( items->size() < 1 )
return false;
int nvcont = 0;
int iseg = 0;
std::list< IDF_OUTLINE* >::const_iterator scont = items->begin();
std::list< IDF_OUTLINE* >::const_iterator econt = items->end();
std::list<IDF_SEGMENT*>::iterator sseg;
std::list<IDF_SEGMENT*>::iterator eseg;
IDF_SEGMENT lseg;
while( scont != econt )
{
nvcont = model.NewContour();
if( nvcont < 0 )
{
ERROR_IDF << "\n";
cerr << "* cannot create an outline\n";
return false;
}
if( (*scont)->size() < 1 )
{
ERROR_IDF << "invalid contour: no vertices\n";
return false;
}
sseg = (*scont)->begin();
eseg = (*scont)->end();
iseg = 0;
while( sseg != eseg )
{
lseg = **sseg;
TransformPoint( lseg, scale, bottom, dX, dY, angle );
if( !AddSegment( model, &lseg, nvcont, iseg ) )
return false;
++iseg;
++sseg;
}
++scont;
}
return true;
}
bool AddSegment( VRML_LAYER& model, IDF_SEGMENT* seg, int icont, int iseg )
{
// note: in all cases we must add all but the last point in the segment
// to avoid redundant points
if( seg->angle != 0.0 )
{
if( seg->IsCircle() )
{
if( iseg != 0 )
{
ERROR_IDF << "adding a circle to an existing vertex list\n";
return false;
}
return model.AppendCircle( seg->center.x, seg->center.y, seg->radius, icont );
}
else
{
return model.AppendArc( seg->center.x, seg->center.y, seg->radius,
seg->offsetAngle, seg->angle, icont );
}
}
if( !model.AddVertex( icont, seg->startPoint.x, seg->startPoint.y ) )
return false;
return true;
}
bool WriteTriangles( std::ofstream& file, VRML_IDS* vID, VRML_LAYER* layer, bool plane,
bool top, double top_z, double bottom_z, int precision, bool compact )
{
if( vID == NULL || layer == NULL )
return false;
file << "Transform {\n";
if( compact && !vID->objectName.empty() )
{
file << "translation " << setprecision( precision ) << vID->dX;
file << " " << vID->dY << " ";
if( vID->bottom )
{
file << -vID->dZ << "\n";
double tx, ty;
// calculate the rotation axis and angle
tx = cos( M_PI2 - vID->dA / 2.0 );
ty = sin( M_PI2 - vID->dA / 2.0 );
file << "rotation " << setprecision( precision );
file << tx << " " << ty << " 0 ";
file << setprecision(5) << M_PI << "\n";
}
else
{
file << vID->dZ << "\n";
file << "rotation 0 0 1 " << setprecision(5) << vID->dA << "\n";
}
file << "children [\n";
if( vID->used )
{
file << "USE " << vID->objectName << "\n";
file << "]\n";
file << "}\n";
return true;
}
file << "DEF " << vID->objectName << " Transform {\n";
if( !plane && top_z <= bottom_z )
{
// the height specification is faulty; make the component
// a bright red to highlight it
vID->colorIndex = 1;
// we don't know the scale, but 5 units is huge in most situations
top_z = bottom_z + 5.0;
}
}
VRML_COLOR* color = &colors[vID->colorIndex];
vID->used = true;
file << "children [\n";
file << "Group {\n";
file << "children [\n";
file << "Shape {\n";
file << "appearance Appearance {\n";
file << "material Material {\n";
// material definition
file << "diffuseColor " << setprecision(3) << color->diff[0] << " ";
file << color->diff[1] << " " << color->diff[2] << "\n";
file << "specularColor " << color->spec[0] << " " << color->spec[1];
file << " " << color->spec[2] << "\n";
file << "emissiveColor " << color->emis[0] << " " << color->emis[1];
file << " " << color->emis[2] << "\n";
file << "ambientIntensity " << color->ambi << "\n";
file << "transparency " << color->tran << "\n";
file << "shininess " << color->shin << "\n";
file << "}\n";
file << "}\n";
file << "geometry IndexedFaceSet {\n";
file << "solid TRUE\n";
file << "coord Coordinate {\n";
file << "point [\n";
// XXX: TODO: check return values of Write() routines; they may fail
// even though the stream is good (bad internal data)
// Coordinates (vertices)
if( plane )
{
if( ! layer->WriteVertices( top_z, file, precision ) )
{
cerr << "* errors writing planar vertices to " << vID->objectName << "\n";
cerr << "** " << layer->GetError() << "\n";
}
}
else
{
if( ! layer->Write3DVertices( top_z, bottom_z, file, precision ) )
{
cerr << "* errors writing 3D vertices to " << vID->objectName << "\n";
cerr << "** " << layer->GetError() << "\n";
}
}
file << "\n";
file << "]\n";
file << "}\n";
file << "coordIndex [\n";
// Indices
if( plane )
layer->WriteIndices( top, file );
else
layer->Write3DIndices( file );
file << "\n";
file << "]\n";
file << "}\n";
file << "}\n";
file << "]\n";
file << "}\n";
file << "]\n";
file << "}\n";
if( compact && !vID->objectName.empty() )
{
file << "]\n";
file << "}\n";
}
return !file.fail();
}
inline void TransformPoint( IDF_SEGMENT& seg, double frac, bool bottom,
double dX, double dY, double angle )
{
dX *= frac;
dY *= frac;
if( bottom )
{
// mirror points on the Y axis
seg.startPoint.x = -seg.startPoint.x;
seg.endPoint.x = -seg.endPoint.x;
seg.center.x = -seg.center.x;
angle = -angle;
}
seg.startPoint.x *= frac;
seg.startPoint.y *= frac;
seg.endPoint.x *= frac;
seg.endPoint.y *= frac;
seg.center.x *= frac;
seg.center.y *= frac;
double tsin = 0.0;
double tcos = 0.0;
if( angle > MIN_ANG || angle < -MIN_ANG )
{
double ta = angle * M_PI / 180.0;
double tx, ty;
tsin = sin( ta );
tcos = cos( ta );
tx = seg.startPoint.x * tcos - seg.startPoint.y * tsin;
ty = seg.startPoint.x * tsin + seg.startPoint.y * tcos;
seg.startPoint.x = tx;
seg.startPoint.y = ty;
tx = seg.endPoint.x * tcos - seg.endPoint.y * tsin;
ty = seg.endPoint.x * tsin + seg.endPoint.y * tcos;
seg.endPoint.x = tx;
seg.endPoint.y = ty;
if( seg.angle != 0 )
{
tx = seg.center.x * tcos - seg.center.y * tsin;
ty = seg.center.x * tsin + seg.center.y * tcos;
seg.center.x = tx;
seg.center.y = ty;
}
}
seg.startPoint.x += dX;
seg.startPoint.y += dY;
seg.endPoint.x += dX;
seg.endPoint.y += dY;
seg.center.x += dX;
seg.center.y += dY;
if( seg.angle != 0 )
{
seg.radius *= frac;
if( bottom )
{
if( !seg.IsCircle() )
{
seg.angle = -seg.angle;
if( seg.offsetAngle > 0.0 )
seg.offsetAngle = 180 - seg.offsetAngle;
else
seg.offsetAngle = -seg.offsetAngle - 180;
}
}
if( angle > MIN_ANG || angle < -MIN_ANG )
seg.offsetAngle += angle;
}
return;
}
bool MakeComponents( IDF3_BOARD& board, std::ofstream& file, bool compact )
{
int cidx = 2; // color index; start at 2 since 0,1 are special (board, NOGEOM_NOPART)
VRML_LAYER vpcb;
double scale = board.GetUserScale();
double thick = board.GetBoardThickness() / 2.0;
// set the arc parameters according to output scale
int tI;
double tMin, tMax;
vpcb.GetArcParams( tI, tMin, tMax );
vpcb.SetArcParams( tI, tMin * scale, tMax * scale );
// Add the component outlines
const std::map< std::string, IDF3_COMPONENT* >*const comp = board.GetComponents();
std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator so;
std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator eo;
double vX, vY, vA;
double tX, tY, tZ, tA;
double top, bot;
bool bottom;
IDF3::IDF_LAYER lyr;
std::map< std::string, VRML_IDS*> cmap; // map colors by outline UID
VRML_IDS* vcp;
while( sc != ec )
{
sc->second->GetPosition( vX, vY, vA, lyr );
if( lyr == IDF3::LYR_BOTTOM )
bottom = true;
else
bottom = false;
so = sc->second->GetOutlinesData()->begin();
eo = sc->second->GetOutlinesData()->end();
while( so != eo )
{
(*so)->GetOffsets( tX, tY, tZ, tA );
tX += vX;
tY += vY;
tA += vA;
vcp = GetColor( cmap, cidx, ((IDF3_COMP_OUTLINE*)((*so)->GetOutline()))->GetUID() );
if( !compact )
{
if( !PopulateVRML( vpcb, (*so)->GetOutline()->GetOutlines(), bottom,
board.GetUserScale(), tX, tY, tA ) )
{
return false;
}
}
else
{
if( !vcp->used && !PopulateVRML( vpcb, (*so)->GetOutline()->GetOutlines(), false,
board.GetUserScale() ) )
{
return false;
}
vcp->dX = tX * scale;
vcp->dY = tY * scale;
vcp->dZ = tZ * scale;
vcp->dA = tA * M_PI / 180.0;
}
if( !compact || !vcp->used )
{
vpcb.EnsureWinding( 0, false );
vpcb.Tesselate( NULL );
}
if( !compact )
{
if( bottom )
{
top = -thick - tZ;
bot = (top - (*so)->GetOutline()->GetThickness() ) * scale;
top *= scale;
}
else
{
bot = thick + tZ;
top = (bot + (*so)->GetOutline()->GetThickness() ) * scale;
bot *= scale;
}
}
else
{
bot = thick;
top = (bot + (*so)->GetOutline()->GetThickness() ) * scale;
bot *= scale;
}
vcp = GetColor( cmap, cidx, ((IDF3_COMP_OUTLINE*)((*so)->GetOutline()))->GetUID() );
vcp->bottom = bottom;
WriteTriangles( file, vcp, &vpcb, false,
false, top, bot, board.GetUserPrecision(), compact );
vpcb.Clear();
++so;
}
++sc;
}
return true;
}
VRML_IDS* GetColor( std::map<std::string, VRML_IDS*>& cmap, int& index, const std::string& uid )
{
static int refnum = 0;
if( index < 2 )
index = 2; // 0 and 1 are special (BOARD, UID=NOGEOM_NOPART)
std::map<std::string, VRML_IDS*>::iterator cit = cmap.find( uid );
if( cit == cmap.end() )
{
VRML_IDS* id = new VRML_IDS;
if( !uid.compare( "NOGEOM_NOPART" ) )
id->colorIndex = 1;
else
id->colorIndex = index++;
std::ostringstream ostr;
ostr << "OBJECTn" << refnum++;
id->objectName = ostr.str();
cout << "* " << ostr.str() << " = '" << uid << "'\n";
cmap.insert( std::pair<std::string, VRML_IDS*>(uid, id) );
if( index >= NCOLORS )
index = 2;
return id;
}
return cit->second;
}
/**
* file: idf_common.cpp
*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2014 Cirilo Bernardo
*
* 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 <list>
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <cerrno>
#include <cstdio>
#include <cmath>
#include <idf_common.h>
#include <idf_helpers.h>
using namespace IDF3;
using namespace std;
std::string source;
std::string message;
IDF_ERROR::IDF_ERROR( const char* aSourceFile,
const char* aSourceMethod,
int aSourceLine,
const std::string& aMessage ) throw()
{
ostringstream ostr;
if( aSourceFile )
ostr << "* " << aSourceFile << ":";
else
ostr << "* [BUG: No Source File]:";
ostr << aSourceLine << ":";
if( aSourceMethod )
ostr << aSourceMethod << "(): ";
else
ostr << "[BUG: No Source Method]:\n* ";
ostr << aMessage;
message = ostr.str();
return;
}
IDF_ERROR::~IDF_ERROR() throw()
{
return;
}
const char* IDF_ERROR::what() const throw()
{
return message.c_str();
}
IDF_NOTE::IDF_NOTE()
{
xpos = 0.0;
ypos = 0.0;
height = 0.0;
length = 0.0;
}
bool IDF_NOTE::ReadNote( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState,
IDF3::IDF_UNIT aBoardUnit )
{
std::string iline; // the input line
bool isComment; // true if a line just read in is a comment line
std::streampos pos;
int idx = 0;
bool quoted = false;
std::string token;
// RECORD 2: X, Y, text Height, text Length, "TEXT"
while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
{
ERROR_IDF;
cerr << "problems reading board notes\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( isComment )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: comment within a section (NOTES)\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
idx = 0;
GetIDFString( iline, token, quoted, idx );
if( quoted )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: X position in NOTES section must not be in quotes\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( CompareToken( ".END_NOTES", token ) )
{
// the end of the note section is a special case; although we return 'false'
// we do not change the board's state variable and we ensure that errno is 0;
// all other false returns set the state to FILE_INVALID
errno = 0;
return false;
}
istringstream istr;
istr.str( token );
istr >> xpos;
if( istr.fail() )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: X position in NOTES section is not numeric\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: Y position in NOTES section is missing\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( quoted )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: Y position in NOTES section must not be in quotes\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
istr.clear();
istr.str( token );
istr >> ypos;
if( istr.fail() )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: Y position in NOTES section is not numeric\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: text height in NOTES section is missing\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( quoted )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: text height in NOTES section must not be in quotes\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
istr.clear();
istr.str( token );
istr >> height;
if( istr.fail() )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: text height in NOTES section is not numeric\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: text length in NOTES section is missing\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( quoted )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: text length in NOTES section must not be in quotes\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
istr.clear();
istr.str( token );
istr >> length;
if( istr.fail() )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: text length in NOTES section is not numeric\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: text value in NOTES section is missing\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
text = token;
if( aBoardUnit == UNIT_THOU )
{
xpos *= IDF_MM_TO_THOU;
ypos *= IDF_MM_TO_THOU;
height *= IDF_MM_TO_THOU;
length *= IDF_MM_TO_THOU;
}
return true;
}
bool IDF_NOTE::WriteNote( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit )
{
if( aBoardUnit == UNIT_THOU )
{
aBoardFile << setiosflags(ios::fixed) << setprecision(1)
<< (xpos / IDF_MM_TO_THOU) << " "
<< (ypos / IDF_MM_TO_THOU) << " "
<< (height / IDF_MM_TO_THOU) << " "
<< (length / IDF_MM_TO_THOU) << " ";
}
else
{
aBoardFile << setiosflags(ios::fixed) << setprecision(5)
<< xpos << " " << ypos << " " << height << " " << length << " ";
}
aBoardFile << "\"" << text << "\"\n";
return !aBoardFile.bad();
}
void IDF_NOTE::SetText( const std::string& aText )
{
text = aText;
return;
}
void IDF_NOTE::SetPosition( double aXpos, double aYpos )
{
xpos = aXpos;
ypos = aYpos;
return;
}
void IDF_NOTE::SetSize( double aHeight, double aLength )
{
height = aHeight;
length = aLength;
return;
}
const std::string& IDF_NOTE::GetText( void )
{
return text;
}
void IDF_NOTE::GetPosition( double& aXpos, double& aYpos )
{
aXpos = xpos;
aYpos = ypos;
return;
}
void IDF_NOTE::GetSize( double& aHeight, double& aLength )
{
aHeight = height;
aLength = length;
return;
}
/*
* CLASS: IDF_DRILL_DATA
*/
IDF_DRILL_DATA::IDF_DRILL_DATA()
{
dia = 0.0;
x = 0.0;
y = 0.0;
plating = NPTH;
kref = NOREFDES;
khole = MTG;
owner = UNOWNED;
return;
}
IDF_DRILL_DATA::IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY,
IDF3::KEY_PLATING aPlating,
const std::string aRefDes,
const std::string aHoleType,
IDF3::KEY_OWNER aOwner )
{
if( aDrillDia < 0.3 )
dia = 0.3;
else
dia = aDrillDia;
x = aPosX;
y = aPosY;
plating = aPlating;
if( !aRefDes.compare( "BOARD" ) )
{
kref = BOARD;
}
else if( aRefDes.empty() || !aRefDes.compare( "NOREFDES" ) )
{
kref = NOREFDES;
}
else if( !aRefDes.compare( "PANEL" ) )
{
kref = PANEL;
}
else
{
kref = REFDES;
refdes = aRefDes;
}
if( !aHoleType.compare( "PIN" ) )
{
khole = PIN;
}
else if( !aHoleType.compare( "VIA" ) )
{
khole = VIA;
}
else if( aHoleType.empty() || !aHoleType.compare( "MTG" ) )
{
khole = MTG;
}
else if( !aHoleType.compare( "TOOL" ) )
{
khole = TOOL;
}
else
{
khole = OTHER;
holetype = aHoleType;
}
owner = aOwner;
} // IDF_DRILL_DATA::IDF_DRILL_DATA( ... )
bool IDF_DRILL_DATA::Matches( double aDrillDia, double aPosX, double aPosY )
{
double ddia = aDrillDia - dia;
IDF_POINT p1, p2;
p1.x = x;
p1.y = y;
p2.x = aPosX;
p2.y = aPosY;
if( ddia > -0.00001 && ddia < 0.00001 && p1.Matches( p2, 0.00001 ) )
return true;
return false;
}
bool IDF_DRILL_DATA::Read( std::ifstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit,
IDF3::FILE_STATE aBoardState )
{
std::string iline; // the input line
bool isComment; // true if a line just read in is a comment line
std::streampos pos;
int idx = 0;
bool quoted = false;
std::string token;
// RECORD 2: DIA, X, Y, Plating Style, REFDES, HOLE TYPE, HOLE OWNER
while( !FetchIDFLine( aBoardFile, iline, isComment, pos ) && aBoardFile.good() );
if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
{
ERROR_IDF;
cerr << "problems reading board drilled holes\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( isComment )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: comment within a section (DRILLED HOLES)\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
idx = 0;
GetIDFString( iline, token, quoted, idx );
if( quoted )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: drill diameter must not be in quotes\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( CompareToken( ".END_DRILLED_HOLES", token ) )
{
// the end of the section is a special case; although we return 'false'
// we do not change the board's state variable and we ensure that errno is 0;
// all other false returns set the state to FILE_INVALID
errno = 0;
return false;
}
istringstream istr;
istr.str( token );
istr >> dia;
if( istr.fail() )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: drill diameter is not numeric\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( ( aBoardUnit == UNIT_MM && dia < IDF_MIN_DIA_MM )
|| ( aBoardUnit != UNIT_MM && dia < IDF_MIN_DIA_THOU ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Invalid drill diameter (too small): " << token << "\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: missing X position for drilled hole\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( quoted )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: X position in DRILLED HOLES section must not be in quotes\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
istr.clear();
istr.str( token );
istr >> x;
if( istr.fail() )
{
ERROR_IDF;
cerr << "* Violation of specification: X position in DRILLED HOLES section is not numeric\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: missing Y position for drilled hole\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( quoted )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: Y position in DRILLED HOLES section must not be in quotes\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
istr.clear();
istr.str( token );
istr >> y;
if( istr.fail() )
{
ERROR_IDF;
cerr << "* Violation of specification: Y position in DRILLED HOLES section is not numeric\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: missing PLATING for drilled hole\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( CompareToken( "PTH", token ) )
{
plating = IDF3::PTH;
}
else if( CompareToken( "NPTH", token ) )
{
plating = IDF3::NPTH;
}
else
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: invalid PLATING type ('" << token << "')\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: missing REFDES for drilled hole\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( CompareToken( "BOARD", token ) )
{
kref = IDF3::BOARD;
}
else if( CompareToken( "NOREFDES", token ) )
{
kref = IDF3::NOREFDES;
}
else if( CompareToken( "PANEL", token ) )
{
kref = IDF3::PANEL;
}
else
{
kref = IDF3::REFDES;
refdes = token;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: missing HOLE TYPE for drilled hole\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( CompareToken( "PIN", token ) )
{
khole = IDF3::PIN;
}
else if( CompareToken( "VIA", token ) )
{
khole = IDF3::VIA;
}
else if( CompareToken( "MTG", token ) )
{
khole = IDF3::MTG;
}
else if( CompareToken( "TOOL", token ) )
{
khole = IDF3::TOOL;
}
else
{
khole = IDF3::OTHER;
holetype = token;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: missing OWNER for drilled hole\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( !ParseOwner( token, owner ) )
{
ERROR_IDF;
cerr << "invalid IDFv3 file\n";
cerr << "* Violation of specification: invalid OWNER for drilled hole ('" << token << "')\n";
aBoardState = IDF3::FILE_INVALID;
return false;
}
if( aBoardUnit == UNIT_THOU )
{
dia *= IDF_MM_TO_THOU;
x *= IDF_MM_TO_THOU;
y *= IDF_MM_TO_THOU;
}
return true;
}
bool IDF_DRILL_DATA::Write( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit )
{
std::string holestr;
std::string refstr;
std::string ownstr;
std::string pltstr;
switch( khole )
{
case PIN:
holestr = "PIN";
break;
case VIA:
holestr = "VIA";
break;
case TOOL:
holestr = "TOOL";
break;
case OTHER:
holestr = "\"" + holetype + "\"";
break;
default:
holestr = "MTG";
break;
}
switch( kref )
{
case BOARD:
refstr = "BOARD";
break;
case PANEL:
refstr = "PANEL";
break;
case REFDES:
refstr = "\"" + refdes + "\"";
break;
default:
refstr = "NOREFDES";
break;
}
if( plating == PTH )
pltstr = "PTH";
else
pltstr = "NPTH";
switch( owner )
{
case MCAD:
ownstr = "MCAD";
break;
case ECAD:
ownstr = "ECAD";
break;
default:
ownstr = "UNOWNED";
break;
}
if( aBoardUnit == UNIT_MM )
{
aBoardFile << std::setiosflags( std::ios::fixed ) << std::setprecision( 3 ) << dia << " "
<< std::setprecision( 5 ) << x << " " << y << " "
<< pltstr.c_str() << " " << refstr.c_str() << " "
<< holestr.c_str() << " " << ownstr.c_str() << "\n";
}
else
{
aBoardFile << std::setiosflags( std::ios::fixed ) << std::setprecision( 1 ) << (dia / IDF_MM_TO_THOU) << " "
<< std::setprecision( 1 ) << (x / IDF_MM_TO_THOU) << " " << (y / IDF_MM_TO_THOU) << " "
<< pltstr.c_str() << " " << refstr.c_str() << " "
<< holestr.c_str() << " " << ownstr.c_str() << "\n";
}
return ! aBoardFile.fail();
} // IDF_DRILL_DATA::Write( aBoardFile, unitMM )
double IDF_DRILL_DATA::GetDrillDia()
{
return dia;
}
double IDF_DRILL_DATA::GetDrillXPos()
{
return x;
}
double IDF_DRILL_DATA::GetDrillYPos()
{
return y;
}
IDF3::KEY_PLATING IDF_DRILL_DATA::GetDrillPlating()
{
return plating;
}
const std::string& IDF_DRILL_DATA::GetDrillRefDes()
{
switch( kref )
{
case BOARD:
refdes = "BOARD";
break;
case PANEL:
refdes = "PANEL";
break;
case REFDES:
break;
default:
refdes = "NOREFDES";
break;
}
return refdes;
}
const std::string& IDF_DRILL_DATA::GetDrillHoleType()
{
switch( khole )
{
case PIN:
holetype = "PIN";
break;
case VIA:
holetype = "VIA";
break;
case TOOL:
holetype = "TOOL";
break;
case OTHER:
break;
default:
holetype = "MTG";
break;
}
return holetype;
}
#ifdef DEBUG_IDF
void IDF3::PrintSeg( IDF_SEGMENT* aSegment )
{
if( aSegment->IsCircle() )
{
fprintf(stdout, "printSeg(): CIRCLE: C(%.3f, %.3f) P(%.3f, %.3f) rad. %.3f\n",
aSegment->startPoint.x, aSegment->startPoint.y,
aSegment->endPoint.x, aSegment->endPoint.y,
aSegment->radius );
return;
}
if( aSegment->angle < -MIN_ANG || aSegment->angle > MIN_ANG )
{
fprintf(stdout, "printSeg(): ARC: p1(%.3f, %.3f) p2(%.3f, %.3f) ang. %.3f\n",
aSegment->startPoint.x, aSegment->startPoint.y,
aSegment->endPoint.x, aSegment->endPoint.y,
aSegment->angle );
return;
}
fprintf(stdout, "printSeg(): LINE: p1(%.3f, %.3f) p2(%.3f, %.3f)\n",
aSegment->startPoint.x, aSegment->startPoint.y,
aSegment->endPoint.x, aSegment->endPoint.y );
return;
}
#endif
bool IDF_POINT::Matches( const IDF_POINT& aPoint, double aRadius )
{
double dx = x - aPoint.x;
double dy = y - aPoint.y;
double d2 = dx * dx + dy * dy;
if( d2 <= aRadius * aRadius )
return true;
return false;
}
double IDF_POINT::CalcDistance( const IDF_POINT& aPoint ) const
{
double dx = aPoint.x - x;
double dy = aPoint.y - y;
double dist = sqrt( dx * dx + dy * dy );
return dist;
}
double IDF3::CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
{
return atan2( aEndPoint.y - aStartPoint.y, aEndPoint.x - aStartPoint.x );
}
double IDF3::CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
{
double ang = CalcAngleRad( aStartPoint, aEndPoint );
// round to thousandths of a degree
int iang = int (ang / M_PI * 1800000.0);
ang = iang / 10000.0;
return ang;
}
void IDF3::GetOutline( std::list<IDF_SEGMENT*>& aLines,
IDF_OUTLINE& aOutline )
{
aOutline.Clear();
// NOTE: To tell if the point order is CCW or CW,
// sum all: (endPoint.X[n] - startPoint.X[n])*(endPoint[n] + startPoint.Y[n])
// If the result is >0, the direction is CW, otherwise
// it is CCW. Note that the result cannot be 0 unless
// we have a bounded area of 0.
// First we find the segment with the leftmost point
std::list<IDF_SEGMENT*>::iterator bl = aLines.begin();
std::list<IDF_SEGMENT*>::iterator el = aLines.end();
std::list<IDF_SEGMENT*>::iterator idx = bl++; // iterator for the object with minX
double minx = (*idx)->GetMinX();
double curx;
while( bl != el )
{
curx = (*bl)->GetMinX();
if( curx < minx )
{
minx = curx;
idx = bl;
}
++bl;
}
aOutline.push( *idx );
#ifdef DEBUG_IDF
PrintSeg( *idx );
#endif
aLines.erase( idx );
// If the item is a circle then we're done
if( aOutline.front()->IsCircle() )
return;
// Assemble the loop
bool complete = false; // set if loop is complete
bool matched; // set if a segment's end point was matched
while( !complete )
{
matched = false;
bl = aLines.begin();
el = aLines.end();
while( bl != el && !matched )
{
if( (*bl)->MatchesStart( aOutline.back()->endPoint ) )
{
if( (*bl)->IsCircle() )
{
// a circle on the perimeter is pathological but we just ignore it
++bl;
}
else
{
matched = true;
#ifdef DEBUG_IDF
PrintSeg( *bl );
#endif
aOutline.push( *bl );
aLines.erase( bl );
}
continue;
}
++bl;
}
if( !matched )
{
// attempt to match the end points
bl = aLines.begin();
el = aLines.end();
while( bl != el && !matched )
{
if( (*bl)->MatchesEnd( aOutline.back()->endPoint ) )
{
if( (*bl)->IsCircle() )
{
// a circle on the perimeter is pathological but we just ignore it
++bl;
}
else
{
matched = true;
(*bl)->SwapEnds();
#ifdef DEBUG_IDF
printSeg( *bl );
#endif
aOutline.push( *bl );
aLines.erase( bl );
}
continue;
}
++bl;
}
}
if( !matched )
{
// still no match - attempt to close the loop
if( (aOutline.size() > 1) || ( aOutline.front()->angle < -MIN_ANG )
|| ( aOutline.front()->angle > MIN_ANG ) )
{
// close the loop
IDF_SEGMENT* seg = new IDF_SEGMENT( aOutline.back()->endPoint,
aOutline.front()->startPoint );
if( seg )
{
complete = true;
#ifdef DEBUG_IDF
printSeg( seg );
#endif
aOutline.push( seg );
break;
}
}
// the outline is bad; drop the segments
aOutline.Clear();
return;
}
// check if the loop is complete
if( aOutline.front()->MatchesStart( aOutline.back()->endPoint ) )
{
complete = true;
break;
}
}
}
IDF_SEGMENT::IDF_SEGMENT()
{
angle = 0.0;
offsetAngle = 0.0;
radius = 0.0;
}
IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint )
{
angle = 0.0;
offsetAngle = 0.0;
radius = 0.0;
startPoint = aStartPoint;
endPoint = aEndPoint;
}
IDF_SEGMENT::IDF_SEGMENT( const IDF_POINT& aStartPoint,
const IDF_POINT& aEndPoint,
double aAngle,
bool aFromKicad )
{
double diff = abs( aAngle ) - 360.0;
if( ( diff < MIN_ANG
&& diff > -MIN_ANG ) || ( aAngle < MIN_ANG && aAngle > -MIN_ANG ) || (!aFromKicad) )
{
angle = 0.0;
startPoint = aStartPoint;
endPoint = aEndPoint;
if( diff < MIN_ANG && diff > -MIN_ANG )
{
angle = 360.0;
center = aStartPoint;
offsetAngle = 0.0;
radius = aStartPoint.CalcDistance( aEndPoint );
}
else if( aAngle > MIN_ANG || aAngle < -MIN_ANG )
{
angle = aAngle;
CalcCenterAndRadius();
}
return;
}
// we need to convert from the KiCad arc convention
angle = aAngle;
center = aStartPoint;
offsetAngle = IDF3::CalcAngleDeg( aStartPoint, aEndPoint );
radius = aStartPoint.CalcDistance( aEndPoint );
startPoint = aEndPoint;
double ang = offsetAngle + aAngle;
ang = (ang / 180.0) * M_PI;
endPoint.x = ( radius * cos( ang ) ) + center.x;
endPoint.y = ( radius * sin( ang ) ) + center.y;
}
bool IDF_SEGMENT::MatchesStart( const IDF_POINT& aPoint, double aRadius )
{
return startPoint.Matches( aPoint, aRadius );
}
bool IDF_SEGMENT::MatchesEnd( const IDF_POINT& aPoint, double aRadius )
{
return endPoint.Matches( aPoint, aRadius );
}
void IDF_SEGMENT::CalcCenterAndRadius( void )
{
// NOTE: this routine does not check if the points are the same
// or too close to be sensible in a production setting.
double offAng = IDF3::CalcAngleRad( startPoint, endPoint );
double d = startPoint.CalcDistance( endPoint ) / 2.0;
double xm = ( startPoint.x + endPoint.x ) * 0.5;
double ym = ( startPoint.y + endPoint.y ) * 0.5;
radius = d / sin( angle * M_PI / 360.0 );
if( radius < 0.0 )
{
radius = -radius;
}
// calculate the height of the triangle with base d and hypotenuse r
double dh2 = radius * radius - d * d;
if( dh2 < 0 )
{
// this should only ever happen due to rounding errors when r == d
dh2 = 0;
}
double h = sqrt( dh2 );
if( angle > 0.0 )
offAng += M_PI2;
else
offAng -= M_PI2;
if( ( angle > 180.0 ) || ( angle < -180.0 ) )
offAng += M_PI;
center.x = h * cos( offAng ) + xm;
center.y = h * sin( offAng ) + ym;
offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
}
bool IDF_SEGMENT::IsCircle( void )
{
double diff = abs( angle ) - 360.0;
if( ( diff < MIN_ANG ) && ( diff > -MIN_ANG ) )
return true;
return false;
}
double IDF_SEGMENT::GetMinX( void )
{
if( angle == 0.0 )
return std::min( startPoint.x, endPoint.x );
// Calculate the leftmost point of the circle or arc
if( IsCircle() )
{
// if only everything were this easy
return center.x - radius;
}
// cases:
// 1. CCW arc: if offset + included angle >= 180 deg then
// MinX = center.x - radius, otherwise MinX is the
// same as for the case of a line.
// 2. CW arc: if offset + included angle <= -180 deg then
// MinX = center.x - radius, otherwise MinX is the
// same as for the case of a line.
if( angle > 0 )
{
// CCW case
if( ( offsetAngle + angle ) >= 180.0 )
{
return center.x - radius;
}
else
{
return std::min( startPoint.x, endPoint.x );
}
}
// CW case
if( ( offsetAngle + angle ) <= -180.0 )
{
return center.x - radius;
}
return std::min( startPoint.x, endPoint.x );
}
void IDF_SEGMENT::SwapEnds( void )
{
if( IsCircle() )
{
// reverse the direction
angle = -angle;
return;
}
IDF_POINT tmp = startPoint;
startPoint = endPoint;
endPoint = tmp;
if( ( angle < MIN_ANG ) && ( angle > -MIN_ANG ) )
return; // nothing more to do
// change the direction of the arc
angle = -angle;
// calculate the new offset angle
offsetAngle = IDF3::CalcAngleDeg( center, startPoint );
}
bool IDF_OUTLINE::IsCCW( void )
{
// note: when outlines are not valid, 'false' is returned
switch( outline.size() )
{
case 0:
// no outline
return false;
break;
case 1:
// circles are always reported as CCW
if( outline.front()->IsCircle() )
return true;
else
return false;
break;
case 2:
// we may have a closed outline consisting of:
// 1. arc and line, winding depends on the arc
// 2. 2 arcs, winding depends on larger arc
{
double a1 = outline.front()->angle;
double a2 = outline.back()->angle;
if( ( a1 < -MIN_ANG || a1 > MIN_ANG )
&& ( a2 < -MIN_ANG || a2 > MIN_ANG ) )
{
// we have 2 arcs
if( abs( a1 ) >= abs( a2 ) )
{
// winding depends on a1
if( a1 < 0.0 )
return false;
else
return true;
}
else
{
if( a2 < 0.0 )
return false;
else
return true;
}
}
// we may have a line + arc (or 2 lines)
if( a1 < -MIN_ANG )
return false;
if( a1 > MIN_ANG )
return true;
if( a2 < -MIN_ANG )
return false;
if( a2 > MIN_ANG )
return true;
// we have 2 lines (invalid outline)
return false;
}
break;
default:
break;
}
double winding = dir + ( outline.front()->startPoint.x - outline.back()->endPoint.x )
* ( outline.front()->startPoint.y + outline.back()->endPoint.y );
if( winding > 0.0 )
return false;
return true;
}
// returns true if the outline is a circle
bool IDF_OUTLINE::IsCircle( void )
{
if( outline.front()->IsCircle() )
return true;
return false;
}
bool IDF_OUTLINE::push( IDF_SEGMENT* item )
{
if( !outline.empty() )
{
if( item->IsCircle() )
{
// not allowed
ERROR_IDF << "INVALID GEOMETRY\n";
cerr << "* a circle is being added to a non-empty outline\n";
return false;
}
else
{
if( outline.back()->IsCircle() )
{
// we can't add lines to a circle
ERROR_IDF << "INVALID GEOMETRY\n";
cerr << "* a line is being added to a circular outline\n";
return false;
}
else if( !item->MatchesStart( outline.back()->endPoint ) )
{
// startPoint[N] != endPoint[N -1]
ERROR_IDF << "INVALID GEOMETRY\n";
cerr << "* disjoint segments (current start point != last end point)\n";
cerr << "* start point: " << item->startPoint.x << ", " << item->startPoint.y << "\n";
cerr << "* end point: " << outline.back()->endPoint.x << ", " << outline.back()->endPoint.y << "\n";
return false;
}
}
}
outline.push_back( item );
dir += ( outline.back()->endPoint.x - outline.back()->startPoint.x )
* ( outline.back()->endPoint.y + outline.back()->startPoint.y );
return true;
}
/**
* @file idf_common.h
*/
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013-2014 Cirilo Bernardo
*
* 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
*/
#ifndef IDF_COMMON_H
#define IDF_COMMON_H
#include <list>
#include <fstream>
#include <exception>
#include <string>
#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795028841
#endif
#ifndef M_PI2
#define M_PI2 ( M_PI / 2.0 )
#endif
#ifndef M_PI4
#define M_PI4 ( M_PI / 4.0 )
#endif
// differences in angle smaller than MIN_ANG are considered equal
#define MIN_ANG (0.01)
class IDF_POINT;
class IDF_SEGMENT;
class IDF_DRILL_DATA;
class IDF_OUTLINE;
class IDF_LIB;
struct IDF_ERROR : std::exception
{
std::string message;
IDF_ERROR( const char* aSourceFile,
const char* aSourceMethod,
int aSourceLine,
const std::string& aMessage ) throw();
virtual ~IDF_ERROR() throw();
virtual const char* what() const throw();
};
namespace IDF3 {
/**
* ENUM FILE_STATE
* represents state values for the IDF parser's input
*/
enum FILE_STATE
{
FILE_START = 0, // no data has been read; expecting .HEADER
FILE_HEADER, // header has been read; expecting .BOARD_OUTLINE
FILE_OUTLINE, // board outline has been read; most sections can be accepted
FILE_PLACEMENT, // placement has been read; no further sections can be accepted
FILE_INVALID, // file is invalid
FILE_ERROR // other errors while processing the file
};
/**
* ENUM KEY_OWNER
* represents the type of CAD which has ownership an object
*/
enum KEY_OWNER
{
UNOWNED = 0, //< either MCAD or ECAD may modify a feature
MCAD, //< only MCAD may modify a feature
ECAD //< only ECAD may modify a feature
};
/**
* ENUM KEY_HOLETYPE
* represents the purpose of an IDF hole
*/
enum KEY_HOLETYPE
{
PIN = 0, //< drill hole is for a pin
VIA, //< drill hole is for a via
MTG, //< drill hole is for mounting
TOOL, //< drill hole is for tooling
OTHER //< user has specified a custom type
};
/**
* ENUM KEY_PLATING
* represents the plating condition of a hole
*/
enum KEY_PLATING
{
PTH = 0, //< Plate-Through Hole
NPTH //< Non-Plate-Through Hole
};
/**
* ENUM KEY_REFDES
* represents a component's Reference Designator
*/
enum KEY_REFDES
{
BOARD = 0, //< feature is associated with the board
NOREFDES, //< feature is associated with a component with no RefDes
PANEL, //< feature is associated with an IDF panel
REFDES //< reference designator as assigned by the CAD software
};
/**
* ENUM CAD_TYPE
* represents the class of CAD program which is opening or modifying a file
*/
enum CAD_TYPE
{
CAD_ELEC = 0, //< An Electrical CAD is opening/modifying the file
CAD_MECH, //< A Mechanical CAD is opening/modifying the file
CAD_INVALID
};
/**
* ENUM IDF_LAYER
* represents the various IDF layer classes and groupings
*/
enum IDF_LAYER
{
LYR_TOP = 0,
LYR_BOTTOM,
LYR_BOTH,
LYR_INNER,
LYR_ALL,
LYR_INVALID
};
/**
* ENUM OUTLINE_TYPE
* identifies the class of outline
*/
enum OUTLINE_TYPE
{
OTLN_BOARD = 0,
OTLN_OTHER,
OTLN_PLACE,
OTLN_ROUTE,
OTLN_PLACE_KEEPOUT,
OTLN_ROUTE_KEEPOUT,
OTLN_VIA_KEEPOUT,
OTLN_GROUP_PLACE,
OTLN_COMPONENT,
OTLN_INVALID
};
/**
* ENUM COMP_TYPE
* identifies whether a component is a mechanical or electrical part
*/
enum COMP_TYPE
{
COMP_ELEC = 0, //< Component library object is an electrical part
COMP_MECH, //< Component library object is a mechanical part
COMP_INVALID
};
/**
* ENUM IDF_UNIT
* represents the native unit of the board and of component outlines
*/
enum IDF_UNIT
{
UNIT_MM = 0, //< Units in the file are in millimeters
UNIT_THOU, //< Units in the file are in mils (aka thou)
UNIT_INVALID
};
/**
* ENUM IDF_PLACEMENT
* represents the placement status of a component
*/
enum IDF_PLACEMENT
{
PS_UNPLACED = 0, //< component location on the board has not been specified
PS_PLACED, //< component location has been specified and may be modified by ECAD or MCAD
PS_MCAD, //< component location has been specified and may only be modified by MCAD
PS_ECAD, //< component location has been specified and may only be modified by ECAD
PS_INVALID
};
/**
* Function CalcAngleRad
* calculates the angle (radians) between the horizon and the segment aStartPoint to aEndPoint
*
* @param aStartPoint is the start point of a line segment
* @param aEndPoint is the end point of a line segment
*
* @return double: the angle in radians
*/
double CalcAngleRad( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
/**
* Function CalcAngleDeg
* calculates the angle (degrees) between the horizon and the segment aStartPoint to aEndPoint
*
* @param aStartPoint is the start point of a line segment
* @param aEndPoint is the end point of a line segment
*
* @return double: the angle in degrees
*/
double CalcAngleDeg( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
/**
* Function GetOutline
* takes contiguous elements from 'aLines' and stuffs them into 'aOutline'; elements put
* into the outline are deleted from aLines. This function is useful for sorting the jumbled
* mess of line segments and arcs which represent a board outline and cutouts in KiCad.
* The function will determine which segment element within aLines contains the leftmost
* point and retrieve the outline of which that segment is part.
*
* @param aLines (input/output) is a list of IDF segments which comprise an outline and
* cutouts.
* @param aOutline (output) is the ordered set of segments
*/
void GetOutline( std::list<IDF_SEGMENT*>& aLines,
IDF_OUTLINE& aOutline );
#ifdef DEBUG_IDF
// prints out segment information for debug purposes
void PrintSeg( IDF_SEGMENT* aSegment );
#endif
}
/**
* Class IDF_NOTE
* represents an entry in the NOTE section of an IDF file
*/
class IDF_NOTE
{
private:
std::string text; // note text as per IDFv3
double xpos; // text X position as per IDFv3
double ypos; // text Y position as per IDFv3
double height; // text height as per IDFv3
double length; // text length as per IDFv3
public:
IDF_NOTE();
/**
* Function ReadNote
* reads a note entry from an IDFv3 file
*
* @param aBoardFile is an open BOARD file; the file position must be set to the start of a NOTE entry
* @param aBoardState is the parser's current state value
* @param aBoardUnit is the BOARD file's native units (MM or THOU)
*
* @return bool: true if a note item was read, false otherwise. In case of unrecoverable errors
* an exception is thrown
*/
bool ReadNote( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState, IDF3::IDF_UNIT aBoardUnit );
/**
* Function WriteNote
* writes a note entry to an IDFv3 file
*
* @param aBoardFile is an open BOARD file; the file position must be within a NOTE section
* @param aBoardUnit is the BOARD file's native units (MM or THOU)
*
* @return bool: true if the item was successfully written, false otherwise. In case of
* unrecoverable errors an exception is thrown
*/
bool WriteNote( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit );
/**
* Function SetText
* sets the text to be stored as a NOTE entry
*/
void SetText( const std::string& aText );
/**
* Function SetPosition
* sets the position (mm) of the NOTE entry
*/
void SetPosition( double aXpos, double aYpos );
/**
* Function SetSize
* sets the height and length (mm) of the NOTE entry
*/
void SetSize( double aHeight, double aLength );
/**
* Function GetText
* returns the string stored in the note entry
*/
const std::string& GetText( void );
/**
* Function GetText
* returns the position (mm) of the note entry
*/
void GetPosition( double& aXpos, double& aYpos );
/**
* Function GetText
* returns the height and length (mm) of the note entry
*/
void GetSize( double& aHeight, double& aLength );
};
/**
* @Class IDF_DRILL_DATA
* contains information describing a drilled hole and is responsible for
* writing this information to a file in compliance with the IDFv3 specification.
*/
class IDF_DRILL_DATA
{
private:
double dia;
double x;
double y;
IDF3::KEY_PLATING plating;
IDF3::KEY_REFDES kref;
IDF3::KEY_HOLETYPE khole;
std::string refdes;
std::string holetype;
IDF3::KEY_OWNER owner;
public:
/**
* Constructor IDF_DRILL_DATA
* creates an empty drill entry which can be populated by the
* Read() function
*/
IDF_DRILL_DATA();
/**
* Constructor IDF_DRILL_DATA
* creates a drill entry with information compliant with the
* IDFv3 specifications.
* @param aDrillDia : drill diameter
* @param aPosX : X coordinate of the drill center
* @param aPosY : Y coordinate of the drill center
* @param aPlating : flag, PTH or NPTH
* @param aRefDes : component Reference Designator
* @param aHoleType : purpose of hole
* @param aOwner : one of MCAD, ECAD, UNOWNED
*/
IDF_DRILL_DATA( double aDrillDia, double aPosX, double aPosY,
IDF3::KEY_PLATING aPlating,
const std::string aRefDes,
const std::string aHoleType,
IDF3::KEY_OWNER aOwner );
/**
* Function Matches
* returns true if the given drill diameter and location
* matches the diameter and location of this IDF_DRILL_DATA object
*
* @param aDrillDia is the drill diameter (mm)
* @param aPosX is the X position (mm) of the drilled hole
* @param aPosY is the Y position (mm) of the drilled hole
*
* @return bool: true if the diameter and position match this object
*/
bool Matches( double aDrillDia, double aPosX, double aPosY );
/**
* Function Read
* read a drill entry from an IDFv3 file
*
* @param aBoardFile is an open IDFv3 file; the file position must be within the DRILLED_HOLES section
* @param aBoardUnit is the board file's native unit (MM or THOU)
* @param aBoardState is the state value of the parser
*
* @return bool: true if data was successfully read, otherwise false. In case of an
* unrecoverable error an exception is thrown
*/
bool Read( std::ifstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit, IDF3::FILE_STATE aBoardState );
/**
* Function Write
* writes a single line representing a hole within a .DRILLED_HOLES section
*
* @param aBoardFile is an open BOARD file
* @param aBoardUnit is the native unit of the output file
*
* @return bool: true if the data was successfully written, otherwise false. In case of
* an unrecoverable error an exception is thrown
*/
bool Write( std::ofstream& aBoardFile, IDF3::IDF_UNIT aBoardUnit );
/**
* Function GettDrillDia
* returns the drill diameter in mm
*/
double GetDrillDia();
/**
* Function GettDrillXPos
* returns the drill's X position in mm
*/
double GetDrillXPos();
/**
* Function GettDrillYPos
* returns the drill's Y position in mm
*/
double GetDrillYPos();
/**
* Function GetDrillPlating
* returns the plating value (PTH, NPTH)
*/
IDF3::KEY_PLATING GetDrillPlating();
/**
* Function GetDrillRefDes
* returns the reference designator of the hole; this
* may be a component reference designator, BOARD, or
* NOREFDES as per IDFv3.
*/
const std::string& GetDrillRefDes();
/**
* Function GetDrillHoleType
* returns the classification of the hole; this may be one of
* PIN, VIA, MTG, TOOL, or a user-specified string
*/
const std::string& GetDrillHoleType();
};
/**
* @Class IDF_POINT
* represents a point as used by the various IDF related classes
*/
class IDF_POINT
{
public:
double x; // < X coordinate
double y; // < Y coordinate
IDF_POINT()
{
x = 0.0;
y = 0.0;
}
/**
* Function Matches()
* returns true if the given coordinate point is within the given radius
* of the point.
*
* @param aPoint : coordinates of the point being compared
* @param aRadius : radius (mm) within which the points are considered the same
*
* @return bool: true if this point matches the given point
*/
bool Matches( const IDF_POINT& aPoint, double aRadius = 1e-5 );
/**
* Function CalcDistance()
* returns the Euclidean distance between this point and the given point
*
* @param aPoint : coordinates of the point whose distance is to be determined
*
* @return double: distance between this point and aPoint
*/
double CalcDistance( const IDF_POINT& aPoint ) const;
};
/**
* @Class IDF_SEGMENT
* represents a geometry segment as used in IDFv3 outlines; it may be any of
* an arc, line segment, or circle
*/
class IDF_SEGMENT
{
private:
/**
* Function CalcCenterAndRadius()
* Calculates the center, radius, and angle between center and start point given the
* IDF compliant points and included angle.
*
* @var startPoint, @var endPoint, and @var angle must be set prior as per IDFv3
*/
void CalcCenterAndRadius( void );
public:
IDF_POINT startPoint; ///< starting point coordinates in mm
IDF_POINT endPoint; ///< end point coordinates in mm
IDF_POINT center; ///< center of an arc or circle; internally calculated and not to be set by the user
double angle; ///< included angle (degrees) according to IDFv3 specification
double offsetAngle; ///< angle between center and start of arc; internally calculated
double radius; ///< radius of the arc or circle; internally calculated
/**
* Constructor IDF_SEGMENT
* initializes the internal variables
*/
IDF_SEGMENT();
/**
* Function IDF_SEGMENT
* creates a straight segment
*/
IDF_SEGMENT( const IDF_POINT& aStartPoint, const IDF_POINT& aEndPoint );
/**
* Constructor IDF_SEGMENT
* creates a straight segment, arc, or circle depending on the angle
*
* @param aStartPoint : start point (center if using KiCad convention, otherwise IDF convention)
* @param aEndPoint : end point (start of arc if using KiCad convention, otherwise IDF convention)
* @param aAngle : included angle; the KiCad convention is equivalent to the IDF convention
* @param fromKicad : set true if we need to convert from KiCad to IDF convention
*/
IDF_SEGMENT( const IDF_POINT& aStartPoint,
const IDF_POINT& aEndPoint,
double aAngle,
bool aFromKicad );
/**
* Function MatchesStart
* returns true if the given coordinate is within a radius 'rad'
* of the start point.
*
* @param aPoint : coordinates of the point (mm) being compared
* @param aRadius : radius (mm) within which the points are considered the same
*
* @return bool: true if the given point matches the start point of this segment
*/
bool MatchesStart( const IDF_POINT& aPoint, double aRadius = 1e-3 );
/**
* Function MatchesEnd
* returns true if the given coordinate is within a radius 'rad'
* of the end point.
*
* @param aPoint : coordinates (mm) of the point being compared
* @param aRadius : radius (mm) within which the points are considered the same
*
* @return bool: true if the given point matches the end point of this segment
*/
bool MatchesEnd( const IDF_POINT& aPoint, double aRadius = 1e-3 );
/**
* Function IsCircle
* returns true if this segment is a circle
*/
bool IsCircle( void );
/**
* Function GetMinX()
* returns the minimum X coordinate of this segment
*/
double GetMinX( void );
/**
* Function SwapEnds()
* Swaps the start and end points and alters internal
* variables as necessary for arcs
*/
void SwapEnds( void );
};
/**
* @Class IDF_OUTLINE
* contains segment and winding information for an IDF outline
*/
class IDF_OUTLINE
{
private:
double dir; // accumulator to help determine winding direction
std::list<IDF_SEGMENT*> outline; // sequential segments comprising an outline
public:
IDF_OUTLINE() { dir = 0.0; }
~IDF_OUTLINE() { Clear(); }
/**
* Function IsCCW
* returns true if the current list of points represents a counterclockwise winding
*/
bool IsCCW( void );
/**
* Function IsCircle
* returns true if this outline is a circle
*/
bool IsCircle( void );
/**
* Function Clear
* clears the internal list of outline segments
*/
void Clear( void )
{
dir = 0.0;
while( !outline.empty() )
{
delete outline.front();
outline.pop_front();
}
}
/**
* Function size
* returns the size of the internal segment list
*/
size_t size( void )
{
return outline.size();
}
/**
* Function empty
* returns true if the internal segment list is empty
*/
bool empty( void )
{
return outline.empty();
}
/**
* Function front
* returns the front() iterator of the internal segment list
*/
IDF_SEGMENT*& front( void )
{
return outline.front();
}
/**
* Function back
* returns the back() iterator of the internal segment list
*/
IDF_SEGMENT*& back( void )
{
return outline.back();
}
/**
* Function begin
* returns the begin() iterator of the internal segment list
*/
std::list<IDF_SEGMENT*>::iterator begin( void )
{
return outline.begin();
}
/**
* Function end
* returns the end() iterator of the internal segment list
*/
std::list<IDF_SEGMENT*>::iterator end( void )
{
return outline.end();
}
/**
* Function push
* adds a segment to the internal segment list; segments must be added
* in order so that startPoint[N] == endPoint[N - 1]
*
* @param item is a pointer to the segment to add to the outline
*
* @return bool: true if the segment was added, otherwise false
* (outline restrictions have been violated)
*/
bool push( IDF_SEGMENT* item );
};
#endif // IDF_COMMON_H
......@@ -149,7 +149,9 @@ int main( int argc, char **argv )
tstr.clear();
tstr.str( line );
if( (tstr >> dia) && dia > 0.0 )
tstr >> dia;
if( !tstr.fail() && dia > 0.0 )
ok = true;
}
......@@ -163,7 +165,9 @@ int main( int argc, char **argv )
tstr.clear();
tstr.str( line );
if( (tstr >> length) && length > 0.0 )
tstr >> length;
if( !tstr.fail() && length > 0.0 )
ok = true;
}
......@@ -177,7 +181,9 @@ int main( int argc, char **argv )
tstr.clear();
tstr.str( line );
if( (tstr >> extraZ) && extraZ >= 0.0 )
tstr >> extraZ;
if( !tstr.fail() && extraZ >= 0.0 )
ok = true;
}
......@@ -191,7 +197,9 @@ int main( int argc, char **argv )
tstr.clear();
tstr.str( line );
if( (tstr >> wireDia) && wireDia > 0.0 )
tstr >> wireDia;
if( !tstr.fail() && wireDia > 0.0 )
{
if( wireDia < dia )
ok = true;
......@@ -236,7 +244,9 @@ void make_vcyl( bool inch, bool axial, double dia, double length,
tstr.clear();
tstr.str( line );
if( (tstr >> pitch) && pitch > 0.0 )
tstr >> pitch;
if( !tstr.fail() && pitch > 0.0 )
{
if( (pitch - wireDia) <= (dia / 2.0) )
{
......@@ -436,7 +446,9 @@ void make_hcyl( bool inch, bool axial, double dia, double length,
tstr.clear();
tstr.str( line );
if( (tstr >> pitch) && pitch > 0.0 )
tstr >> pitch;
if( !tstr.fail() && pitch > 0.0 )
{
if( axial )
{
......@@ -477,7 +489,9 @@ void make_hcyl( bool inch, bool axial, double dia, double length,
tstr.clear();
tstr.str( line );
if( (tstr >> lead) && lead > 0.0 )
tstr >> lead;
if( !tstr.fail() && lead > 0.0 )
{
if( lead < wireDia )
cout << "* WARNING: lead length must be >= wireDia\n";
......
.HEADER
BOARD_FILE 3.0 "Created by KiCad (2014-01-21 BZR 4629)-product" 2014/01/23.09:19:46 1
"Arduino_MEGA_2560-Rev3.kicad_pcb" MM
.END_HEADER
.BOARD_OUTLINE ECAD
1.60000
0 246.20220 -25.67000 0
0 247.20220 -26.67000 0
0 344.26220 -26.67000 0
0 345.26220 -25.67000 0
0 345.26220 -25.40000 0
0 347.80220 -22.86000 0
0 347.80220 11.43000 0
0 345.26220 13.97000 0
0 345.26220 24.13000 0
0 342.72220 26.67000 0
0 247.20220 26.67000 0
0 246.20220 25.67000 0
0 246.20220 -25.67000 0
.END_BOARD_OUTLINE
.DRILLED_HOLES
3.200 342.72220 -24.13000 NPTH "@HOLE0" MTG ECAD
3.200 261.44220 24.13000 NPTH "@HOLE1" MTG ECAD
3.200 336.37220 24.13000 NPTH "@HOLE2" MTG ECAD
3.200 260.17220 -24.13000 NPTH "@HOLE3" MTG ECAD
3.200 312.24220 8.89000 NPTH "@HOLE4" MTG ECAD
3.200 312.24220 -19.05000 NPTH "@HOLE5" MTG ECAD
0.950 309.82920 3.81000 PTH "ICSP" PIN ECAD
0.950 312.36920 3.81000 PTH "ICSP" PIN ECAD
0.950 309.82920 1.27000 PTH "ICSP" PIN ECAD
0.950 312.36920 1.27000 PTH "ICSP" PIN ECAD
0.950 309.82920 -1.27000 PTH "ICSP" PIN ECAD
0.950 312.36920 -1.27000 PTH "ICSP" PIN ECAD
0.850 309.70220 24.13000 PTH "PWML" PIN ECAD
0.850 307.16220 24.13000 PTH "PWML" PIN ECAD
0.850 304.62220 24.13000 PTH "PWML" PIN ECAD
0.850 302.08220 24.13000 PTH "PWML" PIN ECAD
0.850 299.54220 24.13000 PTH "PWML" PIN ECAD
0.850 297.00220 24.13000 PTH "PWML" PIN ECAD
0.850 294.46220 24.13000 PTH "PWML" PIN ECAD
0.850 291.92220 24.13000 PTH "PWML" PIN ECAD
1.400 254.88900 -23.36800 PTH "X1" PIN ECAD
1.400 257.88620 -18.36420 PTH "X1" PIN ECAD
1.400 251.89180 -18.36420 PTH "X1" PIN ECAD
1.400 255.65100 -23.36800 PTH "X1" PIN ECAD
1.400 254.12700 -23.36800 PTH "X1" PIN ECAD
1.400 251.89180 -19.12620 PTH "X1" PIN ECAD
1.400 251.89180 -17.60220 PTH "X1" PIN ECAD
1.400 257.88620 -19.12620 PTH "X1" PIN ECAD
1.400 257.88620 -17.60220 PTH "X1" PIN ECAD
0.850 297.00220 -24.13000 PTH "ADCL" PIN ECAD
0.850 299.54220 -24.13000 PTH "ADCL" PIN ECAD
0.850 302.08220 -24.13000 PTH "ADCL" PIN ECAD
0.850 304.62220 -24.13000 PTH "ADCL" PIN ECAD
0.850 307.16220 -24.13000 PTH "ADCL" PIN ECAD
0.850 309.70220 -24.13000 PTH "ADCL" PIN ECAD
0.850 312.24220 -24.13000 PTH "ADCL" PIN ECAD
0.850 314.78220 -24.13000 PTH "ADCL" PIN ECAD
0.850 332.56220 24.13000 PTH "COMMUNICATION" PIN ECAD
0.850 330.02220 24.13000 PTH "COMMUNICATION" PIN ECAD
0.850 327.48220 24.13000 PTH "COMMUNICATION" PIN ECAD
0.850 324.94220 24.13000 PTH "COMMUNICATION" PIN ECAD
0.850 322.40220 24.13000 PTH "COMMUNICATION" PIN ECAD
0.850 319.86220 24.13000 PTH "COMMUNICATION" PIN ECAD
0.850 317.32220 24.13000 PTH "COMMUNICATION" PIN ECAD
0.850 314.78220 24.13000 PTH "COMMUNICATION" PIN ECAD
0.850 319.86220 -24.13000 PTH "ADCH" PIN ECAD
0.850 322.40220 -24.13000 PTH "ADCH" PIN ECAD
0.850 324.94220 -24.13000 PTH "ADCH" PIN ECAD
0.850 327.48220 -24.13000 PTH "ADCH" PIN ECAD
0.850 330.02220 -24.13000 PTH "ADCH" PIN ECAD
0.850 332.56220 -24.13000 PTH "ADCH" PIN ECAD
0.850 335.10220 -24.13000 PTH "ADCH" PIN ECAD
0.850 337.64220 -24.13000 PTH "ADCH" PIN ECAD
0.950 254.72220 10.18000 PTH "X2" PIN ECAD
0.950 254.72220 12.68000 PTH "X2" PIN ECAD
0.950 252.72220 12.68000 PTH "X2" PIN ECAD
0.950 252.72220 10.18000 PTH "X2" PIN ECAD
2.200 250.01220 17.43000 PTH "X2" PIN ECAD
2.200 250.01220 5.43000 PTH "X2" PIN ECAD
0.950 267.03020 20.82800 PTH "ICSP1" PIN ECAD
0.950 267.03020 18.28800 PTH "ICSP1" PIN ECAD
0.950 264.49020 20.82800 PTH "ICSP1" PIN ECAD
0.950 264.49020 18.28800 PTH "ICSP1" PIN ECAD
0.950 261.95020 20.82800 PTH "ICSP1" PIN ECAD
0.950 261.95020 18.28800 PTH "ICSP1" PIN ECAD
0.850 267.15720 -0.63500 PTH "Y2" PIN ECAD
0.850 262.07720 -0.63500 PTH "Y2" PIN ECAD
0.950 267.03020 13.20800 PTH "JP5" PIN ECAD
0.950 264.49020 13.20800 PTH "JP5" PIN ECAD
0.950 267.03020 15.74800 PTH "JP5" PIN ECAD
0.950 264.49020 15.74800 PTH "JP5" PIN ECAD
0.950 340.18220 24.13000 PTH "XIO" PIN ECAD
0.950 342.72220 24.13000 PTH "XIO" PIN ECAD
0.950 340.18220 21.59000 PTH "XIO" PIN ECAD
0.950 342.72220 21.59000 PTH "XIO" PIN ECAD
0.950 340.18220 19.05000 PTH "XIO" PIN ECAD
0.950 342.72220 19.05000 PTH "XIO" PIN ECAD
0.950 340.18220 16.51000 PTH "XIO" PIN ECAD
0.950 342.72220 16.51000 PTH "XIO" PIN ECAD
0.950 340.18220 13.97000 PTH "XIO" PIN ECAD
0.950 342.72220 13.97000 PTH "XIO" PIN ECAD
0.950 340.18220 11.43000 PTH "XIO" PIN ECAD
0.950 342.72220 11.43000 PTH "XIO" PIN ECAD
0.950 340.18220 8.89000 PTH "XIO" PIN ECAD
0.950 342.72220 8.89000 PTH "XIO" PIN ECAD
0.950 340.18220 6.35000 PTH "XIO" PIN ECAD
0.950 342.72220 6.35000 PTH "XIO" PIN ECAD
0.950 340.18220 3.81000 PTH "XIO" PIN ECAD
0.950 342.72220 3.81000 PTH "XIO" PIN ECAD
0.950 340.18220 1.27000 PTH "XIO" PIN ECAD
0.950 342.72220 1.27000 PTH "XIO" PIN ECAD
0.950 340.18220 -1.27000 PTH "XIO" PIN ECAD
0.950 342.72220 -1.27000 PTH "XIO" PIN ECAD
0.950 340.18220 -3.81000 PTH "XIO" PIN ECAD
0.950 342.72220 -3.81000 PTH "XIO" PIN ECAD
0.950 340.18220 -6.35000 PTH "XIO" PIN ECAD
0.950 342.72220 -6.35000 PTH "XIO" PIN ECAD
0.950 340.18220 -8.89000 PTH "XIO" PIN ECAD
0.950 342.72220 -8.89000 PTH "XIO" PIN ECAD
0.950 340.18220 -11.43000 PTH "XIO" PIN ECAD
0.950 342.72220 -11.43000 PTH "XIO" PIN ECAD
0.950 340.18220 -13.97000 PTH "XIO" PIN ECAD
0.950 342.72220 -13.97000 PTH "XIO" PIN ECAD
0.950 340.18220 -16.51000 PTH "XIO" PIN ECAD
0.950 342.72220 -16.51000 PTH "XIO" PIN ECAD
0.950 340.18220 -19.05000 PTH "XIO" PIN ECAD
0.950 342.72220 -19.05000 PTH "XIO" PIN ECAD
0.850 264.99820 24.13000 PTH "JP6" PIN ECAD
0.850 267.53820 24.13000 PTH "JP6" PIN ECAD
0.850 270.07820 24.13000 PTH "JP6" PIN ECAD
0.850 272.61820 24.13000 PTH "JP6" PIN ECAD
0.850 275.15820 24.13000 PTH "JP6" PIN ECAD
0.850 277.69820 24.13000 PTH "JP6" PIN ECAD
0.850 280.23820 24.13000 PTH "JP6" PIN ECAD
0.850 282.77820 24.13000 PTH "JP6" PIN ECAD
0.850 285.31820 24.13000 PTH "JP6" PIN ECAD
0.850 287.85820 24.13000 PTH "JP6" PIN ECAD
0.850 274.14220 -24.13000 PTH "POWER" PIN ECAD
0.850 276.68220 -24.13000 PTH "POWER" PIN ECAD
0.850 279.22220 -24.13000 PTH "POWER" PIN ECAD
0.850 281.76220 -24.13000 PTH "POWER" PIN ECAD
0.850 284.30220 -24.13000 PTH "POWER" PIN ECAD
0.850 286.84220 -24.13000 PTH "POWER" PIN ECAD
0.850 289.38220 -24.13000 PTH "POWER" PIN ECAD
0.850 291.92220 -24.13000 PTH "POWER" PIN ECAD
.END_DRILLED_HOLES
.HEADER
LIBRARY_FILE 3.0 "Created by KiCad (2014-01-21 BZR 4629)-product" 2014/01/23.09:19:46 1
.END_HEADER
.HEADER
BOARD_FILE 3.0 "Sample File Generator" 10/22/96.16:02:44 1
sample_board THOU
.END_HEADER
# This is the first BOARD section
# SEC1-0
# SEC1-1
.BOARD_OUTLINE MCAD
62.0
0 5030.5 -120.0 0.0
0 5187.5 -120.0 0.0
0 5187.5 2130.0 0.0
0 5155.0 2130.0 0.0
0 5155.0 2550.0 -180.0
0 5187.5 2550.0 0.0
0 5187.5 4935.0 0.0
0 4945.0 5145.0 0.0
0 4945.0 5420.0 0.0
0 4865.0 5500.0 0.0
0 210.0 5500.0 0.0
0 130.0 5420.0 0.0
0 130.0 5145.0 0.0
0 -112.5 4935.0 0.0
0 -112.5 2550.0 0.0
0 -80.0 2550.0 0.0
0 -80.0 2130.0 -180.0
0 -112.5 2130.0 0.0
0 -112.5 -140.0 0.0
0 45.5 -140.0 0.0
0 45.5 -400.0 0.0
0 2442.5 -400.0 0.0
0 2442.5 -140.0 0.0
0 2631.5 -140.0 0.0
0 2631.5 -400.0 0.0
0 5030.5 -400.0 0.0
0 5030.5 -120.0 0.0
1 2650.0 2350.0 0.0
1 3000.0 2350.0 360.0
.END_BOARD_OUTLINE
# This is the second BOARD section
# SEC2-0
# SEC2-1
# NOT SEC1-1
.ROUTE_OUTLINE ECAD
ALL
0 5112.5 150.0 0.0
0 5112.5 2058.2 0.0
0 5112.5 2621.8 -162.9
0 5112.5 4863.2 0.0
0 4878.8 5075.0 0.0
0 226.4 5075.0 0.0
0 138.0 4910.3 0.0
0 138.0 4800.0 0.0
0 -37.5 4662.5 0.0
0 -37.5 2621.8 0.0
0 -37.5 2058.2 -162.9
0 -37.5 150.0 0.0
0 162.5 0.0 0.0
0 4912.5 0.0 0.0
0 5112.5 150.0 0.0
.END_ROUTE_OUTLINE
# This is the third BOARD section
# SEC3-0
# SEC3-1
.PLACE_OUTLINE MCAD
TOP 1000.0
0 5080.0 2034.9 0.0
0 5080.0 2645.1 -152.9
0 5080.0 4837.3 0.0
0 4855.3 5042.5 0.0
0 252.9 5042.5 0.0
0 170.5 4896.9 0.0
0 170.5 4798.4 0.0
0 -5.0 4659.0 0.0
0 -5.0 2645.1 0.0
0 -5.0 2034.9 -152.9
0 -5.0 182.5 0.0
0 192.0 32.5 0.0
0 4883.1 32.5 0.0
0 5080.0 182.5 0.0
0 5080.0 2034.9 0.0
.END_PLACE_OUTLINE
# This is the fourth BOARD section
# SEC4-0
# SEC4-1
.PLACE_OUTLINE UNOWNED
BOTTOM 200.0
0 300.0 200.0 0.0
0 4800.0 200.0 0.0
0 4800.0 4800.0 0.0
0 300.0 4800.0 0.0
0 300.0 200.0 0.0
.END_PLACE_OUTLINE
# This is the fifth BOARD section
# SEC5-0
# SEC5-1
.ROUTE_KEEPOUT ECAD
ALL
0 2650.0 2350.0 0.0
0 3100.0 2350.0 360.0
.END_ROUTE_KEEPOUT
# This is the sixth BOARD section
# SEC6-0
# SEC6-1
.PLACE_KEEPOUT MCAD
BOTH 0.0
0 2650.0 2350.0 0.0
0 3100.0 2350.0 360.0
.END_PLACE_KEEPOUT
# This is the seventh BOARD section
# SEC7-0
# SEC7-1
.PLACE_KEEPOUT MCAD
TOP 300.0
0 3700.0 5000.0 0.0
0 3700.0 4300.0 0.0
0 4000.0 4300.0 0.0
0 4000.0 3700.0 0.0
0 5000.0 3700.0 0.0
0 5000.0 4800.0 0.0
0 4800.0 5000.0 0.0
0 3700.0 5000.0 0.0
.END_PLACE_KEEPOUT
# This is the eighth BOARD section
# SEC8-0
# SEC8-1
.DRILLED_HOLES
30.0 1800.0 100.0 PTH J1 PIN ECAD
30.0 1700.0 100.0 PTH J1 PIN ECAD
30.0 1600.0 100.0 PTH J1 PIN ECAD
30.0 1500.0 100.0 PTH J1 PIN ECAD
30.0 1400.0 100.0 PTH J1 PIN ECAD
30.0 1300.0 100.0 PTH J1 PIN ECAD
30.0 1200.0 100.0 PTH J1 PIN ECAD
30.0 1100.0 100.0 PTH J1 PIN ECAD
30.0 1000.0 100.0 PTH J1 PIN ECAD
30.0 0900.0 100.0 PTH J1 PIN ECAD
30.0 0800.0 100.0 PTH J1 PIN ECAD
30.0 0700.0 100.0 PTH J1 PIN ECAD
30.0 0700.0 200.0 PTH J1 PIN ECAD
30.0 0800.0 200.0 PTH J1 PIN ECAD
30.0 0900.0 200.0 PTH J1 PIN ECAD
30.0 1000.0 200.0 PTH J1 PIN ECAD
30.0 1100.0 200.0 PTH J1 PIN ECAD
30.0 1200.0 200.0 PTH J1 PIN ECAD
30.0 1300.0 200.0 PTH J1 PIN ECAD
30.0 1400.0 200.0 PTH J1 PIN ECAD
30.0 1500.0 200.0 PTH J1 PIN ECAD
30 1600 200 PTH J1 PIN ECAD
30 1700 200 PTH J1 PIN ECAD
30 1800 200 PTH J1 PIN ECAD
30 4400 100 PTH J2 PIN ECAD
30 4300 100 PTH J2 PIN ECAD
30 4200 100 PTH J2 PIN ECAD
30 4100 100 PTH J2 PIN ECAD
30 4000 100 PTH J2 PIN ECAD
30 3900 100 PTH J2 PIN ECAD
30 3800 100 PTH J2 PIN ECAD
30 3700 100 PTH J2 PIN ECAD
30 3600 100 PTH J2 PIN ECAD
30 3500 100 PTH J2 PIN ECAD
30 3400 100 PTH J2 PIN ECAD
30 3300 100 PTH J2 PIN ECAD
30 3300 200 PTH J2 PIN ECAD
30 3400 200 PTH J2 PIN ECAD
30 3500 200 PTH J2 PIN ECAD
30 3600 200 PTH J2 PIN ECAD
30 3700 200 PTH J2 PIN ECAD
30 3800 200 PTH J2 PIN ECAD
30 3900 200 PTH J2 PIN ECAD
30 4000 200 PTH J2 PIN ECAD
30 4100 200 PTH J2 PIN ECAD
30 4200 200 PTH J2 PIN ECAD
30 4300 200 PTH J2 PIN ECAD
30 4400 200 PTH J2 PIN ECAD
30 3000 3300 PTH U3 PIN ECAD
30 3024.2 3203 PTH U3 PIN ECAD
30 3048.4 3105.9 PTH U3 PIN ECAD
30 3072.6 3008.9 PTH U3 PIN ECAD
30 3096.8 2911.9 PTH U3 PIN ECAD
30 3121 2814.9 PTH U3 PIN ECAD
30 3145.2 2717.8 PTH U3 PIN ECAD
30 3436.2 2790.4 PTH U3 PIN ECAD
30 3412.1 2887.4 PTH U3 PIN ECAD
30 3387.9 2984.5 PTH U3 PIN ECAD
30 3363.7 3081.5 PTH U3 PIN ECAD
30 3339.5 3178.5 PTH U3 PIN ECAD
30 3315.3 3275.6 PTH U3 PIN ECAD
30 3291.1 3372.6 PTH U3 PIN ECAD
30 2200 2500 PTH U4 PIN ECAD
30 2100 2500 PTH U4 PIN ECAD
30 2000 2500 PTH U4 PIN ECAD
30 1900 2500 PTH U4 PIN ECAD
30 1800 2500 PTH U4 PIN ECAD
30 1700 2500 PTH U4 PIN ECAD
30 1600 2500 PTH U4 PIN ECAD
30 1600 2200 PTH U4 PIN ECAD
30 1700 2200 PTH U4 PIN ECAD
30 1800 2200 PTH U4 PIN ECAD
30 1900 2200 PTH U4 PIN ECAD
30 2000 2200 PTH U4 PIN ECAD
30 2100 2200 PTH U4 PIN ECAD
30 2200 2200 PTH U4 PIN ECAD
20 2500 3100 PTH BOARD VIA ECAD
20 2500 3200 PTH BOARD VIA ECAD
20 2500 3300 PTH BOARD VIA ECAD
20 2000 1600 PTH BOARD VIA ECAD
20 1100 900 PTH BOARD VIA ECAD
20 1200 1600 PTH BOARD VIA ECAD
20 3900 3800 PTH BOARD VIA ECAD
20 3900 2300 PTH BOARD VIA ECAD
100.0 3100.0 -50.0 NPTH J2 MTG ECAD
100.0 4600.0 -50.0 NPTH J2 MTG ECAD
100.0 500.0 -50.0 NPTH J1 MTG ECAD
100.0 2000.0 -50.0 NPTH J1 MTG ECAD
93.0 5075.0 0.0 PTH BOARD MTG UNOWNED
93.0 0.0 4800.0 NPTH BOARD TOOL MCAD
93.0 0.0 0.0 PTH BOARD MTG UNOWNED
.END_DRILLED_HOLES
# This is the ninth BOARD section
# SEC9-0
# SEC9-1
.NOTES
3500.0 3300.0 75.0 2500.0 "This component rotated 14 degrees"
400.0 4400.0 75.0 3200.0 "Component height limited by enclosure latch"
1800.0 300.0 75.0 1700.0 "Do not move connectors!"
.END_NOTES
# This is the tenth and ALWAYS FINAL BOARD section
# SEC10-0
# SEC10-1
.PLACEMENT
cs13_a pn-cap C1
4000.0 1000.0 100.0 0.0 TOP PLACED
cc1210 pn-cc1210 C2
3000.0 3500.0 0.0 0.0 TOP PLACED
cc1210 pn-cc1210 C3
3200.0 1800.0 0.0 0.0 BOTTOM PLACED
cc1210 pn-cc1210 C4
1400.0 2300.0 0.0 270.0 TOP PLACED
cc1210 pn-cc1210 C5
1799.5 3518.1 0.0 0.0 BOTTOM PLACED
conn_din24 connector J1
1800.0 100.0 0.0 0.0 TOP MCAD
conn_din24 connector J2
4400.0 100.0 0.0 0.0 TOP MCAD
plcc_20 pn-pal16l8-plcc U1
1800.0 3200.0 0.0 0.0 BOTTOM ECAD
plcc_20 pn-pal16l8-plcc U2
3200.0 1800.0 0.0 0.0 TOP PLACED
dip_14w pn-hs346-dip U3
3000.0 3300.0 0.0 14.0 TOP PLACED
dip_14w pn-hs346-dip U4
2200.0 2500.0 0.0 270.0 TOP PLACED
.END_PLACEMENT
.HEADER
LIBRARY_file 3.0 "Sample File Generator" 10/22/96.16:41:37 1
.END_HEADER
# Component #1/5
.ELECTRICAL
cs13_a pn-cap THOU 150.0
0 -55.0 55.0 0.0
0 -55.0 -55.0 0.0
0 135.0 -55.0 0.0
0 135.0 -80.0 0.0
0 565.0 -80.0 0.0
0 565.0 -55.0 0.0
0 755.0 -55.0 0.0
0 755.0 55.0 0.0
0 565.0 55.0 0.0
0 565.0 80.0 0.0
0 135.0 80.0 0.0
0 135.0 55.0 0.0
0 -55.0 55.0 0.0
PROP CAPACITANCE 100.0
PROP TOLERANCE 5.0
.END_ELECTRICAL
# Component #2/5
.ELECTRICAL
cc1210 pn-cc1210 THOU 67.0
0 -40.0 56.0 0.0
0 -40.0 -56.0 0.0
0 182.0 -56.0 0.0
0 182.0 56.0 0.0
0 -40.0 56.0 0.0
PROP CAPACITANCE 0.1
PROP TOLERANCE 5.0
.END_ELECTRICAL
# Component #3/5
.ELECTRICAL
conn_din24 connector THOU 435.0
0 -1400.0 -500.0 0.0
0 300.0 -500.0 0.0
0 300.0 150.0 0.0
0 -1400.0 150.0 0.0
0 -1400.0 -500.0 0.0
.END_ELECTRICAL
# Component #4/5
.ELECTRICAL
dip_14w pn-hs346-dip THOU 200.0
0 350.0 50.0 0.0
0 -50.0 50.0 0.0
0 -50.0 -650.0 0.0
0 350.0 -650.0 0.0
0 350.0 50.0 0.0
.END_ELECTRICAL
# Component #5/5
.ELECTRICAL
plcc_20 pn-pal16l8-plcc THOU 14.0
0 -200.0 240.0 0.0
0 -240.0 200.0 0.0
0 -240.0 -240.0 0.0
0 240.0 -240.0 0.0
0 240.0 240.0 0.0
0 -200.0 240.0 0.0
.END_ELECTRICAL
.HEADER
BOARD_FILE 3.0 "Created by KiCad (2014-01-25 BZR 4633)-product" 2014/02/01.15:09:15 1
"test_idf2.kicad_pcb" MM
.END_HEADER
.BOARD_OUTLINE ECAD
1.60000
0 -86.00000 42.00000 0
0 -86.00000 -42.00000 0
0 86.00000 -42.00000 0
0 86.00000 42.00000 0
0 -86.00000 42.00000 0
.END_BOARD_OUTLINE
.DRILLED_HOLES
0.800 -74.00000 16.00000 PTH BOARD PIN ECAD
0.800 -74.00000 -28.00000 PTH BOARD PIN ECAD
0.850 -55.75000 16.00000 PTH BOARD PIN ECAD
0.850 -52.25000 16.00000 PTH BOARD PIN ECAD
0.850 -35.75000 16.00000 PTH BOARD PIN ECAD
0.850 -32.25000 16.00000 PTH BOARD PIN ECAD
1.575 -57.17500 -28.00000 PTH BOARD PIN ECAD
1.575 -50.82500 -28.00000 PTH BOARD PIN ECAD
1.575 -37.17500 -28.00000 PTH BOARD PIN ECAD
1.575 -30.82500 -28.00000 PTH BOARD PIN ECAD
0.800 -14.00000 16.00000 PTH BOARD PIN ECAD
0.800 -14.00000 -28.00000 PTH BOARD PIN ECAD
0.800 6.00000 16.00000 PTH BOARD PIN ECAD
0.800 6.00000 -28.00000 PTH BOARD PIN ECAD
0.800 26.00000 16.00000 PTH BOARD PIN ECAD
0.800 26.00000 -28.00000 PTH BOARD PIN ECAD
0.800 46.00000 16.00000 PTH BOARD PIN ECAD
0.800 46.00000 -28.00000 PTH BOARD PIN ECAD
0.800 66.00000 16.00000 PTH BOARD PIN ECAD
0.800 66.00000 -28.00000 PTH BOARD PIN ECAD
.END_DRILLED_HOLES
.PLACEMENT
"CYLV_MM" "D5.000_H8.000_Z3.000" "NOREFDES_0"
-74.000000 16.000000 0.000000 0.000 TOP ECAD
"CYLV_IN" "D0.250_H0.250_Z0.127" "NOREFDES_1"
-74.000000 -28.000000 0.000000 0.000 TOP ECAD
"CYLV_MM_L" "D5.000_H8.000_Z3.000_WD0.800_P3.500" "NOREFDES_2"
-54.000000 16.000000 0.000000 0.000 TOP ECAD
"CYLV_MM_R" "D5.000_H8.000_Z3.000_WD0.800_P3.500" "NOREFDES_3"
-34.000000 16.000000 0.000000 0.000 TOP ECAD
"CYLV_IN_L" "D0.250_H0.250_Z0.127_WD0.062_P0.250" "NOREFDES_4"
-54.000000 -28.000000 0.000000 0.000 TOP ECAD
"CYLV_IN_R" "D0.250_H0.250_Z0.127_WD0.062_P0.250" "NOREFDES_5"
-34.000000 -28.000000 0.000000 0.000 TOP ECAD
"CYLH_MM_AXI" "D2.500_H4.000_Z0.500_WD0.600_P8.000" "NOREFDES_6"
-14.000000 16.000000 0.000000 0.000 TOP ECAD
"CYLH_IN_AXI" "D0.098_H0.157_Z0.020_WD0.024_P0.315" "NOREFDES_7"
-14.000000 -28.000000 0.000000 0.000 TOP ECAD
"CYLH_MM_RAD" "D5.000_H6.000_Z0.200_WD0.600_P2.500_L3.000" "NOREFDES_8"
6.000000 16.000000 0.000000 0.000 TOP ECAD
"CYLH_IN_RAD" "D0.197_H0.236_Z0.008_WD0.024_P0.098_L0.118" "NOREFDES_9"
6.000000 -28.000000 0.000000 0.000 TOP ECAD
"RECTMM" "W10.000_L10.000_H6.000_C0.000" "NOREFDES_10"
26.000000 16.000000 0.000000 0.000 TOP ECAD
"RECTIN" "W393_L393_H236_C0" "NOREFDES_11"
26.000000 -28.000000 0.000000 0.000 TOP ECAD
"RECTMM" "W10.000_L10.000_H2.000_C0.500" "NOREFDES_12"
46.000000 16.000000 0.000000 0.000 TOP ECAD
"RECTIN" "W393_L393_H78_C19" "NOREFDES_13"
46.000000 -28.000000 0.000000 0.000 TOP ECAD
"RECTLMM" "W10.000_L10.000_H12.000_D0.800_P6.000" "NOREFDES_14"
66.000000 16.000000 0.000000 0.000 TOP ECAD
"RECTLIN" "W393_L393_H472_D31_P236" "NOREFDES_15"
66.000000 -28.000000 0.000000 0.000 TOP ECAD
.END_PLACEMENT
.HEADER
LIBRARY_FILE 3.0 "Created by KiCad (2014-01-25 BZR 4633)-product" 2014/02/01.15:09:15 1
.END_HEADER
# cylindrical outline, vertical, no pins
# file: "cylvmm_0_D5_L8_Z3.idf"
# dia: 5.000 mm
# length: 8.000 mm
# extra height: 3.000 mm
.ELECTRICAL
"CYLV_MM" "D5.000_H8.000_Z3.000" MM 11.000
0 0 0 0
0 5.000 0 360
.END_ELECTRICAL
# cylindrical outline, vertical, no pins
# file: "cylvin_0_D0.25_L0.25_Z0.127.idf"
# dia: 250 THOU
# length: 250 THOU
# extra height: 127 THOU
.ELECTRICAL
"CYLV_IN" "D0.250_H0.250_Z0.127" THOU 377
0 0 0 0
0 250 0 360
.END_ELECTRICAL
# cylindrical outline, vertical, 1 pin on left
# file: "cylvmm_1L_D5_L8_Z3_WD0.8_P3.5.idf"
# dia: 5.000 mm
# length: 8.000 mm
# extra height: 3.000 mm
# wire dia: 0.800 mm
# pitch: 3.500 mm
.ELECTRICAL
"CYLV_MM_L" "D5.000_H8.000_Z3.000_WD0.800_P3.500" MM 11.000
1 -0.718 0.400 0
1 -0.718 -0.400 -341.586
1 -1.750 -0.400 0
1 -1.750 0.400 -180
1 -0.718 0.400 0
.END_ELECTRICAL
# cylindrical outline, vertical, 1 pin on right
# file: "cylvmm_1R_D5_L8_Z3_WD0.8_P3.5.idf"
# dia: 5.000 mm
# length: 8.000 mm
# extra height: 3.000 mm
# wire dia: 0.800 mm
# pitch: 3.500 mm
.ELECTRICAL
"CYLV_MM_R" "D5.000_H8.000_Z3.000_WD0.800_P3.500" MM 11.000
0 0.718 0.400 0
0 0.718 -0.400 341.586
0 1.750 -0.400 0
0 1.750 0.400 180
0 0.718 0.400 0
.END_ELECTRICAL
# cylindrical outline, vertical, 1 pin on left
# file: "cylvin_1L_D0.25_L0.25_Z0.127_WD0.062_P0.25.idf"
# dia: 250 THOU
# length: 250 THOU
# extra height: 127 THOU
# wire dia: 62 THOU
# pitch: 250 THOU
.ELECTRICAL
"CYLV_IN_L" "D0.250_H0.250_Z0.127_WD0.062_P0.250" THOU 377
1 3 31 0
1 3 -31 -331.282
1 -125 -31 0
1 -125 31 -180
1 3 31 0
.END_ELECTRICAL
# cylindrical outline, vertical, 1 pin on right
# file: "cylvin_1R_D0.25_L0.25_Z0.127_WD0.062_P0.25.idf"
# dia: 250 THOU
# length: 250 THOU
# extra height: 127 THOU
# wire dia: 62 THOU
# pitch: 250 THOU
.ELECTRICAL
"CYLV_IN_R" "D0.250_H0.250_Z0.127_WD0.062_P0.250" THOU 377
0 -3 31 0
0 -3 -31 331.282
0 125 -31 0
0 125 31 180
0 -3 31 0
.END_ELECTRICAL
# cylindrical outline, horiz., axial pins
# file: "resistor.idf"
# dia: 2.500 mm
# length: 4.000 mm
# extra height: 0.500 mm
# wire dia: 0.600 mm
# pitch: 8.000 mm
.ELECTRICAL
"CYLH_MM_AXI" "D2.500_H4.000_Z0.500_WD0.600_P8.000" MM 3.000
0 -2.000 1.250 0
0 -2.000 0.300 0
0 -4.000 0.300 0
0 -4.000 -0.300 180
0 -2.000 -0.300 0
0 -2.000 -1.250 0
0 2.000 -1.250 0
0 2.000 -0.300 0
0 4.000 -0.300 0
0 4.000 0.300 180
0 2.000 0.300 0
0 2.000 1.250 0
0 -2.000 1.250 0
.END_ELECTRICAL
# cylindrical outline, horiz., axial pins
# file: "resistor_in.idf"
# dia: 98 THOU
# length: 157 THOU
# extra height: 20 THOU
# wire dia: 24 THOU
# pitch: 315 THOU
.ELECTRICAL
"CYLH_IN_AXI" "D0.098_H0.157_Z0.020_WD0.024_P0.315" THOU 118
0 -78 49 0
0 -78 12 0
0 -157 12 0
0 -157 -12 180
0 -78 -12 0
0 -78 -49 0
0 78 -49 0
0 78 -12 0
0 157 -12 0
0 157 12 180
0 78 12 0
0 78 49 0
0 -78 49 0
.END_ELECTRICAL
# cylindrical outline, horiz., radial pins
# file: "capacitor.idf"
# dia: 5.000 mm
# length: 6.000 mm
# extra height: 0.200 mm
# wire dia: 0.600 mm
# pitch: 2.500 mm
# lead: 3.000 mm
.ELECTRICAL
"CYLH_MM_RAD" "D5.000_H6.000_Z0.200_WD0.600_P2.500_L3.000" MM 5.200
0 -2.500 9.000 0
0 -2.500 3.000 0
0 -1.550 3.000 0
0 -1.550 0 0
0 -0.950 0 180
0 -0.950 3.000 0
0 0.950 3.000 0
0 0.950 0 0
0 1.550 0 180
0 1.550 3.000 0
0 2.500 3.000 0
0 2.500 9.000 0
0 -2.500 9.000 0
.END_ELECTRICAL
# cylindrical outline, horiz., radial pins
# file: "capacitor_in.idf"
# dia: 197 THOU
# length: 236 THOU
# extra height: 8 THOU
# wire dia: 24 THOU
# pitch: 98 THOU
# lead: 118 THOU
.ELECTRICAL
"CYLH_IN_RAD" "D0.197_H0.236_Z0.008_WD0.024_P0.098_L0.118" THOU 205
0 -98 354 0
0 -98 118 0
0 -61 118 0
0 -61 0 0
0 -37 0 180
0 -37 118 0
0 37 118 0
0 37 0 0
0 61 0 180
0 61 118 0
0 98 118 0
0 98 354 0
0 -98 354 0
.END_ELECTRICAL
# rectangular outline
# file: "rectMM_10x10x6_C0.idf"
# width: 10.000 mm
# length: 10.000 mm
# height: 6.000 mm
# chamfer: 0.000 mm
.ELECTRICAL
"RECTMM" "W10.000_L10.000_H6.000_C0.000" MM 6.000
0 5.000 5.000 0
0 -5.000 5.000 0
0 -5.000 -5.000 0
0 5.000 -5.000 0
0 5.000 5.000 0
.END_ELECTRICAL
# rectangular outline
# file: "rectIN_10x10x6mm_C0mm.idf"
# width: 393 THOU
# length: 393 THOU
# height: 236 THOU
# chamfer: 0 THOU
.ELECTRICAL
"RECTIN" "W393_L393_H236_C0" THOU 236
0 196 196 0
0 -196 196 0
0 -196 -196 0
0 196 -196 0
0 196 196 0
.END_ELECTRICAL
# rectangular outline
# file: "rectMM_10x10x2_C0.5.idf"
# width: 10.000 mm
# length: 10.000 mm
# height: 2.000 mm
# chamfer: 0.500 mm
.ELECTRICAL
"RECTMM" "W10.000_L10.000_H2.000_C0.500" MM 2.000
0 5.000 5.000 0
0 -4.500 5.000 0
0 -5.000 4.500 0
0 -5.000 -5.000 0
0 5.000 -5.000 0
0 5.000 5.000 0
.END_ELECTRICAL
# rectangular outline
# file: "rectIN_10x10x2mm_C0.5mm.idf"
# width: 393 THOU
# length: 393 THOU
# height: 78 THOU
# chamfer: 19 THOU
.ELECTRICAL
"RECTIN" "W393_L393_H78_C19" THOU 78
0 196 196 0
0 -176 196 0
0 -196 176 0
0 -196 -196 0
0 196 -196 0
0 196 196 0
.END_ELECTRICAL
# rectangular outline, leaded
# file: "rectLMM_10x10x12_D0.8_P6.0.idf"
# width: 10.000 mm
# length: 10.000 mm
# height: 12.000 mm
# wire dia: 0.800 mm
# pitch: 6.000 mm
.ELECTRICAL
"RECTLMM" "W10.000_L10.000_H12.000_D0.800_P6.000" MM 12.000
0 3.000 0.400 0
0 2.000 0.400 0
0 2.000 5.000 0
0 -8.000 5.000 0
0 -8.000 -5.000 0
0 2.000 -5.000 0
0 2.000 -0.400 0
0 3.000 -0.400 0
0 3.000 0.400 180
.END_ELECTRICAL
# rectangular outline, leaded
# file: "rectLIN_10x10x12mm_D0.8mm_P6.0mm.idf"
# width: 393 THOU
# length: 393 THOU
# height: 472 THOU
# wire dia: 31 THOU
# pitch: 236 THOU
.ELECTRICAL
"RECTLIN" "W393_L393_H472_D31_P236" THOU 472
0 118 15 0
0 78 15 0
0 78 196 0
0 -315 196 0
0 -315 -196 0
0 78 -196 0
0 78 -15 0
0 118 -15 0
0 118 15 180
.END_ELECTRICAL
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 Cirilo Bernardo
*
* 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 <cctype>
#include <iostream>
#include <sstream>
#include <idf_common.h>
#include <idf_helpers.h>
using namespace std;
using namespace IDF3;
// fetch a line from the given input file and trim the ends
bool IDF3::FetchIDFLine( std::ifstream& aModel, std::string& aLine, bool& isComment, std::streampos& aFilePos )
{
aLine = "";
aFilePos = aModel.tellg();
if( aFilePos == -1 )
return false;
std::getline( aModel, aLine );
isComment = false;
// A comment begins with a '#' and must be the first character on the line
if( aLine[0] == '#' )
{
// opening '#' is stripped
isComment = true;
aLine.erase( aLine.begin() );
}
// strip leading and trailing spaces
while( !aLine.empty() && isspace( *aLine.begin() ) )
aLine.erase( aLine.begin() );
while( !aLine.empty() && isspace( *aLine.rbegin() ) )
aLine.erase( --aLine.end() );
// a comment line may be empty to improve human readability
if( aLine.empty() && !isComment )
return false;
return true;
}
// extract an IDF string and move the index to point to the character after the substring
bool IDF3::GetIDFString( const std::string& aLine, std::string& aIDFString,
bool& hasQuotes, int& aIndex )
{
// 1. drop all leading spaces
// 2. if the first character is '"', read until the next '"',
// otherwise read until the next space or EOL.
std::ostringstream ostr;
int len = aLine.length();
int idx = aIndex;
if( idx < 0 || idx >= len )
return false;
while( isspace( aLine[idx] ) && idx < len ) ++idx;
if( idx == len )
{
aIndex = idx;
return false;
}
if( aLine[idx] == '"' )
{
hasQuotes = true;
++idx;
while( aLine[idx] != '"' && idx < len )
ostr << aLine[idx++];
if( idx == len )
{
ERROR_IDF << "unterminated quote mark in line:\n";
std::cerr << "LINE: " << aLine << "\n";
aIndex = idx;
return false;
}
++idx;
}
else
{
hasQuotes = false;
while( !isspace( aLine[idx] ) && idx < len )
ostr << aLine[idx++];
}
aIDFString = ostr.str();
aIndex = idx;
return true;
}
// perform a comparison between a fixed token string and an input string.
// the token is assumed to be an upper case IDF token and the input string
// is data from an IDF file. Since IDF tokens are case-insensitive, we cannot
// assume anything about the case of the input string.
bool IDF3::CompareToken( const char* aTokenString, const std::string& aInputString )
{
std::string::size_type i, j;
std::string bigToken = aInputString;
j = aInputString.length();
for( i = 0; i < j; ++i )
bigToken[i] = std::toupper( bigToken[i] );
if( !bigToken.compare( aTokenString ) )
return true;
return false;
}
// parse a string for an IDF3::KEY_OWNER
bool IDF3::ParseOwner( const std::string& aToken, IDF3::KEY_OWNER& aOwner )
{
if( CompareToken( "UNOWNED", aToken ) )
{
aOwner = UNOWNED;
return true;
}
else if( CompareToken( "ECAD", aToken ) )
{
aOwner = ECAD;
return true;
}
else if( CompareToken( "MCAD", aToken ) )
{
aOwner = MCAD;
return true;
}
ERROR_IDF << "unrecognized IDF OWNER: '" << aToken << "'\n";
return false;
}
bool IDF3::ParseIDFLayer( const std::string& aToken, IDF3::IDF_LAYER& aLayer )
{
if( CompareToken( "TOP", aToken ) )
{
aLayer = LYR_TOP;
return true;
}
else if( CompareToken( "BOTTOM", aToken ) )
{
aLayer = LYR_BOTTOM;
return true;
}
else if( CompareToken( "BOTH", aToken ) )
{
aLayer = LYR_BOTH;
return true;
}
else if( CompareToken( "INNER", aToken ) )
{
aLayer = LYR_INNER;
return true;
}
else if( CompareToken( "ALL", aToken ) )
{
aLayer = LYR_ALL;
return true;
}
ERROR_IDF << "unrecognized IDF LAYER: '" << aToken << "'\n";
aLayer = LYR_INVALID;
return false;
}
bool IDF3::WriteLayersText( std::ofstream& aBoardFile, IDF3::IDF_LAYER aLayer )
{
switch( aLayer )
{
case LYR_TOP:
aBoardFile << "TOP";
break;
case LYR_BOTTOM:
aBoardFile << "BOTTOM";
break;
case LYR_BOTH:
aBoardFile << "BOTH";
break;
case LYR_INNER:
aBoardFile << "INNER";
break;
case LYR_ALL:
aBoardFile << "ALL";
break;
default:
ERROR_IDF << "Invalid IDF layer" << aLayer << "\n";
return false;
break;
}
return !aBoardFile.fail();
}
std::string IDF3::GetPlacementString( IDF3::IDF_PLACEMENT aPlacement )
{
switch( aPlacement )
{
case PS_UNPLACED:
return "UNPLACED";
case PS_PLACED:
return "PLACED";
case PS_MCAD:
return "MCAD";
case PS_ECAD:
return "ECAD";
default:
break;
}
std::ostringstream ostr;
ostr << "[INVALID PLACEMENT VALUE]:" << aPlacement;
return ostr.str();
}
std::string IDF3::GetLayerString( IDF3::IDF_LAYER aLayer )
{
switch( aLayer )
{
case LYR_TOP:
return "TOP";
case LYR_BOTTOM:
return "BOTTOM";
case LYR_BOTH:
return "BOTH";
case LYR_INNER:
return "INNER";
case LYR_ALL:
return "ALL";
default:
break;
}
std::ostringstream ostr;
ostr << "[INVALID LAYER VALUE]:" << aLayer;
return ostr.str();
}
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 Cirilo Bernardo
*
* 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
*/
#ifndef IDF_HELPERS_H
#define IDF_HELPERS_H
#include <wx/wx.h>
#include <fstream>
#include <string>
#include <idf_common.h>
/**
* Macro TO_UTF8
* converts a wxString to a UTF8 encoded C string for all wxWidgets build modes.
* wxstring is a wxString, not a wxT() or _(). The scope of the return value
* is very limited and volatile, but can be used with printf() style functions well.
* NOTE: Taken from KiCad include/macros.h
*/
#define TO_UTF8( wxstring ) ( (const char*) (wxstring).utf8_str() )
/**
* function FROM_UTF8
* converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
* NOTE: Taken from KiCad include/macros.h
*/
static inline wxString FROM_UTF8( const char* cstring )
{
wxString line = wxString::FromUTF8( cstring );
if( line.IsEmpty() ) // happens when cstring is not a valid UTF8 sequence
line = wxConvCurrent->cMB2WC( cstring ); // try to use locale conversion
return line;
}
#define ERROR_IDF std::cerr << "* " << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "(): "
// minimum drill diameters / slot widths to be represented in the IDF output
#define IDF_MIN_DIA_MM ( 0.001 )
#define IDF_MIN_DIA_THOU ( 0.00039 )
// conversion between mm and thou
#define IDF_MM_TO_THOU 0.0254
namespace IDF3
{
/**
* Function FetchIDFLine
* retrieves a single line from an IDF file and performs minimal processing. If a comment symbol
* is encountered then it is removed and a single leading space is removed if present; all trailing
* spaces are removed. If the line is not a comment then all leading and trailing spaces are stripped.
*
* @param aModel is an open IDFv3 file
* @param aLine (output) is the line retrieved from the file
* @param isComment (output) is set to true if the line is a comment
* @param aFilePos (output) is set to the beginning of the line in case the file needs to be rewound
*
* @return bool: true if a line was read and was not empty; otherwise false
*/
bool FetchIDFLine( std::ifstream& aModel, std::string& aLine, bool& isComment, std::streampos& aFilePos );
/**
* Function GetIDFString
* parses a line retrieved via FetchIDFLine() and returns the first IDF string found from the starting
* point aIndex
*
* @param aLine is the line to parse
* @param aIDFString (output) is the IDF string retrieved
* @param hasQuotes (output) is true if the string was in quotation marks
* @param aIndex (input/output) is the index into the input line
*
* @return bool: true if a string was retrieved, otherwise false
*/
bool GetIDFString( const std::string& aLine, std::string& aIDFString,
bool& hasQuotes, int& aIndex );
/**
* Function CompareToken
* performs a case-insensitive comparison of a token string and an input string
*
* @param aToken is an IDF token such as ".HEADER"
* @param aInputString is a string typically retrieved via GetIDFString
*
* @return bool: true if the token and input string match
*/
bool CompareToken( const char* aTokenString, const std::string& aInputString );
/**
* Function ParseOwner
* parses the input string for a valid IDF Owner type
*
* @param aToken is the string to be parsed
* @param aOwner (output) is the IDF Owner class
*
* @return bool: true if a valid OWNER was found, otherwise false
*/
bool ParseOwner( const std::string& aToken, IDF3::KEY_OWNER& aOwner );
/**
* Function ParseIDFLayer
* parses an input string for a valid IDF layer specification
*
* @param aToken is the string to be parsed
* @param aLayer (output) is the IDF Layer type or group
*
* @return bool: true if a valid IDF Layer type was found, otherwise false
*/
bool ParseIDFLayer( const std::string& aToken, IDF3::IDF_LAYER& aLayer );
/**
* Function WriteLayersText
* writes out text corresponding to the given IDF Layer type
*
* @param aBoardFile is an IDFv3 file open for output
* @param aLayer is the IDF Layer type
*
* @return bool: true if the data was successfully written, otherwise false
*/
bool WriteLayersText( std::ofstream& aBoardFile, IDF3::IDF_LAYER aLayer );
/**
* Function GetPlacementString
* returns a string representing the given IDF Placement type
*
* @param aPlacement is the IDF placement type to encode as a string
*
* @return string: the string representation of aPlacement
*/
std::string GetPlacementString( IDF3::IDF_PLACEMENT aPlacement );
/**
* Function GetLayerString
* returns a string representing the given IDF Layer type
*
* @param aLayer is the IDF layer type to encode as a string
*
* @return string: the string representation of aLayer
*/
std::string GetLayerString( IDF3::IDF_LAYER aLayer );
}
#endif // IDF_HELPERS_H
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 Cirilo Bernardo
*
* 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 <iostream>
#include <iomanip>
#include <sstream>
#include <cmath>
#include <idf_helpers.h>
#include <idf_outlines.h>
#include <idf_parser.h>
using namespace IDF3;
using namespace std;
/*
* CLASS: BOARD OUTLINE
*/
BOARD_OUTLINE::BOARD_OUTLINE()
{
outlineType = OTLN_BOARD;
single = false;
owner = UNOWNED;
parent = NULL;
thickness = 0.0;
unit = UNIT_MM;
return;
}
BOARD_OUTLINE::~BOARD_OUTLINE()
{
Clear();
return;
}
IDF3::OUTLINE_TYPE BOARD_OUTLINE::GetOutlineType( void )
{
return outlineType;
}
bool BOARD_OUTLINE::readOutlines( std::ifstream& aBoardFile )
{
// reads the outline data from a file
double x, y, ang;
double dLoc = 1e-5; // distances are equal when closer than 0.1 micron
bool comment = false;
bool quoted = false;
bool closed = false;
int idx = 0;
int loopidx = -1;
int tmp = 0;
int npts = 0;
std::string iline;
std::string entry;
std::stringstream tstr;
IDF_OUTLINE* op = NULL;
IDF_SEGMENT* sp = NULL;
IDF_POINT prePt;
IDF_POINT curPt;
std::streampos pos;
// destroy any existing outline data
ClearOutlines();
while( aBoardFile.good() )
{
if( !FetchIDFLine( aBoardFile, iline, comment, pos ) )
continue;
idx = 0;
GetIDFString( iline, entry, quoted, idx );
if( quoted )
{
ERROR_IDF << "invalid outline; FIELD 1 is quoted\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
// check for the end of the section
if( entry.size() >= 5 && CompareToken( ".END_", entry.substr( 0, 5 ) ) )
{
// rewind to the start of the last line; the routine invoking
// this is responsible for checking that the current '.END_ ...'
// matches the section header.
aBoardFile.seekg( pos );
if( outlines.size() > 0 )
{
if( npts > 0 && !closed )
{
ERROR_IDF << "invalid outline (not closed)\n";
return false;
}
// verify winding
if( !single )
{
if( !outlines.front()->IsCCW() )
{
ERROR_IDF << "invalid IDF3 file (BOARD_OUTLINE)\n";
cerr << "* first outline is not in CCW order\n";
//#warning TO BE IMPLEMENTED
// outlines.front()->EnsureWinding( false );
return true;
}
if( outlines.size() > 1 && outlines.back()->IsCCW() && !outlines.back()->IsCircle() )
{
ERROR_IDF << "invalid IDF3 file (BOARD_OUTLINE)\n";
cerr << "* cutout points are not in CW order\n";
//#warning TO BE IMPLEMENTED
// outlines.front()->EnsureWinding( true );
return true;
}
}
}
return true;
}
tstr.clear();
tstr << entry;
tstr >> tmp;
if( tstr.fail() )
{
if( outlineType == OTLN_COMPONENT && CompareToken( "PROP", entry ) )
{
aBoardFile.seekg( pos );
return true;
}
ERROR_IDF << "invalid outline; FIELD 1 is not numeric\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
if( tmp != loopidx )
{
// index change
if( tmp < 0 )
{
ERROR_IDF << "invalid outline; FIELD 1 is invalid\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
if( loopidx == -1 )
{
// first outline
if( single )
{
// outline may have a Loop Index of 0 or 1
if( tmp == 0 || tmp == 1 )
{
op = new IDF_OUTLINE;
if( op == NULL )
{
ERROR_IDF << "memory allocation failed\n";
return false;
}
outlines.push_back( op );
loopidx = tmp;
}
else
{
ERROR_IDF << "invalid outline; FIELD 1 is invalid (must be 0 or 1)\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
}
else
{
// outline *MUST* have a Loop Index of 0
if( tmp != 0 )
{
ERROR_IDF << "invalid outline; first outline of a BOARD or PANEL must have a Loop Index of 0\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
op = new IDF_OUTLINE;
if( op == NULL )
{
ERROR_IDF << "memory allocation failed\n";
return false;
}
outlines.push_back( op );
loopidx = tmp;
}
// end of block for first outline
}
else
{
// outline for cutout
if( single )
{
ERROR_IDF << "invalid outline; a simple outline type may only have one outline\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
if( tmp - loopidx != 1 )
{
ERROR_IDF << "invalid outline; cutouts must be numbered in order from 1 onwards\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
// verify winding of previous outline
if( ( loopidx = 0 && !op->IsCCW() )
|| ( loopidx > 0 && op->IsCCW() ) )
{
ERROR_IDF << "invalid outline (violation of loop point order rules by Loop Index "
<< loopidx << ")\n";
return false;
}
op = new IDF_OUTLINE;
if( op == NULL )
{
ERROR_IDF << "memory allocation failed\n";
return false;
}
outlines.push_back( op );
loopidx = tmp;
}
// end of index change code
npts = 0;
closed = false;
}
if( op == NULL )
{
ERROR_IDF << "invalid outline; FIELD 1 is invalid\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
if( !GetIDFString( iline, entry, quoted, idx ) )
{
ERROR_IDF << "invalid RECORD 3, FIELD 2 does not exist\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
if( quoted )
{
ERROR_IDF << "invalid RECORD 3, FIELD 2 is quoted\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
tstr.clear();
tstr << entry;
tstr >> x;
if( tstr.fail() )
{
ERROR_IDF << "invalid RECORD 3, invalid X value in FIELD 2\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
if( !GetIDFString( iline, entry, quoted, idx ) )
{
ERROR_IDF << "invalid RECORD 3, FIELD 3 does not exist\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
if( quoted )
{
ERROR_IDF << "invalid RECORD 3, FIELD 3 is quoted\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
tstr.clear();
tstr << entry;
tstr >> y;
if( tstr.fail() )
{
ERROR_IDF << "invalid RECORD 3, invalid Y value in FIELD 3\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
if( !GetIDFString( iline, entry, quoted, idx ) )
{
ERROR_IDF << "invalid RECORD 3, FIELD 4 does not exist\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
if( quoted )
{
ERROR_IDF << "invalid RECORD 3, FIELD 4 is quoted\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
tstr.clear();
tstr << entry;
tstr >> ang;
if( tstr.fail() )
{
ERROR_IDF << "invalid ANGLE value in FIELD 3\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
// the line was successfully read; convert to mm if necessary
if( unit == UNIT_THOU )
{
x *= IDF_MM_TO_THOU;
y *= IDF_MM_TO_THOU;
}
if( npts++ == 0 )
{
// first point
prePt.x = x;
prePt.y = y;
// ensure that the first point is not an arc specification
if( ang < -MIN_ANG || ang > MIN_ANG )
{
ERROR_IDF << "invalid RECORD 3, first point has non-zero angle\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
}
else
{
// Nth point
if( closed )
{
ERROR_IDF << "invalid RECORD 3; adding a segment to a closed outline\n";
std::cerr << " LINE: " << iline << "\n";
return false;
}
curPt.x = x;
curPt.y = y;
if( ang > -MIN_ANG && ang < MIN_ANG )
{
sp = new IDF_SEGMENT( prePt, curPt );
}
else
{
sp = new IDF_SEGMENT( prePt, curPt, ang, false );
}
if( sp == NULL )
{
ERROR_IDF << "memory allocation failure\n";
return false;
}
if( sp->IsCircle() )
{
// this is a circle; the loop is closed
if( op->size() != 0 )
{
ERROR_IDF << "invalid RECORD 3; adding a circle to a non-empty outline\n";
std::cerr << " LINE: " << iline << "\n";
delete sp;
return false;
}
closed = true;
}
else if( op->size() != 0 )
{
if( curPt.Matches( op->front()->startPoint, dLoc ) )
closed = true;
}
op->push( sp );
prePt.x = x;
prePt.y = y;
}
} // while( aBoardFile.good() )
// NOTE:
// 1. ideally we would ensure that there are no arcs with a radius of 0; this entails
// actively calculating the last point as the previous entry could have been an instruction
return false;
}
bool BOARD_OUTLINE::writeComments( std::ofstream& aBoardFile )
{
if( comments.empty() )
return true;
list< string >::const_iterator itS = comments.begin();
list< string >::const_iterator itE = comments.end();
while( itS != itE )
{
aBoardFile << "# " << *itS << "\n";
++itS;
}
return !aBoardFile.fail();
}
bool BOARD_OUTLINE::writeOwner( std::ofstream& aBoardFile )
{
switch( owner )
{
case ECAD:
aBoardFile << "ECAD\n";
break;
case MCAD:
aBoardFile << "MCAD\n";
break;
default:
aBoardFile << "UNOWNED\n";
break;
}
return !aBoardFile.fail();
}
bool BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutline, size_t aIndex )
{
// TODO: check the stream integrity
std::list<IDF_SEGMENT*>::iterator bo;
std::list<IDF_SEGMENT*>::iterator eo;
if( aOutline->size() == 1 )
{
if( !aOutline->front()->IsCircle() )
{
// this is a bad outline
ERROR_IDF << "bad outline (single segment item, not circle)\n";
return false;
}
if( single )
aIndex = 0;
// NOTE: a circle always has an angle of 360, never -360,
// otherwise SolidWorks chokes on the file.
if( unit == UNIT_MM )
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< aOutline->front()->startPoint.x << " "
<< aOutline->front()->startPoint.y << " 0\n";
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< aOutline->front()->endPoint.x << " "
<< aOutline->front()->endPoint.y << " 360\n";
}
else
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< (aOutline->front()->startPoint.x / IDF_MM_TO_THOU) << " "
<< (aOutline->front()->startPoint.y / IDF_MM_TO_THOU) << " 0\n";
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< (aOutline->front()->endPoint.x / IDF_MM_TO_THOU) << " "
<< (aOutline->front()->endPoint.y / IDF_MM_TO_THOU) << " 360\n";
}
return !aBoardFile.fail();
}
// ensure that the very last point is the same as the very first point
aOutline->back()-> endPoint = aOutline->front()->startPoint;
if( single )
{
// only indices 0 (CCW) and 1 (CW) are valid; set the index according to
// the outline's winding
if( aOutline->IsCCW() )
aIndex = 0;
else
aIndex = 1;
}
// check if we must reverse things
if( ( aOutline->IsCCW() && ( aIndex > 0 ) )
|| ( ( !aOutline->IsCCW() ) && ( aIndex == 0 ) ) )
{
eo = aOutline->begin();
bo = aOutline->end();
--bo;
// for the first item we write out both points
if( unit == UNIT_MM )
{
if( aOutline->front()->angle < MIN_ANG && aOutline->front()->angle > -MIN_ANG )
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< aOutline->front()->endPoint.x << " "
<< aOutline->front()->endPoint.y << " 0\n";
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< aOutline->front()->startPoint.x << " "
<< aOutline->front()->startPoint.y << " 0\n";
}
else
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< aOutline->front()->endPoint.x << " "
<< aOutline->front()->endPoint.y << " 0\n";
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< aOutline->front()->startPoint.x << " "
<< aOutline->front()->startPoint.y << " "
<< setprecision(5) << -aOutline->front()->angle << "\n";
}
}
else
{
if( aOutline->front()->angle < MIN_ANG && aOutline->front()->angle > -MIN_ANG )
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< (aOutline->front()->endPoint.x / IDF_MM_TO_THOU) << " "
<< (aOutline->front()->endPoint.y / IDF_MM_TO_THOU) << " 0\n";
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< (aOutline->front()->startPoint.x / IDF_MM_TO_THOU) << " "
<< (aOutline->front()->startPoint.y / IDF_MM_TO_THOU) << " 0\n";
}
else
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< (aOutline->front()->endPoint.x / IDF_MM_TO_THOU) << " "
<< (aOutline->front()->endPoint.y / IDF_MM_TO_THOU) << " 0\n";
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< (aOutline->front()->startPoint.x / IDF_MM_TO_THOU) << " "
<< (aOutline->front()->startPoint.y / IDF_MM_TO_THOU) << " "
<< setprecision(5) << -aOutline->front()->angle << "\n";
}
}
// for all other segments we only write out the start point
while( bo != eo )
{
if( unit == UNIT_MM )
{
if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->startPoint.x << " "
<< (*bo)->startPoint.y << " 0\n";
}
else
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->startPoint.x << " "
<< (*bo)->startPoint.y << " "
<< setprecision(5) << -(*bo)->angle << "\n";
}
}
else
{
if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->startPoint.x / IDF_MM_TO_THOU) << " "
<< ((*bo)->startPoint.y / IDF_MM_TO_THOU) << " 0\n";
}
else
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->startPoint.x / IDF_MM_TO_THOU) << " "
<< ((*bo)->startPoint.y / IDF_MM_TO_THOU) << " "
<< setprecision(5) << -(*bo)->angle << "\n";
}
}
--bo;
}
}
else
{
bo = aOutline->begin();
eo = aOutline->end();
// for the first item we write out both points
if( unit == UNIT_MM )
{
if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->startPoint.x << " "
<< (*bo)->startPoint.y << " 0\n";
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->endPoint.x << " "
<< (*bo)->endPoint.y << " 0\n";
}
else
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->startPoint.x << " "
<< (*bo)->startPoint.y << " 0\n";
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->endPoint.x << " "
<< (*bo)->endPoint.y << " "
<< setprecision(5) << (*bo)->angle << "\n";
}
}
else
{
if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->startPoint.x / IDF_MM_TO_THOU) << " "
<< ((*bo)->startPoint.y / IDF_MM_TO_THOU) << " 0\n";
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->endPoint.x / IDF_MM_TO_THOU) << " "
<< ((*bo)->endPoint.y / IDF_MM_TO_THOU) << " 0\n";
}
else
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->startPoint.x / IDF_MM_TO_THOU) << " "
<< ((*bo)->startPoint.y / IDF_MM_TO_THOU) << " 0\n";
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->endPoint.x / IDF_MM_TO_THOU) << " "
<< ((*bo)->endPoint.y / IDF_MM_TO_THOU) << " "
<< setprecision(5) << (*bo)->angle << "\n";
}
}
++bo;
// for all other segments we only write out the last point
while( bo != eo )
{
if( unit == UNIT_MM )
{
if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->endPoint.x << " "
<< (*bo)->endPoint.y << " 0\n";
}
else
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->endPoint.x << " "
<< (*bo)->endPoint.y << " "
<< setprecision(5) << (*bo)->angle << "\n";
}
}
else
{
if( (*bo)->angle < MIN_ANG && (*bo)->angle > -MIN_ANG )
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->endPoint.x / IDF_MM_TO_THOU) << " "
<< ((*bo)->endPoint.y / IDF_MM_TO_THOU) << " 0\n";
}
else
{
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->endPoint.x / IDF_MM_TO_THOU) << " "
<< ((*bo)->endPoint.y / IDF_MM_TO_THOU) << " "
<< setprecision(5) << (*bo)->angle << "\n";
}
}
++bo;
}
}
return !aBoardFile.fail();
}
bool BOARD_OUTLINE::writeOutlines( std::ofstream& aBoardFile )
{
if( outlines.empty() )
return true;
int idx = 0;
std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
while( itS != itE )
{
if( !writeOutline( aBoardFile, *itS, idx++ ) )
return false;
++itS;
}
return true;
}
void BOARD_OUTLINE::SetUnit( IDF3::IDF_UNIT aUnit )
{
unit = aUnit;
return;
}
IDF3::IDF_UNIT BOARD_OUTLINE::GetUnit( void )
{
return unit;
}
bool BOARD_OUTLINE::SetThickness( double aThickness )
{
if( aThickness < 0.0 )
return false;
thickness = aThickness;
return true;
}
double BOARD_OUTLINE::GetThickness( void )
{
return thickness;
}
bool BOARD_OUTLINE::ReadData( std::ifstream& aBoardFile, const std::string& aHeader )
{
// BOARD_OUTLINE (PANEL_OUTLINE)
// .BOARD_OUTLINE [OWNER]
// [thickness]
// [outlines]
// check RECORD 1
std::string token;
bool quoted = false;
int idx = 0;
if( !GetIDFString( aHeader, token, quoted, idx ) )
{
ERROR_IDF << "invalid invocation; blank header line\n";
return false;
}
if( quoted )
{
ERROR_IDF << "section names may not be quoted:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
if( !CompareToken( ".BOARD_OUTLINE", token ) )
{
ERROR_IDF << "not a board outline:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
if( !GetIDFString( aHeader, token, quoted, idx ) )
{
ERROR_IDF << "no OWNER; setting to UNOWNED\n";
owner = UNOWNED;
}
else
{
if( !ParseOwner( token, owner ) )
{
ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
owner = UNOWNED;
}
}
// check RECORD 2
std::string iline;
bool comment = false;
std::streampos pos;
while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
if( ( !aBoardFile.good() && !aBoardFile.eof() ) || iline.empty() )
{
ERROR_IDF << "bad .BOARD_OUTLINE section (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within .BOARD_OUTLINE section\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad .BOARD_OUTLINE section (no thickness)\n";
return false;
}
std::stringstream teststr;
teststr << token;
teststr >> thickness;
if( teststr.fail() )
{
ERROR_IDF << "bad .BOARD_OUTLINE section (invalid RECORD 2)\n";
std::cerr << "\tLINE: " << iline << "\n";
return false;
}
if( unit == UNIT_THOU )
thickness *= IDF_MM_TO_THOU;
// read RECORD 3 values
// XXX - check the return value - we may have empty lines and what-not
readOutlines( aBoardFile );
// check RECORD 4
while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
{
ERROR_IDF << "bad .BOARD_OUTLINE section (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within .BOARD_OUTLINE section\n";
return false;
}
if( !CompareToken( ".END_BOARD_OUTLINE", iline ) )
{
ERROR_IDF << "bad .BOARD_OUTLINE section (no .END_BOARD_OUTLINE)\n";
return false;
}
return true;
}
bool BOARD_OUTLINE::WriteData( std::ofstream& aBoardFile )
{
writeComments( aBoardFile );
// note: a BOARD_OUTLINE section is required, even if it is empty
aBoardFile << ".BOARD_OUTLINE ";
writeOwner( aBoardFile );
if( unit == UNIT_MM )
aBoardFile << setiosflags(ios::fixed) << setprecision(5) << thickness << "\n";
else
aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_MM_TO_THOU) << "\n";
if( !writeOutlines( aBoardFile ) )
return false;
aBoardFile << ".END_BOARD_OUTLINE\n\n";
return !aBoardFile.fail();
}
void BOARD_OUTLINE::Clear( void )
{
comments.clear();
ClearOutlines();
owner = UNOWNED;
return;
}
void BOARD_OUTLINE::SetParent( IDF3_BOARD* aParent )
{
parent = aParent;
}
IDF3_BOARD* BOARD_OUTLINE::GetParent( void )
{
return parent;
}
bool BOARD_OUTLINE::AddOutline( IDF_OUTLINE* aOutline )
{
std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
while( itS != itE )
{
if( *itS == aOutline )
return false;
++itS;
}
outlines.push_back( aOutline );
return true;
}
bool BOARD_OUTLINE::DelOutline( IDF_OUTLINE* aOutline )
{
std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
if( outlines.empty() )
return false;
// if there are more than 1 outlines it makes no sense to delete
// the first outline (board outline) since that would have the
// undesirable effect of substituting a cutout outline as the board outline
if( aOutline == outlines.front() )
{
if( outlines.size() > 1 )
return false;
outlines.clear();
return true;
}
while( itS != itE )
{
if( *itS == aOutline )
{
outlines.erase( itS );
return true;
}
++itS;
}
return false;
}
bool BOARD_OUTLINE::DelOutline( size_t aIndex )
{
std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
if( outlines.empty() )
return false;
if( aIndex >= outlines.size() )
return false;
if( aIndex == 0 )
{
// if there are more than 1 outlines it makes no sense to delete
// the first outline (board outline) since that would have the
// undesirable effect of substituting a cutout outline as the board outline
if( outlines.size() > 1 )
return false;
delete *itS;
outlines.clear();
return true;
}
for( ; aIndex > 0; --aIndex )
++itS;
delete *itS;
outlines.erase( itS );
return true;
}
const std::list< IDF_OUTLINE* >*const BOARD_OUTLINE::GetOutlines( void )
{
return &outlines;
}
size_t BOARD_OUTLINE::OutlinesSize( void )
{
return outlines.size();
}
IDF_OUTLINE* BOARD_OUTLINE::GetOutline( size_t aIndex )
{
if( aIndex >= outlines.size() )
return NULL;
std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
for( ; aIndex > 0; --aIndex )
++itS;
return *itS;
}
IDF3::KEY_OWNER BOARD_OUTLINE::GetOwner( void )
{
return owner;
}
bool BOARD_OUTLINE::SetOwner( IDF3::KEY_OWNER aOwner )
{
// if this is a COMPONENT OUTLINE there can be no owner
if( outlineType == IDF3::OTLN_COMPONENT )
return true;
// if no one owns the outline, any system may
// set the owner
if( owner == UNOWNED )
{
owner = aOwner;
return true;
}
// if the outline is owned, only the owning
// CAD system can make alterations
if( parent == NULL )
return false;
if( owner == MCAD && parent->GetCadType() == CAD_MECH )
{
owner = aOwner;
return true;
}
if( owner == ECAD && parent->GetCadType() == CAD_ELEC )
{
owner = aOwner;
return true;
}
return false;
}
bool BOARD_OUTLINE::IsSingle( void )
{
return single;
}
void BOARD_OUTLINE::ClearOutlines( void )
{
std::list< IDF_OUTLINE* >::iterator itS = outlines.begin();
std::list< IDF_OUTLINE* >::iterator itE = outlines.end();
while( itS != itE )
{
delete *itS;
++itS;
}
outlines.clear();
return;
}
void BOARD_OUTLINE::AddComment( const std::string& aComment )
{
if( aComment.empty() )
return;
comments.push_back( aComment );
return;
}
size_t BOARD_OUTLINE::CommentsSize( void )
{
return comments.size();
}
std::list< std::string >* BOARD_OUTLINE::GetComments( void )
{
return &comments;
}
const std::string* BOARD_OUTLINE::GetComment( size_t aIndex )
{
if( aIndex >= comments.size() )
return NULL;
std::list< std::string >::iterator itS = comments.begin();
for( ; aIndex > 0; --aIndex )
++itS;
return &(*itS);
}
bool BOARD_OUTLINE::DeleteComment( size_t aIndex )
{
if( aIndex >= comments.size() )
return false;
std::list< std::string >::iterator itS = comments.begin();
for( ; aIndex > 0; --aIndex )
++itS;
comments.erase( itS );
return true;
}
void BOARD_OUTLINE::ClearComments( void )
{
comments.clear();
return;
}
/*
* CLASS: OTHER_OUTLINE
*/
OTHER_OUTLINE::OTHER_OUTLINE()
{
outlineType = OTLN_OTHER;
side = LYR_INVALID;
single = true;
return;
}
void OTHER_OUTLINE::SetOutlineIdentifier( const std::string aUniqueID )
{
uniqueID = aUniqueID;
return;
}
const std::string& OTHER_OUTLINE::GetOutlineIdentifier( void )
{
return uniqueID;
}
bool OTHER_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
{
switch( aSide )
{
case LYR_TOP:
case LYR_BOTTOM:
side = aSide;
break;
default:
ERROR_IDF << "invalid side (" << aSide << "); must be one of TOP/BOTTOM\n";
side = LYR_INVALID;
return false;
break;
}
return true;
}
IDF3::IDF_LAYER OTHER_OUTLINE::GetSide( void )
{
return side;
}
bool OTHER_OUTLINE::ReadData( std::ifstream& aBoardFile, const std::string& aHeader )
{
// OTHER_OUTLINE/VIA_KEEPOUT
// .OTHER_OUTLINE [OWNER]
// [outline identifier] [thickness] [board side: Top/Bot] {not present in VA\IA KEEPOUT}
// [outline]
// check RECORD 1
std::string token;
bool quoted = false;
int idx = 0;
if( !GetIDFString( aHeader, token, quoted, idx ) )
{
ERROR_IDF << "invalid invocation; blank header line\n";
return false;
}
if( quoted )
{
ERROR_IDF << "section names may not be quoted:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
if( outlineType == OTLN_OTHER )
{
if( !CompareToken( ".OTHER_OUTLINE", token ) )
{
ERROR_IDF << "not an OTHER outline:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
}
else
{
if( !CompareToken( ".VIA_KEEPOUT", token ) )
{
ERROR_IDF << "not a VIA_KEEPOUT outline:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
}
if( !GetIDFString( aHeader, token, quoted, idx ) )
{
ERROR_IDF << "no OWNER; setting to UNOWNED\n";
owner = UNOWNED;
}
else
{
if( !ParseOwner( token, owner ) )
{
ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
owner = UNOWNED;
}
}
std::string iline;
bool comment = false;
std::streampos pos;
if( outlineType == OTLN_OTHER )
{
// check RECORD 2
// [outline identifier] [thickness] [board side: Top/Bot]
while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
{
ERROR_IDF << "bad .OTHER_OUTLINE section (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within .OTHER_OUTLINE section\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad .OTHER_OUTLINE section (no outline identifier)\n";
return false;
}
uniqueID = token;
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad .OTHER_OUTLINE section (no thickness)\n";
return false;
}
std::stringstream teststr;
teststr << token;
teststr >> thickness;
if( teststr.fail() )
{
ERROR_IDF << "bad .OTHER_OUTLINE section (invalid RECORD 2 reading thickness)\n";
std::cerr << "\tLINE: " << iline << "\n";
return false;
}
if( unit == UNIT_THOU )
thickness *= IDF_MM_TO_THOU;
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad .OTHER_OUTLINE section (no board side)\n";
return false;
}
if( !ParseIDFLayer( token, side ) || ( side != LYR_TOP && side != LYR_BOTTOM ) )
{
ERROR_IDF << "bad .OTHER_OUTLINE section (invalid side, must be TOP/BOTTOM only)\n";
std::cerr << "\tLINE: " << iline << "\n";
return false;
}
}
// read RECORD 3 values
readOutlines( aBoardFile );
// check RECORD 4
while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
{
ERROR_IDF << "bad .OTHER_OUTLINE/.VIA_KEEPOUT section (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within .OTHER_OUTLINE/.VIA_KEEPOUT section\n";
return false;
}
if( outlineType == OTLN_OTHER )
{
if( !CompareToken( ".END_OTHER_OUTLINE", iline ) )
{
ERROR_IDF << "bad .OTHER_OUTLINE section (no .END_OTHER_OUTLINE)\n";
return false;
}
}
else
{
if( !CompareToken( ".END_VIA_KEEPOUT", iline ) )
{
ERROR_IDF << "bad .VIA_KEEPOUT section (no .END_VIA_KEEPOUT)\n";
return false;
}
}
return true;
}
bool OTHER_OUTLINE::WriteData( std::ofstream& aBoardFile )
{
// this section is optional; do not write if not required
if( outlines.empty() )
return true;
writeComments( aBoardFile );
// write RECORD 1
if( outlineType == OTLN_OTHER )
aBoardFile << ".OTHER_OUTLINE ";
else
aBoardFile << ".VIA_KEEPOUT ";
writeOwner( aBoardFile );
// write RECORD 2
if( outlineType == OTLN_OTHER )
{
aBoardFile << "\"" << uniqueID << "\" ";
if( unit == UNIT_MM )
aBoardFile << setiosflags(ios::fixed) << setprecision(5) << thickness << " ";
else
aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_MM_TO_THOU) << " ";
switch( side )
{
case LYR_TOP:
case LYR_BOTTOM:
WriteLayersText( aBoardFile, side );
break;
default:
ERROR_IDF << "Invalid OTHER_OUTLINE side (neither top nor bottom): " << side << "\n";
return false;
break;
}
}
// write RECORD 3
if( !writeOutlines( aBoardFile ) )
return false;
// write RECORD 4
if( outlineType == OTLN_OTHER )
aBoardFile << ".END_OTHER_OUTLINE\n\n";
else
aBoardFile << ".END_VIA_KEEPOUT\n\n";
return !aBoardFile.fail();
}
void OTHER_OUTLINE::Clear( void )
{
side = LYR_INVALID;
uniqueID.clear();
BOARD_OUTLINE::Clear();
return;
}
/*
* CLASS: ROUTE_OUTLINE
*/
ROUTE_OUTLINE::ROUTE_OUTLINE()
{
outlineType = OTLN_ROUTE;
single = true;
layers = LYR_INVALID;
}
void ROUTE_OUTLINE::SetLayers( IDF3::IDF_LAYER aLayer )
{
layers = aLayer;
}
IDF3::IDF_LAYER ROUTE_OUTLINE::GetLayers( void )
{
return layers;
}
bool ROUTE_OUTLINE::ReadData( std::ifstream& aBoardFile, const std::string& aHeader )
{
// ROUTE_OUTLINE (or ROUTE_KEEPOUT)
// .ROUTE_OUTLINE [OWNER]
// [layers]
// [outline]
// check RECORD 1
std::string token;
bool quoted = false;
int idx = 0;
if( !GetIDFString( aHeader, token, quoted, idx ) )
{
ERROR_IDF << "invalid invocation; blank header line\n";
return false;
}
if( quoted )
{
ERROR_IDF << "section names may not be quoted:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
if( outlineType == OTLN_ROUTE )
{
if( !CompareToken( ".ROUTE_OUTLINE", token ) )
{
ERROR_IDF << "not a ROUTE outline:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
}
else
{
if( !CompareToken( ".ROUTE_KEEPOUT", token ) )
{
ERROR_IDF << "not a ROUTE KEEPOUT outline:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
}
if( !GetIDFString( aHeader, token, quoted, idx ) )
{
ERROR_IDF << "no OWNER; setting to UNOWNED\n";
owner = UNOWNED;
}
else
{
if( !ParseOwner( token, owner ) )
{
ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
owner = UNOWNED;
}
}
// check RECORD 2
// [layers: TOP, BOTTOM, BOTH, INNER, ALL]
std::string iline;
bool comment = false;
std::streampos pos;
while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
if( !aBoardFile.good() )
{
ERROR_IDF << "bad .ROUTE_OUTLINE/KEEPOUT section (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within .ROUTE_OUTLINE/KEEPOUT section\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad .ROUTE_OUTLINE/KEEPOUT section (no layers specification)\n";
return false;
}
if( quoted )
{
ERROR_IDF << "bad .ROUTE_OUTLINE/KEEPOUT section (layers may not be quoted)\n";
std::cerr << "\tLINE: " << iline << "\n";
return false;
}
if( !ParseIDFLayer( token, layers ) )
{
ERROR_IDF << "bad .ROUTE_OUTLINE/KEEPOUT section (invalid layer)\n";
std::cerr << "\tLINE: " << iline << "\n";
return false;
}
// read RECORD 3 values
readOutlines( aBoardFile );
// check RECORD 4
while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
{
ERROR_IDF << "bad .ROUTE_OUTLINE/KEEPOUT section (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within .ROUTE_OUTLINE/KEEPOUT section\n";
return false;
}
if( outlineType == OTLN_ROUTE )
{
if( !CompareToken( ".END_ROUTE_OUTLINE", iline ) )
{
ERROR_IDF << "bad .ROUTE_OUTLINE section (no .END_ROUTE_OUTLINE)\n";
return false;
}
}
else
{
if( !CompareToken( ".END_ROUTE_KEEPOUT", iline ) )
{
ERROR_IDF << "bad .ROUTE_KEEPOUT section (no .END_ROUTE_KEEPOUT)\n";
return false;
}
}
return true;
}
bool ROUTE_OUTLINE::WriteData( std::ofstream& aBoardFile )
{
// this section is optional; do not write if not required
if( outlines.empty() )
return true;
if( layers == LYR_INVALID )
{
ERROR_IDF << "layer not specified\n";
return false;
}
writeComments( aBoardFile );
// write RECORD 1
if( outlineType == OTLN_ROUTE )
aBoardFile << ".ROUTE_OUTLINE ";
else
aBoardFile << ".ROUTE_KEEPOUT ";
writeOwner( aBoardFile );
// write RECORD 2
WriteLayersText( aBoardFile, layers );
aBoardFile << "\n";
// write RECORD 3
if( !writeOutlines( aBoardFile ) )
return false;
// write RECORD 4
if( outlineType == OTLN_ROUTE )
aBoardFile << ".END_ROUTE_OUTLINE\n\n";
else
aBoardFile << ".END_ROUTE_KEEPOUT\n\n";
return !aBoardFile.fail();
}
void ROUTE_OUTLINE::Clear( void )
{
BOARD_OUTLINE::Clear();
layers = LYR_INVALID;
return;
}
/*
* CLASS: PLACE_OUTLINE
*/
PLACE_OUTLINE::PLACE_OUTLINE()
{
outlineType = OTLN_PLACE;
single = true;
thickness = 0.0;
side = LYR_INVALID;
}
void PLACE_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
{
switch( aSide )
{
case LYR_TOP:
case LYR_BOTTOM:
case LYR_BOTH:
side = aSide;
break;
default:
// XXX - throw
ERROR_IDF << "invalid layer (" << aSide << "): must be one of TOP/BOTTOM/BOTH\n";
side = LYR_INVALID;
return;
break;
}
return;
}
IDF3::IDF_LAYER PLACE_OUTLINE::GetSide( void )
{
return side;
}
void PLACE_OUTLINE::SetMaxHeight( double aHeight )
{
if( aHeight < 0.0 )
{
ERROR_IDF << "invalid height (must be >= 0.0); default to 0\n";
thickness = 0.0;
return;
}
thickness = aHeight;
return;
}
double PLACE_OUTLINE::GetMaxHeight( void )
{
return thickness;
}
bool PLACE_OUTLINE::ReadData( std::ifstream& aBoardFile, const std::string& aHeader )
{
// PLACE_OUTLINE/KEEPOUT
// .PLACE_OUTLINE [OWNER]
// [board side: Top/Bot/Both] [height]
// [outline]
// check RECORD 1
std::string token;
bool quoted = false;
int idx = 0;
if( !GetIDFString( aHeader, token, quoted, idx ) )
{
ERROR_IDF << "invalid invocation; blank header line\n";
return false;
}
if( quoted )
{
ERROR_IDF << "section names may not be quoted:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
if( outlineType == OTLN_PLACE )
{
if( !CompareToken( ".PLACE_OUTLINE", token ) )
{
ERROR_IDF << "not a PLACE outline:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
}
else
{
if( !CompareToken( ".PLACE_KEEPOUT", token ) )
{
ERROR_IDF << "not a PLACE_KEEPOUT outline:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
}
if( !GetIDFString( aHeader, token, quoted, idx ) )
{
ERROR_IDF << "no OWNER; setting to UNOWNED\n";
owner = UNOWNED;
}
else
{
if( !ParseOwner( token, owner ) )
{
ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
owner = UNOWNED;
}
}
// check RECORD 2
// [board side: Top/Bot/Both] [height]
std::string iline;
bool comment = false;
std::streampos pos;
while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
if( !aBoardFile.good() )
{
ERROR_IDF << "bad .PLACE_OUTLINE/KEEPOUT section (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within .PLACE_OUTLINE/KEEPOUT section\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad .PLACE_OUTLINE/KEEPOUT section (no board side information)\n";
return false;
}
if( !ParseIDFLayer( token, side ) ||
( side != LYR_TOP && side != LYR_BOTTOM && side != LYR_BOTH ) )
{
ERROR_IDF << "bad .PLACE_OUTLINE/KEEPOUT section (invalid side, must be one of TOP/BOTTOM/BOTH)\n";
std::cerr << "\tLINE: " << iline << "\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad .PLACE_OUTLINE/KEEPOUT section (no height)\n";
return false;
}
std::stringstream teststr;
teststr << token;
teststr >> thickness;
if( teststr.fail() )
{
ERROR_IDF << "bad .PLACE_OUTLINE/KEEPOUT section (invalid RECORD 2 reading height)\n";
std::cerr << "\tLINE: " << iline << "\n";
return false;
}
if( unit == UNIT_THOU )
thickness *= IDF_MM_TO_THOU;
// read RECORD 3 values
readOutlines( aBoardFile );
// check RECORD 4
while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
{
ERROR_IDF << "bad .PLACE_OUTLINE/KEEPOUT section (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within .PLACE_OUTLINE/KEEPOUT section\n";
return false;
}
if( outlineType == OTLN_PLACE )
{
if( !GetIDFString( iline, token, quoted, idx )
|| !CompareToken( ".END_PLACE_OUTLINE", token ) )
{
ERROR_IDF << "bad .PLACE_OUTLINE section (no .END_PLACE_OUTLINE)\n";
return false;
}
}
else
{
if( !GetIDFString( iline, token, quoted, idx )
|| !CompareToken( ".END_PLACE_KEEPOUT", token ) )
{
ERROR_IDF << "bad .PLACE_KEEPOUT section (no .END_PLACE_KEEPOUT)\n";
return false;
}
}
return true;
}
bool PLACE_OUTLINE::WriteData( std::ofstream& aBoardFile )
{
// this section is optional; do not write if not required
if( outlines.empty() )
return true;
writeComments( aBoardFile );
// write RECORD 1
if( outlineType == OTLN_PLACE )
aBoardFile << ".PLACE_OUTLINE ";
else
aBoardFile << ".PLACE_KEEPOUT ";
writeOwner( aBoardFile );
// write RECORD 2
switch( side )
{
case LYR_TOP:
case LYR_BOTTOM:
case LYR_BOTH:
WriteLayersText( aBoardFile, side );
break;
default:
ERROR_IDF << "Invalid PLACE_OUTLINE/KEEPOUT side (" << side << "); must be one of TOP/BOTTOM/BOTH\n";
return false;
break;
}
aBoardFile << " ";
if( unit == UNIT_MM )
aBoardFile << setiosflags(ios::fixed) << setprecision(5) << thickness << "\n";
else
aBoardFile << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_MM_TO_THOU) << "\n";
// write RECORD 3
if( !writeOutlines( aBoardFile ) )
return false;
// write RECORD 4
if( outlineType == OTLN_PLACE )
aBoardFile << ".END_PLACE_OUTLINE\n\n";
else
aBoardFile << ".END_PLACE_KEEPOUT\n\n";
return !aBoardFile.fail();
}
void PLACE_OUTLINE::Clear( void )
{
BOARD_OUTLINE::Clear();
thickness = 0.0;
side = LYR_INVALID;
return;
}
/*
* CLASS: ROUTE_KEEPOUT
*/
ROUTE_KO_OUTLINE::ROUTE_KO_OUTLINE()
{
outlineType = OTLN_ROUTE_KEEPOUT;
return;
}
/*
* CLASS: PLACE_KEEPOUT
*/
PLACE_KO_OUTLINE::PLACE_KO_OUTLINE()
{
outlineType = OTLN_PLACE_KEEPOUT;
return;
}
/*
* CLASS: VIA_KEEPOUT
*/
VIA_KO_OUTLINE::VIA_KO_OUTLINE()
{
outlineType = OTLN_VIA_KEEPOUT;
}
/*
* CLASS: PLACEMENT GROUP (PLACE_REGION)
*/
GROUP_OUTLINE::GROUP_OUTLINE()
{
outlineType = OTLN_GROUP_PLACE;
thickness = 0.0;
side = LYR_INVALID;
single = true;
return;
}
void GROUP_OUTLINE::SetSide( IDF3::IDF_LAYER aSide )
{
switch( aSide )
{
case LYR_TOP:
case LYR_BOTTOM:
case LYR_BOTH:
side = aSide;
break;
default:
// XXX throw
ERROR_IDF << "invalid side (" << aSide << "); must be one of TOP/BOTTOM/BOTH\n";
return;
break;
}
return;
}
IDF3::IDF_LAYER GROUP_OUTLINE::GetSide( void )
{
return side;
}
void GROUP_OUTLINE::SetGroupName( std::string aGroupName )
{
groupName = aGroupName;
return;
}
const std::string& GROUP_OUTLINE::GetGroupName( void )
{
return groupName;
}
bool GROUP_OUTLINE::ReadData( std::ifstream& aBoardFile, const std::string& aHeader )
{
// Placement Group
// .PLACE_REGION [OWNER]
// [side: Top/Bot/Both ] [component group name]
// [outline]
// check RECORD 1
std::string token;
bool quoted = false;
int idx = 0;
if( !GetIDFString( aHeader, token, quoted, idx ) )
{
ERROR_IDF << "invalid invocation; blank header line\n";
return false;
}
if( quoted )
{
ERROR_IDF << "section names may not be quoted:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
if( !CompareToken( ".PLACE_REGION", token ) )
{
ERROR_IDF << "not a PLACE_REGION outline:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
if( !GetIDFString( aHeader, token, quoted, idx ) )
{
ERROR_IDF << "no OWNER; setting to UNOWNED\n";
owner = UNOWNED;
}
else
{
if( !ParseOwner( token, owner ) )
{
ERROR_IDF << "invalid OWNER (reverting to UNOWNED): " << token << "\n";
owner = UNOWNED;
}
}
std::string iline;
bool comment = false;
std::streampos pos;
// check RECORD 2
// [side: Top/Bot/Both ] [component group name]
while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
if( !aBoardFile.good() )
{
ERROR_IDF << "bad .PLACE_REGION section (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within .PLACE_REGION section\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad .PLACE_REGION section (no board side)\n";
return false;
}
if( !ParseIDFLayer( token, side ) ||
( side != LYR_TOP && side != LYR_BOTTOM && side != LYR_BOTH ) )
{
ERROR_IDF << "bad .PLACE_REGION section (invalid side, must be TOP/BOTTOM/BOTH)\n";
std::cerr << "\tLINE: " << iline << "\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad .PLACE_REGION section (no outline identifier)\n";
return false;
}
groupName = token;
// read RECORD 3 values
readOutlines( aBoardFile );
// check RECORD 4
while( aBoardFile.good() && !FetchIDFLine( aBoardFile, iline, comment, pos ) );
if( ( !aBoardFile.good() && aBoardFile.eof() ) || iline.empty() )
{
ERROR_IDF << "bad .PLACE_REGION section (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within .PLACE_REGION section\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx )
|| !CompareToken( ".END_PLACE_REGION", token ) )
{
ERROR_IDF << "bad .PLACE_REGION section (no .END_PLACE_REGION)\n";
return false;
}
return true;
}
bool GROUP_OUTLINE::WriteData( std::ofstream& aBoardFile )
{
// this section is optional; do not write if not required
if( outlines.empty() )
return true;
writeComments( aBoardFile );
// write RECORD 1
aBoardFile << ".PLACE_REGION ";
writeOwner( aBoardFile );
// write RECORD 2
switch( side )
{
case LYR_TOP:
case LYR_BOTTOM:
case LYR_BOTH:
WriteLayersText( aBoardFile, side );
break;
default:
ERROR_IDF << "Invalid PLACE_REGION side (must be TOP/BOTTOM/BOTH): " << side << "\n";
return false;
break;
}
aBoardFile << " \"" << groupName << "\"\n";
// write RECORD 3
if( !writeOutlines( aBoardFile ) )
return false;
// write RECORD 4
aBoardFile << ".END_PLACE_REGION\n\n";
return !aBoardFile.fail();
}
void GROUP_OUTLINE::Clear( void )
{
BOARD_OUTLINE::Clear();
thickness = 0.0;
side = LYR_INVALID;
groupName.clear();
return;
}
/*
* CLASS: COMPONENT OUTLINE
*/
IDF3_COMP_OUTLINE::IDF3_COMP_OUTLINE()
{
single = true;
outlineType = OTLN_COMPONENT;
compType = COMP_INVALID;
refNum = 0;
return;
}
bool IDF3_COMP_OUTLINE::readProperties( std::ifstream& aLibFile )
{
bool quoted = false;
bool comment = false;
std::string iline;
std::string token;
std::streampos pos;
std::string pname; // property name
std::string pval; // property value
int idx = 0;
while( aLibFile.good() )
{
if( !FetchIDFLine( aLibFile, iline, comment, pos ) )
continue;
idx = 0;
if( comment )
{
ERROR_IDF << "comment within component outline section\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad component outline section (no PROP)\n";
return false;
}
if( quoted )
{
ERROR_IDF << "bad component outline section (PROP or .END may not be quoted)\n";
return false;
}
if( token.size() >= 5 && CompareToken( ".END_", token.substr( 0, 5 ) ) )
{
aLibFile.seekg( pos );
return true;
}
if( !CompareToken( "PROP", token ) )
{
ERROR_IDF << "invalid electrical outline; expecting PROP or .END_ELECTRICAL\n";
std::cerr << "\tLINE: " << iline << "\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad component outline section (no prop name)\n";
return false;
}
pname = token;
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad component outline section (no prop value)\n";
return false;
}
pval = token;
if( props.insert( pair< string, string >(pname, pval) ).second == false )
{
ERROR_IDF << "bad component outline: duplicate property name '" << pname << "'\n";
return false;
}
}
return !aLibFile.fail();
}
bool IDF3_COMP_OUTLINE::writeProperties( std::ofstream& aLibFile )
{
if( props.empty() )
return true;
std::map< std::string, std::string >::const_iterator itS = props.begin();
std::map< std::string, std::string >::const_iterator itE = props.end();
while( itS != itE )
{
aLibFile << "PROP " << "\"" << itS->first << "\" \""
<< itS->second << "\"\n";
++itS;
}
return !aLibFile.fail();
}
bool IDF3_COMP_OUTLINE::ReadData( std::ifstream& aLibFile, const std::string& aHeader )
{
// .ELECTRICAL/.MECHANICAL
// [GEOM] [PART] [UNIT] [HEIGHT]
// [outline]
// [PROP] [prop name] [prop value]
// check RECORD 1
std::string token;
bool quoted = false;
int idx = 0;
if( !GetIDFString( aHeader, token, quoted, idx ) )
{
ERROR_IDF << "invalid invocation; blank header line\n";
return false;
}
if( quoted )
{
ERROR_IDF << "section names may not be quoted:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
if( CompareToken( ".ELECTRICAL", token ) )
{
compType = COMP_ELEC;
}
else if( CompareToken( ".MECHANICAL", token ) )
{
compType = COMP_MECH;
}
else
{
ERROR_IDF << "not a component outline:\n";
std::cerr << "\tLINE: " << aHeader << "\n";
return false;
}
// check RECORD 2
// [GEOM] [PART] [UNIT] [HEIGHT]
std::string iline;
bool comment = false;
std::streampos pos;
while( aLibFile.good() && !FetchIDFLine( aLibFile, iline, comment, pos ) );
if( !aLibFile.good() )
{
ERROR_IDF << "bad component outline data (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within a component outline section\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad component outline (no GEOMETRY NAME)\n";
return false;
}
geometry = token;
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad component outline (no PART NAME)\n";
return false;
}
part = token;
if( part.empty() && geometry.empty() )
{
ERROR_IDF << "bad component outline (both GEOMETRY and PART names are empty)\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad component outline (no unit type)\n";
return false;
}
if( CompareToken( "MM", token ) )
{
unit = UNIT_MM;
}
else if( CompareToken( "THOU", token ) )
{
unit = UNIT_THOU;
}
else
{
ERROR_IDF << "bad component outline (invalid unit type)\n";
std::cerr << "\tLINE: " << iline << "\n";
return false;
}
if( !GetIDFString( iline, token, quoted, idx ) )
{
ERROR_IDF << "bad component outline (no height)\n";
return false;
}
std::istringstream teststr;
teststr.str( token );
teststr >> thickness;
if( teststr.fail() )
{
ERROR_IDF << "bad component outline (invalid height)\n";
std::cerr << "\tLINE: " << iline << "\n";
return false;
}
if( unit == UNIT_THOU )
thickness *= IDF_MM_TO_THOU;
// read RECORD 3 values
readOutlines( aLibFile );
if( compType == COMP_ELEC )
{
if( !readProperties( aLibFile ) )
return false;
}
// check RECORD 4
while( aLibFile.good() && !FetchIDFLine( aLibFile, iline, comment, pos ) );
if( ( !aLibFile.good() && aLibFile.eof() ) || iline.empty() )
{
ERROR_IDF << "bad component outline data (premature end)\n";
return false;
}
idx = 0;
if( comment )
{
ERROR_IDF << "comment within component outline section\n";
return false;
}
if( compType == COMP_ELEC )
{
if( !CompareToken( ".END_ELECTRICAL", iline ) )
{
ERROR_IDF << "bad component outline (no .END_ELECTRICAL)\n";
return false;
}
}
else
{
if( !CompareToken( ".END_MECHANICAL", iline ) )
{
ERROR_IDF << "corrupt .MECHANICAL outline\n";
return false;
}
}
return true;
}
bool IDF3_COMP_OUTLINE::WriteData( std::ofstream& aLibFile )
{
if( compType != COMP_ELEC && compType != COMP_MECH )
{
ERROR_IDF << "component type not set or invalid\n";
return false;
}
if( refNum == 0 )
return true; // nothing to do
writeComments( aLibFile );
// note: the outline section is required, even if it is empty
if( compType == COMP_ELEC )
aLibFile << ".ELECTRICAL\n";
else
aLibFile << ".MECHANICAL\n";
// RECORD 2
// [GEOM] [PART] [UNIT] [HEIGHT]
aLibFile << "\"" << geometry << "\" \"" << part << "\" ";
if( unit == UNIT_MM )
aLibFile << "MM " << setiosflags(ios::fixed) << setprecision(5) << thickness << "\n";
else
aLibFile << "THOU " << setiosflags(ios::fixed) << setprecision(1) << (thickness / IDF_MM_TO_THOU) << "\n";
if( !writeOutlines( aLibFile ) )
return false;
if( compType == COMP_ELEC )
{
writeProperties( aLibFile );
aLibFile << ".END_ELECTRICAL\n\n";
}
else
{
aLibFile << ".END_MECHANICAL\n\n";
}
return !aLibFile.fail();
}
void IDF3_COMP_OUTLINE::Clear( void )
{
BOARD_OUTLINE::Clear();
uid.clear();
geometry.clear();
part.clear();
compType = COMP_INVALID;
refNum = 0;
props.clear();
return;
}
void IDF3_COMP_OUTLINE::SetComponentClass( IDF3::COMP_TYPE aCompClass )
{
switch( aCompClass )
{
case COMP_ELEC:
case COMP_MECH:
compType = aCompClass;
break;
default:
// XXX - throw
ERROR_IDF << "invalid component class (must be ELECTRICAL or MECHANICAL)\n";
return;
break;
}
return;
}
IDF3::COMP_TYPE IDF3_COMP_OUTLINE::GetComponentClass( void )
{
return compType;
}
void IDF3_COMP_OUTLINE::SetGeomName( const std::string& aGeomName )
{
geometry = aGeomName;
uid.clear();
return;
}
const std::string& IDF3_COMP_OUTLINE::GetGeomName( void )
{
return geometry;
}
void IDF3_COMP_OUTLINE::SetPartName( const std::string& aPartName )
{
part = aPartName;
uid.clear();
return;
}
const std::string& IDF3_COMP_OUTLINE::GetPartName( void )
{
return part;
}
const std::string& IDF3_COMP_OUTLINE::GetUID( void )
{
if( !uid.empty() )
return uid;
if( geometry.empty() && part.empty() )
return uid;
uid = geometry + "_" + part;
return uid;
}
int IDF3_COMP_OUTLINE::IncrementRef( void )
{
return ++refNum;
}
int IDF3_COMP_OUTLINE::DecrementRef( void )
{
if( refNum == 0 )
{
ERROR_IDF << "BUG: decrementing refNum beyond 0\n";
return 0;
}
--refNum;
return refNum;
}
bool IDF3_COMP_OUTLINE::CreateDefaultOutline( const std::string &aGeom, const std::string &aPart )
{
Clear();
if( aGeom.empty() && aPart.empty() )
{
geometry = "NOGEOM";
part = "NOPART";
uid = "NOGEOM_NOPART";
}
else
{
geometry = aGeom;
part = aPart;
uid = aGeom + "_" + aPart;
}
compType = COMP_ELEC;
thickness = 5.0;
unit = UNIT_MM;
// Create a star shape 5mm high with points on 5 and 3 mm circles
double a, da;
da = M_PI / 5.0;
a = da / 2.0;
IDF_POINT p1, p2;
IDF_OUTLINE* ol = new IDF_OUTLINE;
IDF_SEGMENT* sp;
p1.x = 1.5 * cos( a );
p1.y = 1.5 * sin( a );
if( ol == NULL )
return false;
for( int i = 0; i < 10; ++i )
{
if( i & 1 )
{
p2.x = 2.5 * cos( a );
p2.y = 2.5 * sin( a );
}
else
{
p2.x = 1.5 * cos( a );
p2.y = 1.5 * sin( a );
}
sp = new IDF_SEGMENT( p1, p2 );
if( sp == NULL )
{
Clear();
return false;
}
ol->push( sp );
a += da;
p1 = p2;
}
a = da / 2.0;
p2.x = 1.5 * cos( a );
p2.y = 1.5 * sin( a );
sp = new IDF_SEGMENT( p1, p2 );
if( sp == NULL )
{
Clear();
return false;
}
ol->push( sp );
outlines.push_back( ol );
return true;
}
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 Cirilo Bernardo
*
* 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
*/
#ifndef IDF_OUTLINES_H
#define IDF_OUTLINES_H
#include <string>
#include <list>
#include <map>
#include <wx/string.h>
#include <wx/filename.h>
#include <idf_common.h>
/*
* NOTES ON OUTLINE TYPES:
*
* BOARD_OUTLINE (PANEL_OUTLINE)
* .BOARD_OUTLINE [OWNER]
* [thickness]
* [outlines]
*
* OTHER_OUTLINE
* .OTHER_OUTLINE [OWNER]
* [outline identifier] [thickness] [board side: Top/Bot]
* [outline]
*
* ROUTE_OUTLINE
* .ROUTE_OUTLINE [OWNER]
* [layers]
* [outline]
*
* PLACE_OUTLINE
* .PLACE_OUTLINE [OWNER]
* [board side: Top/Bot/Both] [height]
* [outline]
*
* ROUTE_KEEPOUT
* .ROUTE_KEEPOUT [OWNER]
* [layers]
* [outline]
*
* VIA_KEEPOUT
* .VIA_KEEPOUT [OWNER]
* [outline]
*
* PLACE_KEEPOUT
* .PLACE_KEEPOUT [OWNER]
* [board side: Top/Bot/Both] [height]
* [outline]
*
* Placement Group
* .PLACE_REGION [OWNER]
* [side: Top/Bot/Both ] [component group name]
* [outline]
*
* Component Outline:
* .ELECTRICAL/.MECHANICAL
* [GEOM] [PART] [UNIT] [HEIGHT]
* [outline]
* [PROP] [prop name] [prop value]
*/
class IDF3_BOARD;
/**
* Class BOARD_OUTLINE
* supports the IDFv3 BOARD OUTLINE data and is the basis of other IDFv3 outline classes
*/
class BOARD_OUTLINE
{
protected:
std::list< IDF_OUTLINE* > outlines;
IDF3::KEY_OWNER owner; // indicates the owner of this outline (MCAD, ECAD, UNOWNED)
IDF3::OUTLINE_TYPE outlineType;// type of IDF outline
bool single; // true if only a single outline is accepted
std::list< std::string > comments; // associated comment list
IDF3::IDF_UNIT unit; // outline's native unit (MM or THOU)
IDF3_BOARD* parent; // BOARD which contains this outline
double thickness; // Board/Extrude Thickness or Height (IDF spec)
// Read outline data from a BOARD or LIBRARY file's outline section
bool readOutlines( std::ifstream& aBoardFile );
// Write comments to a BOARD or LIBRARY file (must not be within a SECTION as per IDFv3 spec)
bool writeComments( std::ofstream& aBoardFile );
// Write the outline owner to a BOARD file
bool writeOwner( std::ofstream& aBoardFile );
// Write the data of a single outline object
bool writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutline, size_t aIndex );
// Iterate through the outlines and write out all data
bool writeOutlines( std::ofstream& aBoardFile ); // write outline data (no headers)
public:
BOARD_OUTLINE();
virtual ~BOARD_OUTLINE();
/**
* Function SetUnit
* sets the native unit of the outline; except for component outlines this must
* be the same as the native unit of the parent IDF_BOARD object
*
* @param aUnit is the native unit (UNIT_MM or UNIT_THOU)
*/
virtual void SetUnit( IDF3::IDF_UNIT aUnit );
/**
* Function GetUnit
* returns the native unit type of the outline
*
* @return IDF_UNIT is the native unit (UNIT_MM or UNIT_THOU)
*/
virtual IDF3::IDF_UNIT GetUnit( void );
/**
* Function SetThickness
* sets the thickness or height of the outline (mm)
*
* @param aThickness is the thickness or height of the outline in mm
*/
virtual bool SetThickness( double aThickness );
/**
* Function GetThickness
* returns the thickness or height of an outline (mm)
*/
virtual double GetThickness( void );
/**
* Function ReadData
* reads data from a .BOARD_OUTLINE section
*
* @param aBoardFile is an IDFv3 file opened for reading
* @param aHeader is the ".BOARD_OUTLINE" header line as read by FetchIDFLine
*
* @return bool: true if the BOARD_OUTLINE section was successfully read, otherwise
* false. In case of an unrecoverable error an exception is thrown. On a successful
* return the file pointer will be at the line following .END_BOARD_OUTLINE
*/
virtual bool ReadData( std::ifstream& aBoardFile, const std::string& aHeader );
/**
* Function WriteData
* writes the comments and .BOARD_OUTLINE section to an IDFv3 file
*
* @param aBoardFile is an IDFv3 file opened for writing
*
* @return bool: true if the data had been successfully written, otherwise false.
*/
virtual bool WriteData( std::ofstream& aBoardFile );
/**
* Function Clear
* frees memory and reinitializes all internal data except for the parent pointer
*/
virtual void Clear( void );
/**
* Function GetOutlineType
* returns the type of outline according to the IDFv3 classification
*/
IDF3::OUTLINE_TYPE GetOutlineType( void );
/**
* Function SetParent
* sets the parent IDF_BOARD object
*/
void SetParent( IDF3_BOARD* aParent );
/**
* Function GetParent
* returns the parent IDF_BOARD object
*/
IDF3_BOARD* GetParent( void );
/**
* Function AddOutline
* adds the specified outline to this object.
*
* @param aOutline is a valid IDF outline
*
* @return bool: true if the outline was added; false if the outline
* already existed. If the outline cannot be added due to a violation
* of the IDF specification (multiple outlines for anything other than
* a BOARD_OUTLINE, or the ownership rules are violated) an exception is
* thrown.
*/
bool AddOutline( IDF_OUTLINE* aOutline );
/**
* Function DelOutline( IDF_OUTLINE* aOutline )
* removes the given outline, subject to IDF ownership rules,
* if it is owned by this object. The outline pointer remains
* valid and it is the user's responsibility to delete the object.
* The first outline in the list will never be deleted unless it
* is the sole remaining outline; this is to ensure that a board
* outline is not removed while the cutouts remain.
*
* @param aOutline is a pointer to the outline to remove from the list
*
* @return bool: true if the outline was found and removed; false if
* the outline was not found. If an ownership violation occurs an
* exception is thrown.
*/
bool DelOutline( IDF_OUTLINE* aOutline );
/**
* Function DelOutline( IDF_OUTLINE* aOutline )
* deletes the outline specified by the given index, subject to
* IDF ownership rules. The outline data is destroyed.
* The first outline in the list will never be deleted unless it
* is the sole remaining outline; this is to ensure that a board
* outline is not removed while the cutouts remain.
*
* @param aIndex is an index to the outline to delete
*
* @return bool: true if the outline was found and deleted; false if
* the outline was not found. If an ownership violation or indexation
* error occurs an exception is thrown.
*/
bool DelOutline( size_t aIndex );
/**
* Function GetOutlines
* returns a pointer to the internal outlines list. It is up to the
* user to respect the IDFv3 specification and avoid changes to this
* list which are in violation of the specification.
*/
const std::list< IDF_OUTLINE* >*const GetOutlines( void );
/**
* Function OutlinesSize
* returns the number of items in the internal outline list
*/
size_t OutlinesSize( void );
/**
* Function GetOutline
* returns a pointer to the outline as specified by aIndex.
* If the index is out of bounds an error is thrown. It is the
* responsibility of the user to observe IDF ownership rules.
*/
IDF_OUTLINE* GetOutline( size_t aIndex );
/**
* Function GetOwner
* returns the ownership status of the outline ( ECAD, MCAD, UNOWNED)
*/
IDF3::KEY_OWNER GetOwner( void );
/**
* Function SetOwner
* sets the ownership status of the outline subject to IDF
* ownership rules. The return value is true if the ownership
* was changed and false if a specification violation occurred.
*/
bool SetOwner( IDF3::KEY_OWNER aOwner );
/**
* Function IsSingle
* return true if this type of outline only supports a single
* outline. All outlines except for BOARD_OUTLINE are single.
*/
bool IsSingle( void );
/**
* Function ClearOutlines
* clears internal data except for the parent pointer
*/
void ClearOutlines( void );
/**
* Function AddComment
* adds a comment to the outline data; this function is not
* subject to IDF ownership rules.
*/
void AddComment( const std::string& aComment );
/**
* Function CommentSize
* returns the number of comments in the internal list
*/
size_t CommentsSize( void );
/**
* Function GetComments
* returns a pointer to the internal list of comments
*/
std::list< std::string >* GetComments( void );
/**
* Function GetComment
* returns the string representing the indexed comment or
* NULL if the index is out of bounds
*/
const std::string* GetComment( size_t aIndex );
/**
* Function DeleteComment
* deletes a comment based on the given index.
*
* @return bool: true if a comment was deleted, false if
* the index is out of bounds.
*/
bool DeleteComment( size_t aIndex );
/**
* Function ClearComments
* deletes all comments
*/
void ClearComments( void );
};
/**
* Class OTHER_OUTLINE
* describes miscellaneous extrusions on the board
*/
class OTHER_OUTLINE : public BOARD_OUTLINE
{
private:
std::string uniqueID; // Outline Identifier (IDF spec)
IDF3::IDF_LAYER side; // Board Side [TOP/BOTTOM ONLY] (IDF spec)
public:
OTHER_OUTLINE();
/**
* Function SetOutlineIdentifier
* sets the Outline Identifier string of this OTHER_OUTLINE object
* as per IDFv3 spec.
*/
virtual void SetOutlineIdentifier( const std::string aUniqueID );
/**
* Function GetOutlineIdentifier
* returns the object's Outline Identifier
*/
virtual const std::string& GetOutlineIdentifier( void );
/**
* Function SetSide
* sets the side which this outline is applicable to (TOP, BOTTOM).
*
* @return bool: true if the side was set, false if the side is invalid.
* An exception is thrown if there is a violation of IDF ownership rules.
*/
virtual bool SetSide( IDF3::IDF_LAYER aSide );
/**
* Function GetSide
* returns the side which this outline is applicable to
*/
virtual IDF3::IDF_LAYER GetSide( void );
/**
* Function ReadData
* reads an OTHER_OUTLINE data from an IDFv3 file.
*
* @param aBoardFile is an IDFv3 file open for reading
* @param aHeader is the .OTHER_OUTLINE header as read via FetchIDFLine
*
* @return bool: true if data was read, otherwise false. If an unrecoverable
* error occurs an exception is thrown.
*/
virtual bool ReadData( std::ifstream& aBoardFile, const std::string& aHeader );
/**
* Function WriteData
* writes the OTHER_OUTLINE data to an open IDFv3 file
*
* @param aBoardFile is an IDFv3 file open for writing
*
* @return bool: true if the data was successfully written, otherwise false.
*/
virtual bool WriteData( std::ofstream& aBoardFile );
/**
* Function Clear
* deletes internal data except for the parent object
*/
virtual void Clear( void );
};
/**
* Class ROUTE_OUTLINE
* describes routing areas on the board
*/
class ROUTE_OUTLINE : public BOARD_OUTLINE
{
protected:
IDF3::IDF_LAYER layers; // Routing layers (IDF spec)
public:
ROUTE_OUTLINE();
/**
* Function SetLayers
* sets the layer or group of layers this outline is applicable to.
* This function is subject to IDF ownership rules. An exception is
* thrown if an invalid layer is provided or an IDF ownership violation
* occurs.
*/
virtual void SetLayers( IDF3::IDF_LAYER aLayer );
/**
* Function GetLayers
* returns the layer or group of layers which this outline is applicable to
*/
virtual IDF3::IDF_LAYER GetLayers( void );
/**
* Function ReadData
* reads ROUTE_OUTLINE data from an IDFv3 file
*
* @param aBoardFile is an open IDFv3 board file
* @param aHeader is the .ROUTE_OUTLINE header as returned by FetchIDFLine
*
* @return bool: true if data was read, otherwise false. If unrecoverable
* errors occur an exception is thrown.
*/
virtual bool ReadData( std::ifstream& aBoardFile, const std::string& aHeader );
/**
* Function WriteData
* writes the ROUTE_OUTLINE data to an open IDFv3 file
*/
virtual bool WriteData( std::ofstream& aBoardFile );
/**
* Function Clear
* deletes internal data except for the parent object
*/
virtual void Clear( void );
};
/**
* Class PLACE_OUTLINE
* describes areas on the board for placing components
*/
class PLACE_OUTLINE : public BOARD_OUTLINE
{
protected:
IDF3::IDF_LAYER side; // Board Side [TOP/BOTTOM/BOTH ONLY] (IDF spec)
double height; // Max Height (IDF spec)
public:
PLACE_OUTLINE();
/**
* Function SetSide
* sets the side (TOP, BOTTOM, BOTH) which this outline applies to,
* subject to IDF ownership rules. An exception is thrown if there is
* an ownership violation or an invalid layer is passed.
*/
virtual void SetSide( IDF3::IDF_LAYER aSide );
/**
* Function GetSide
* returns the side which this outline is applicable to
*/
virtual IDF3::IDF_LAYER GetSide( void );
/**
* Function SetMaxHeight
* sets the maximum height of a component within this outline,
* subject to IDF ownership rules. An exception is thrown if
* there is an ownership violation or aHeight is negative.
*/
virtual void SetMaxHeight( double aHeight );
/**
* Function GetMaxHeight
* returns the maximum allowable height for a component in this region
*/
virtual double GetMaxHeight( void );
/**
* Function ReadData
* reads PLACE_OUTLINE data from an open IDFv3 file.
*
* @param aBoardFile is an IDFv3 file opened for reading
* @param aHeader is the .PLACE_OUTLINE header as returned by FetchIDFLine
*
* @return bool: true if data was read, otherwise false. If there are
* unrecoverable errors an exception is thrown.
*/
virtual bool ReadData( std::ifstream& aBoardFile, const std::string& aHeader );
/**
* Function WriteData
* writes the PLACE_OUTLINE data to an open IDFv3 file
*
* @param aBoardFile is an IDFv3 file opened for writing
*
* @return bool: true if the data was successfully written, otherwise false
*/
virtual bool WriteData( std::ofstream& aBoardFile );
/**
* Function Clear
* deletes all internal data
*/
virtual void Clear( void );
};
/**
* Class ROUTE_KO_OUTLINE
* describes regions and layers where no electrical routing is permitted
*/
class ROUTE_KO_OUTLINE : public ROUTE_OUTLINE
{
public:
ROUTE_KO_OUTLINE();
};
/**
* Class VIA_KO_OUTLINE
* describes regions in which vias are prohibited. Note: IDFv3 only considers
* thru-hole vias and makes no statement regarding behavior with blind or buried
* vias.
*/
class VIA_KO_OUTLINE : public OTHER_OUTLINE
{
public:
VIA_KO_OUTLINE();
};
/**
* Class PLACE_KO_OUTLINE
* represents regions and layers in which no component may
* be placed or on which a maximum component height is in effect.
*/
class PLACE_KO_OUTLINE : public PLACE_OUTLINE
{
public:
PLACE_KO_OUTLINE();
};
/**
* Class GROUP_OUTLINE
* represents regions and layers in which user-specified features or components
* may be placed.
*/
class GROUP_OUTLINE : public BOARD_OUTLINE
{
private:
IDF3::IDF_LAYER side; // Board Side [TOP/BOTTOM/BOTH ONLY] (IDF spec)
std::string groupName; // non-unique string
public:
GROUP_OUTLINE();
/**
* Function SetSide
* sets the side which this outline applies to (TOP, BOTTOM, BOTH),
* subject to IDF ownership rules. If an ownership violation occurs
* or an invalid side is specified, an exception is thrown.
*/
virtual void SetSide( IDF3::IDF_LAYER aSide );
/**
* Function GetSide
* returns the side which this outline applies to
*/
virtual IDF3::IDF_LAYER GetSide( void );
/**
* Function SetGroupName
* sets the name of the group, subject to IDF ownership rules.
* An empty name or an ownership violation results in a thrown
* exception.
*/
virtual void SetGroupName( std::string aGroupName );
/**
* Function GetGroupName
* returns a reference to the (non-unique) group name
*/
virtual const std::string& GetGroupName( void );
/**
* Function ReadData
* reads GROUP_OUTLINE data from an open IDFv3 file
*
* @param aBoardFile is an open IDFv3 file
* @param aHeader is the .PLACE_REGION header as returned by FetchIDFLine
*
* @return bool: true if data was read, otherwise false. If an unrecoverable
* error occurs an exception is thrown.
*/
virtual bool ReadData( std::ifstream& aBoardFile, const std::string& aHeader );
/**
* Function WriteData
* writes the data to a .PLACE_REGION section of an IDFv3 file
*
* @param aBoardFile is an IDFv3 file open for writing
*
* @return bool: true if the data is successfully written, otherwise false
*/
virtual bool WriteData( std::ofstream& aBoardFile );
/**
* Function Clear
* deletes internal data, subject to IDF ownership rules
*/
virtual void Clear( void );
};
/**
* class IDF3_COMP_OUTLINE
* represents a component's outline as stored in an IDF library file
*/
class IDF3_COMP_OUTLINE : public BOARD_OUTLINE
{
private:
std::string uid; // unique ID
std::string geometry; // geometry name (IDF)
std::string part; // part name (IDF)
IDF3::COMP_TYPE compType; // component type
int refNum; // number of components referring to this outline
std::map< std::string, std::string > props; // properties list
bool readProperties( std::ifstream& aLibFile );
bool writeProperties( std::ofstream& aLibFile );
public:
IDF3_COMP_OUTLINE();
/**
* Function ReadData
* reads a component outline from an open IDFv3 file
*
* @param aLibFile is an open IDFv3 Library file
* @param aHeader is the .ELECTRICAL or .MECHANICAL header as returned by FetchIDFLine
*
* @return bool: true if data was read, otherwise false. If unrecoverable errors
* occur, an exception is thrown.
*/
virtual bool ReadData( std::ifstream& aLibFile, const std::string& aHeader );
/**
* Function WriteData
* writes comments and component outline data to an IDFv3 Library file
*
* @param aLibFile is an IDFv3 library file open for writing
*
* @return bool: true if the data was successfully written, otherwise false
*/
virtual bool WriteData( std::ofstream& aLibFile );
/**
* Function Clear
* deletes internal outline data
*/
virtual void Clear( void );
/**
* Function SetComponentClass
* sets the type of component outline (.ELECTRICAL or .MECHANICAL)
* If the specified class is invalid an exception is thrown.
*/
void SetComponentClass( IDF3::COMP_TYPE aCompClass );
/**
* Function GetComponentClass
* returns the class of component represented by this outline
*/
IDF3::COMP_TYPE GetComponentClass( void );
/**
* Function SetGeomName
* sets the Geometry Name (Package Name, IDFv3 spec) of the component outline
*/
void SetGeomName( const std::string& aGeomName );
/**
* Function GetGeomName
* returns the Geometry Name (Package Name) of the component outline
*/
const std::string& GetGeomName( void );
/**
* Function SetPartName
* sets the Part Name (Part Number, IDFv3 spec) of the component outline
*/
void SetPartName( const std::string& aPartName );
/**
* Function GetPartName
* returns the Part Name (Part Number) of the component outline
*/
const std::string& GetPartName( void );
/**
* Function GetUID
* returns the unique identifier for this component outline;
* this is equal to GEOM_NAME + "_" + PART_NAME
*/
const std::string& GetUID( void );
/**
* Function IncrementRef
* increments the internal reference counter to keep track of the number of
* components referring to this outline.
*/
int IncrementRef( void );
/**
* Function DecrementRef
* decrements the internal reference counter to keep track of the number of
* components referring to this outline.
*/
int DecrementRef( void );
/**
* Function CreateDefaultOutline
* creates a default outline with the given Geometry and Part names.
* This outline is a star with outer radius 5mm and inner radius 2.5mm.
*/
bool CreateDefaultOutline( const std::string &aGeom, const std::string &aPart );
// XXX: property manipulators
};
#endif // IDF_OUTLINES_H
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014 Cirilo Bernardo
*
* 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
*/
// NOTE:
// 1. Due to the complexity of objects and the risk of accumulated
// position errors, CAD packages should only delete or add complete
// components. If a component being added already exists, it is
// replaced by the new component IF and only if the CAD type is
// permitted to make such changes.
//
// 2. Internally all units shall be in mm and by default we shall
// write files with mm units. The internal flags mm/thou shall only
// be used to translate data being read from or written to files.
// This avoids the painful management of a mixture of mm and thou.
// The API shall require all dimensions in mm; for people using any
// other unit, it is their responsibility to perform the conversion
// to mm. Conversion back to thou may incur small rounding errors.
// BUGS:
// 1. IDF compliance: On DELETE operations, ensure that the CAD
// has permission to make these deletions. This is no small task;
// however this compliance task can be deferred since it is not
// essential to the immediate needs of KiCad which are IDF
// export and IDF->VRML conversion
#ifndef IDF_PARSER_H
#define IDF_PARSER_H
#include <idf_outlines.h>
class IDF3_COMPONENT;
class IDF3_COMP_OUTLINE_DATA
{
private:
double xoff; // X offset from KiCad or X placement from IDF file
double yoff; // Y offset from KiCad or Y placement from IDF file
double zoff; // height offset (specified in IDFv3 spec, corresponds to KiCad Z offset)
double aoff; // angular offset from KiCad or Rotation Angle from IDF file
IDF3_COMP_OUTLINE* outline; // component outline to use
IDF3_COMPONENT* parent; // associated component
public:
/**
* Constructor
* creates an object with default settings and no parent or associated outline
*/
IDF3_COMP_OUTLINE_DATA();
/**
* Constructor
* creates an object with default settings and the specified parent and associated outline
*
* @param aParent is the owning IDF3_COMPONENT object
* @param aOutline is the outline for this placed component
*/
IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent, IDF3_COMP_OUTLINE* aOutline );
/**
* Constructor
* creates an object the specified parent and associated outline and the specified
* data.
*
* @param aParent is the owning IDF3_COMPONENT object
* @param aOutline is the outline for this placed component
* @param aXoff is the X offset of this outline in relation to its parent
* @param aYoff is the Y offset of this outline in relation to its parent
* @param aZoff is the board offset of this outline as per IDFv3 specification
* @param aAoff is the rotational offset of this outline in relation to its parent
*/
IDF3_COMP_OUTLINE_DATA( IDF3_COMPONENT* aParent, IDF3_COMP_OUTLINE* aOutline,
double aXoff, double aYoff, double aZoff, double aAngleOff );
~IDF3_COMP_OUTLINE_DATA();
/**
* Function SetOffsets
* sets the position and orientation of this outline item in relation to its parent
*
* @param aXoff is the X offset of this outline in relation to its parent
* @param aYoff is the Y offset of this outline in relation to its parent
* @param aZoff is the board offset of this outline as per IDFv3 specification
* @param aAoff is the rotational offset of this outline in relation to its parent
*/
void SetOffsets( double aXoff, double aYoff, double aZoff, double aAngleOff );
/**
* Function GetOffsets
* retrieves the position and orientation of this outline item in relation to its parent
*
* @param aXoff is the X offset of this outline in relation to its parent
* @param aYoff is the Y offset of this outline in relation to its parent
* @param aZoff is the board offset of this outline as per IDFv3 specification
* @param aAoff is the rotational offset of this outline in relation to its parent
*/
void GetOffsets( double& aXoff, double& aYoff, double& aZoff, double& aAngleOff );
/**
* Function SetParent
* sets the parent object
*
* @param aParent is the owning IDF3_COMPONENT object
*/
void SetParent( IDF3_COMPONENT* aParent );
/**
* Function SetOutline
* sets the outline whose position is managed by this object
*
* @param aOutline is the outline for this component
*/
void SetOutline( IDF3_COMP_OUTLINE* aOutline );
/**
* Function GetOutline
* retrieves the outline whose position is managed by this object
*
* @return IDF3_COMP_OUTLINE*: the outline for this component
*/
IDF3_COMP_OUTLINE* GetOutline( void )
{
return outline;
}
/**
* Function ReadPlaceData
* reads placement data from an open IDFv3 file
*
* @param aBoardFile is the open IDFv3 file
* @param aBoardState is the internal status flag of the IDF parser
* @param aBoard is the IDF3_BOARD object which will store the data
*
* @return bool: true if placement data was successfully read. false if
* no placement data was read; this may happen if the end of the placement
* data was encountered or an error occurred. if an error occurred then
* aBoardState is set to IDF3::FILE_INVALID or IDF3::FILE_ERROR.
*/
bool ReadPlaceData( std::ifstream &aBoardFile, IDF3::FILE_STATE& aBoardState,
IDF3_BOARD *aBoard );
/**
* Function WritePlaceData
* writes RECORD 2 and RECORD 3 of a PLACEMENT section as per IDFv3 specification
*
* @param aBoardFile is the open IDFv3 file
* @param aXpos is the X location of the parent component
* @param aYpos is the Y location of the parent component
* @param aAngle is the rotation of the parent component
* @param aRefDes is the reference designator of the parent component
* @param aPlacement is the IDF Placement Status of the parent component
* @param aSide is the IDF Layer Designator (TOP or BOTTOM)
*
* @return bool: true if data was successfully written, otherwise false
*/
bool WritePlaceData( std::ofstream& aBoardFile, double aXpos, double aYpos, double aAngle,
const std::string aRefDes, IDF3::IDF_PLACEMENT aPlacement,
IDF3::IDF_LAYER aSide );
};
class IDF3_COMPONENT
{
private:
std::list< IDF3_COMP_OUTLINE_DATA* > components;
std::list< IDF_DRILL_DATA* > drills;
double xpos;
double ypos;
double angle;
IDF3::IDF_PLACEMENT placement;
IDF3::IDF_LAYER layer; // [TOP/BOTTOM ONLY as per IDF spec]
bool hasPosition; ///< True after SetPosition is called once
std::string refdes; ///< Reference Description (MUST BE UNIQUE)
IDF3_BOARD* parent;
public:
/**
* Constructor
* sets internal parameters to default values
*/
IDF3_COMPONENT();
/**
* Constructor
* sets the parent object and initializes other internal parameters to default values
*
* @param aParent is the owning IDF3_BOARD object
*/
IDF3_COMPONENT( IDF3_BOARD* aParent );
~IDF3_COMPONENT();
/**
* Function SetParent
* sets the parent object
*
* @param aParent is the owning IDF3_BOARD object
*/
void SetParent( IDF3_BOARD* aParent );
/**
* Function GetCadType
* returns the type of CAD (IDF3::CAD_ELEC, IDF3::CAD_MECH) which instantiated this object
*
* @return IDF3::CAD_TYPE
*/
IDF3::CAD_TYPE GetCadType( void );
/**
* Function GetCadType
* returns the IDF UNIT type of the parent object or IDF3::UNIT_INVALID if
* the parent was not set
*
* @return IDF3::IDF_UNIT
*/
IDF3::IDF_UNIT GetUnit( void );
/**
* Function SetRefDes
* sets the Reference Designator (RefDes) of this component; the RefDes is shared
* by all outlines associated with this component.
*
* @return bool: true if the RefDes was accepted, otherwise false. Prohibited
* values include empty strings and the word PANEL.
*/
bool SetRefDes( const std::string& aRefDes );
/**
* Function GetRefDes
* Retrieves the Reference Designator (RefDes) of this component
*
* @return string: the Reference Designator
*/
const std::string& GetRefDes( void );
/**
* Function AddDrill
* adds a drill entry to the component and returns its pointer
*
* @param aDia diameter of the drill (mm)
* @param aXpos X position of the drill (mm)
* @param aYpos Y position of the drill (mm)
* @param aPlating plating type (PTH, NPTH)
* @param aHoleType hole class (PIN, VIA, MTG, TOOL, etc)
* @param aOwner owning CAD system (ECAD, MCAD, UNOWNED)
*
* @return pointer: a pointer to the newly created drill entry or NULL
*/
IDF_DRILL_DATA* AddDrill( double aDia, double aXpos, double aYpos,
IDF3::KEY_PLATING aPlating,
const std::string aHoleType,
IDF3::KEY_OWNER aOwner );
/**
* Function AddDrill
* adds the given drill entry to the component and returns the pointer
* to indicate success. A return value of NULL indicates that the item
* was not added and it is the user's responsibility to delete the
* object if necessary.
*
* @param aDrilledHole pointer to a drill entry
*
* @return pointer: aDrilledHole if the function succeeds, otherwise NULL
*/
IDF_DRILL_DATA* AddDrill( IDF_DRILL_DATA* aDrilledHole );
/**
* Function DelDrill( double aDia, double aXpos, double aYpos )
* deletes a drill entry based on its size and location. This operation is
* subject to IDF ownership rules.
*
* @param aDia diameter (mm) of the drilled hole to be deleted
* @param aXpos X position (mm) of the hole to be deleted
* @param aYpos X position (mm) of the hole to be deleted
*
* @return bool: true if a drill was found and deleted, otherwise false.
* If an ownership violation occurs an exception is thrown.
*/
bool DelDrill( double aDia, double aXpos, double aYpos );
/**
* Function DelDrill( IDF_DRILL_DATA* aDrill )
* deletes a drill entry based on pointer. This operation is
* subject to IDF ownership rules.
*
* @param aDrill the pointer associated with the drill entry to be deleted
*
* @return bool: true if a drill was found and deleted, otherwise false.
* If an ownership violation occurs an exception is thrown.
*/
bool DelDrill( IDF_DRILL_DATA* aDrill );
/**
* Function GetDrills
* returns a pointer to the internal list of drills. To avoid IDF
* violations, the user should not alter these entries.
*/
const std::list< IDF_DRILL_DATA* >*const GetDrills( void );
/**
* Function AddOutlineData
* adds the given component outline data to this component
*
* @param aComponentOutline is a pointer to the outline data to be added
*
* @return true if the operation succeedes, otherwise false
*/
bool AddOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline );
/**
* Function DeleteOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline )
* removes outline data based on the pointer provided.
*
* @param aComponentOutline is a pointer to be deleted from the internal list
*
* @return bool: true if the data was found and deleted, otherwise false
*/
bool DeleteOutlineData( IDF3_COMP_OUTLINE_DATA* aComponentOutline );
/**
* Function DeleteOutlineData( size_t aIndex )
* removes outline data based on the provided index.
*
* @param aIndex is an index to the internal outline list
*
* @return bool: true if the data was deleted, false if the
* index was out of bounds.
*/
bool DeleteOutlineData( size_t aIndex );
/**
* Function GetOutlineSize
* returns the number of outlines in the internal list
*/
size_t GetOutlinesSize( void );
/**
* Function GetOutlinesData
* returns a pointer to the internal list of outline data
*/
const std::list< IDF3_COMP_OUTLINE_DATA* >*const GetOutlinesData( void );
/**
* Function GetPosition
* retrieves the internal position parameters and returns true if the
* position was previously set, otherwise false.
*/
bool GetPosition( double& aXpos, double& aYpos, double& aAngle, IDF3::IDF_LAYER& aLayer );
// NOTE: it may be possible to extend this so that internal drills and outlines
// are moved when the component is moved. However there is always a danger of
// position creep due to the relative position updates.
/**
* Function SetPosition
* sets the internal position parameters and returns true if the
* position was set, false if the position was previously set. This object
* does not allow modification of the position once it is set since this may
* adversely affect the relationship with its internal objects.
*
* @param aXpos is the X position (mm) of the component
* @param aYpos is the Y position (mm) of the component
* @param aAngle is the rotation of the component (degrees)
* @param aLayer is the layer on which the component is places (TOP, BOTTOM)
*
* @return bool: true if the position was set, otherwise false
*/
bool SetPosition( double aXpos, double aYpos, double aAngle, IDF3::IDF_LAYER aLayer );
/**
* Function GetPlacement
* returns the IDF placement value of this component (UNPLACED, PLACED, ECAD, MCAD)
*/
IDF3::IDF_PLACEMENT GetPlacement( void );
/**
* Function SetPlacement
* sets the placement value of the component subject to ownership rules.
* An exception is thrown if aPlacementValue is invalid or an ownership
* violation occurs.
*/
void SetPlacement( IDF3::IDF_PLACEMENT aPlacementValue );
/**
* Function WriteDrillData
* writes the internal drill data to an IDFv3 .DRILLED_HOLES section
*
* @param aBoardFile is an IDFv3 file opened for writing
*
* @return bool: true if the operation succeeded, otherwise false
*/
bool WriteDrillData( std::ofstream& aBoardFile );
/**
* Function WritePlaceData
* writes the component placement data to an IDFv3 .PLACEMENT section
*
* @param aBoardFile is an IDFv3 file opened for writing
*
* @return bool: true if the operation succeeded, otherwise false
*/
bool WritePlaceData( std::ofstream& aBoardFile );
};
class IDF3_BOARD
{
private:
std::list< IDF_NOTE* > notes; // IDF notes
std::list< std::string > noteComments; // comment list for NOTES section
std::list< std::string > drillComments; // comment list for DRILL section
std::list< std::string > placeComments; // comment list for PLACEMENT section
std::list<IDF_DRILL_DATA*> board_drills;
std::map< std::string, IDF3_COMPONENT*> components; // drill and placement data for components
std::map< std::string, IDF3_COMP_OUTLINE*> compOutlines; // component outlines (data for library file)
std::string boardName;
IDF3::FILE_STATE state;
IDF3::CAD_TYPE cadType;
IDF3::IDF_UNIT unit;
std::string idfSource; // SOURCE string to use when writing BOARD and LIBRARY headers
std::string brdSource; // SOURCE string as retrieved from a BOARD file
std::string libSource; // SOURCE string as retrieved from a LIBRARY file
std::string brdDate; // DATE string from BOARD file
std::string libDate; // DATE string from LIBRARY file
int brdFileVersion; // File Version from BOARD file
int libFileVersion; // File Version from LIBRARY file
int userPrec; // user may store any integer here
double userScale; // user may store a scale for translating to arbitrary units
double userXoff; // user may specify an arbitrary X/Y offset
double userYoff;
// main board outline and cutouts
BOARD_OUTLINE olnBoard;
// OTHER outlines
std::map<std::string, OTHER_OUTLINE*> olnOther;
// ROUTE outlines
std::list<ROUTE_OUTLINE*> olnRoute;
// PLACEMENT outlines
std::list<PLACE_OUTLINE*> olnPlace;
// ROUTE KEEPOUT outlines
std::list<ROUTE_KO_OUTLINE*> olnRouteKeepout;
// VIA KEEPOUT outlines
std::list<VIA_KO_OUTLINE*> olnViaKeepout;
// PLACE KEEPOUT outlines
std::list<PLACE_KO_OUTLINE*> olnPlaceKeepout;
// PLACEMENT GROUP outlines
std::multimap<std::string, GROUP_OUTLINE*> olnGroup;
// Set the unit; this can only be done internally upon
// reading a file or saving
bool setUnit( IDF3::IDF_UNIT aUnit, bool convert = false );
IDF_DRILL_DATA* addCompDrill( double aDia, double aXpos, double aYpos,
IDF3::KEY_PLATING aPlating,
const std::string aHoleType,
IDF3::KEY_OWNER aOwner,
const std::string& aRefDes );
IDF_DRILL_DATA* addCompDrill( IDF_DRILL_DATA* aDrilledHole );
bool delCompDrill( double aDia, double aXpos, double aYpos, std::string aRefDes );
// read the DRILLED HOLES section
bool readBrdDrills( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState );
// read the NOTES section
bool readBrdNotes( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState );
// read the component placement section
bool readBrdPlacement( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState );
// read the board HEADER
bool readBrdHeader( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState );
// read individual board sections; pay attention to IDFv3 section specifications
bool readBrdSection( std::ifstream& aBoardFile, IDF3::FILE_STATE& aBoardState );
// read the board file data
bool readBoardFile( const std::string& aFileName );
// write the board file data
bool writeBoardFile( const std::string& aFileName );
// read the library sections (outlines)
bool readLibSection( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState, IDF3_BOARD* aBoard );
// read the library HEADER
bool readLibHeader( std::ifstream& aLibFile, IDF3::FILE_STATE& aLibState );
// read the library file data
bool readLibFile( const std::string& aFileName );
// write the library file data
bool writeLibFile( const std::string& aFileName );
public:
IDF3_BOARD( IDF3::CAD_TYPE aCadType );
virtual ~IDF3_BOARD();
IDF3::CAD_TYPE GetCadType( void );
// retrieve the nominal unit used in reading/writing
// data. This is primarily for use by owned objects
// and is only of informational use for the end user.
// Internally all data is represented in mm and the
// end user must use only mm in the API.
IDF3::IDF_UNIT GetUnit( void );
void SetBoardName( std::string aBoardName );
const std::string& GetBoardName( void );
bool SetBoardThickness( double aBoardThickness );
double GetBoardThickness( void );
bool ReadFile( const wxString& aFullFileName );
bool WriteFile( const wxString& aFullFileName, bool aUnitMM = true, bool aForceUnitFlag = false );
const std::string& GetIDFSource( void );
void SetIDFSource( const std::string& aIDFSource);
const std::string& GetBoardSource( void );
const std::string& GetLibrarySource( void );
const std::string& GetBoardDate( void );
const std::string& GetLibraryDate( void );
int GetBoardVersion( void );
bool SetBoardVersion( int aVersion );
int GetLibraryVersion( void );
bool SetLibraryVersion( int aVersion );
double GetUserScale( void );
bool SetUserScale( double aScaleFactor );
int GetUserPrecision( void );
bool SetUserPrecision( int aPrecision );
void GetUserOffset( double& aXoff, double& aYoff );
void SetUserOffset( double aXoff, double aYoff );
bool AddBoardOutline( IDF_OUTLINE* aOutline );
bool DelBoardOutline( IDF_OUTLINE* aOutline );
bool DelBoardOutline( size_t aIndex );
size_t GetBoardOutlinesSize( void );
BOARD_OUTLINE* GetBoardOutline( void );
const std::list< IDF_OUTLINE* >*const GetBoardOutlines( void );
/// XXX - TO BE IMPLEMENTED
// AddDrillComment
// AddPlacementComment
// GetDrillComments()
// GetPlacementComments()
// ClearDrillComments()
// ClearPlacementComments()
// AddNoteComment
// GetNoteComments
// AddNote
//
// const std::map<std::string, OTHER_OUTLINE*>*const GetOtherOutlines()
// size_t GetOtherOutlinesSize()
// OTHER_OUTLINE* AddOtherOutline( OTHER_OUTLINE* aOtherOutline )
// bool DelOtherOutline( int aIndex )
// bool DelOtherOutline( OTHER_OUTLINE* aOtherOutline )
//
// const std::list<ROUTE_OUTLINE*>*const GetRouteOutlines()
// size_t GetRouteOutlinesSize()
// ROUTE_OUTLINE* AddRouteOutline( ROUTE_OUTLINE* aRouteOutline )
// bool DelRouteOutline( int aIndex )
// bool DelRouteOutline( ROUTE_OUTLINE* aRouteOutline )
//
// const std::list<PLACE_OUTLINE*>*const GetPlacementOutlines()
// size_t GetPlacementOutlinesSize()
// PLACE_OUTLINE* AddPlacementOutline( PLACE_OUTLINE* aPlaceOutline )
// bool DelPlacementOutline( int aIndex )
// bool DelPlacementOutline( PLACE_OUTLINE* aPlaceOutline )
//
// const std::list<ROUTE_KO_OUTLINE*>*const GetRouteKeepOutOutlines()
// size_t GetRouteKeepOutOutlinesSize()
// ROUTE_KO_OUTLINE* AddRouteKeepoutOutline( ROUTE_KO_OUTLINE* aRouteKeepOut )
// bool DelRouteKeepOutOutline( int aIndex )
// bool DelRouteKeepOutOutline( ROUTE_KO_OUTLINE* aRouteKeepOut )
//
// const std::list<VIA_KO_OUTLINE*>*const GetViaKeepOutOutlines()
// size_t GetViaKeepOutOutlinesSize()
// VIA_KO_OUTLINE* AddViaKeepoutOutline( VIA_KO_OUTLINE* aViaKeepOut )
// bool DelViaKeepOutOutline( int aIndex )
// bool DelViaKeepOutOutline( VIA_KO_OUTLINE* aViaKeepOut )
//
// const std::list<PLACE_KO_OUTLINE*>*const GetPlacementKeepOutOutlines()
// size_t GetPlacementKeepOutOutlinesSize()
// PLACE_KO_OUTLINE* AddPlacementKeepoutOutline( PLACE_KO_OUTLINE* aPlaceKeepOut )
// bool DelPlacementKeepOutOutline( int aIndex )
// bool DelPlacementKeepOutOutline( PLACE_KO_OUTLINE* aPlaceKeepOut )
//
// const std::multimap<std::string, GROUP_OUTLINE*>*const GetGroupOutlines()
// size_t GetGroupOutlinesSize()
// GROUP_OUTLINE* AddGroupOutline( GROUP_OUTLINE* aGroupOutline )
// bool DelGroupOutline( int aIndex )
// bool DelGroupOutline( GROUP_OUTLINE* aGroupOutline )
std::list<IDF_DRILL_DATA*>& GetBoardDrills( void )
{
return board_drills;
}
IDF_DRILL_DATA* AddBoardDrill( double aDia, double aXpos, double aYpos,
IDF3::KEY_PLATING aPlating,
const std::string aHoleType,
IDF3::KEY_OWNER aOwner );
IDF_DRILL_DATA* AddDrill( IDF_DRILL_DATA* aDrilledHole );
bool DelBoardDrill( double aDia, double aXpos, double aYpos );
// a slot is a deficient representation of a kicad slotted hole;
// it is usually associated with a component but IDFv3 does not
// provide for such an association.
bool AddSlot( double aWidth, double aLength, double aOrientation, double aX, double aY );
bool AddComponent( IDF3_COMPONENT* aComponent );
bool DelComponent( IDF3_COMPONENT* aComponent );
bool DelComponent( size_t aIndex );
size_t GetComponentsSize( void );
std::map< std::string, IDF3_COMPONENT* >*const GetComponents( void );
IDF3_COMPONENT* FindComponent( std::string aRefDes );
// returns a pointer to a component outline object or NULL
// if the object doesn't exist
IDF3_COMP_OUTLINE* GetComponentOutline( const std::string aGeomName,
const std::string aPartName,
wxString aFullFileName );
// returns a pointer to the component outline object with the
// unique ID aComponentID
IDF3_COMP_OUTLINE* GetComponentOutline( std::string aComponentID );
// returns a pointer to the outline "NOGEOM NOPART" which is substituted
// whenever a true outline cannot be found or is defective
IDF3_COMP_OUTLINE* GetInvalidOutline( const std::string& aGeomName, const std::string& aPartName );
// clears all data
void Clear( void );
};
#endif // IDF_PARSER_H
......@@ -98,7 +98,9 @@ int main( int argc, char **argv )
tstr.clear();
tstr.str( line );
if( (tstr >> width) && width >= 0.001 )
tstr >> width;
if( !tstr.fail() && width >= 0.001 )
ok = true;
}
......@@ -112,7 +114,9 @@ int main( int argc, char **argv )
tstr.clear();
tstr.str( line );
if( (tstr >> length) && length > 0.0 )
tstr >> length;
if( !tstr.fail() && length > 0.0 )
ok = true;
}
......@@ -126,7 +130,9 @@ int main( int argc, char **argv )
tstr.clear();
tstr.str( line );
if( (tstr >> height) && height >= 0.001 )
tstr >> height;
if( !tstr.fail() && height >= 0.001 )
ok = true;
}
......@@ -140,7 +146,9 @@ int main( int argc, char **argv )
tstr.clear();
tstr.str( line );
if( (tstr >> chamfer) && chamfer >= 0.0 )
tstr >> chamfer;
if( !tstr.fail() && chamfer >= 0.0 )
{
if( chamfer > width / 3.0 || chamfer > length / 3.0 )
cout << "* WARNING: chamfer must be <= MIN( width, length )/3\n";
......@@ -182,7 +190,9 @@ int main( int argc, char **argv )
tstr.clear();
tstr.str( line );
if( (tstr >> wireDia) && wireDia >= 0.001 )
tstr >> wireDia;
if( !tstr.fail() && wireDia >= 0.001 )
{
if( wireDia >= length )
cout << "* WARNING: wire diameter must be < length\n";
......@@ -201,7 +211,9 @@ int main( int argc, char **argv )
tstr.clear();
tstr.str( line );
if( (tstr >> pitch) && pitch >= 0.001 )
tstr >> pitch;
if( !tstr.fail() && pitch >= 0.001 )
{
if( pitch <= ( length + wireDia ) / 2.0 )
cout << "* WARNING: pitch must be > (length + wireDia)/2\n";
......
/*
* file: vrml_layer.cpp
*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 Cirilo Bernardo
*
* 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
*/
/*
* NOTES ON OUTPUT PRECISION:
*
* If we use %.6f then we have no need for special unit dependent formatting:
*
* inch: .0254 microns
* mm: 0.001 microns
* m: 1 micron
*
*/
#include <sstream>
#include <string>
#include <iomanip>
#include <cmath>
#include <vrml_layer.h>
#include <wx/glcanvas.h> // CALLBACK definition, needed on Windows
#ifndef CALLBACK
#define CALLBACK
#endif
#define GLCALLBACK(x) (( void (CALLBACK*)() )&(x))
// minimum sides to a circle
#define MIN_NSIDES 6
static void FormatDoublet( double x, double y, int precision, std::string& strx, std::string& stry )
{
std::ostringstream ostr;
ostr << std::fixed << std::setprecision( precision );
ostr << x;
strx = ostr.str();
ostr.str( "" );
ostr << y;
stry = ostr.str();
while( *strx.rbegin() == '0' )
strx.erase( strx.size() - 1 );
while( *stry.rbegin() == '0' )
stry.erase( stry.size() - 1 );
}
static void FormatSinglet( double x, int precision, std::string& strx )
{
std::ostringstream ostr;
ostr << std::fixed << std::setprecision( precision );
ostr << x;
strx = ostr.str();
while( *strx.rbegin() == '0' )
strx.erase( strx.size() - 1 );
}
int VRML_LAYER::calcNSides( double aRadius, double aAngle )
{
// check #segments on ends of arc
int maxSeg = maxArcSeg * aAngle / M_PI;
if( maxSeg < 3 )
maxSeg = 3;
int csides = aRadius * M_PI / minSegLength;
if( csides < 0 )
csides = -csides;
if( csides > maxSeg )
{
if( csides < 2 * maxSeg )
csides /= 2;
else
csides = (((double) csides) * minSegLength / maxSegLength );
}
if( csides < 3 )
csides = 3;
if( (csides & 1) == 0 )
csides += 1;
return csides;
}
static void CALLBACK vrml_tess_begin( GLenum cmd, void* user_data )
{
VRML_LAYER* lp = (VRML_LAYER*) user_data;
lp->glStart( cmd );
}
static void CALLBACK vrml_tess_end( void* user_data )
{
VRML_LAYER* lp = (VRML_LAYER*) user_data;
lp->glEnd();
}
static void CALLBACK vrml_tess_vertex( void* vertex_data, void* user_data )
{
VRML_LAYER* lp = (VRML_LAYER*) user_data;
lp->glPushVertex( (VERTEX_3D*) vertex_data );
}
static void CALLBACK vrml_tess_err( GLenum errorID, void* user_data )
{
VRML_LAYER* lp = (VRML_LAYER*) user_data;
lp->Fault = true;
lp->SetGLError( errorID );
}
static void CALLBACK vrml_tess_combine( GLdouble coords[3], void* vertex_data[4],
GLfloat weight[4], void** outData, void* user_data )
{
VRML_LAYER* lp = (VRML_LAYER*) user_data;
*outData = lp->AddExtraVertex( coords[0], coords[1] );
}
VRML_LAYER::VRML_LAYER()
{
// arc parameters suitable to mm measurements
maxArcSeg = 48;
minSegLength = 0.1;
maxSegLength = 0.5;
fix = false;
Fault = false;
idx = 0;
ord = 0;
glcmd = 0;
pholes = NULL;
tess = gluNewTess();
if( !tess )
return;
// set up the tesselator callbacks
gluTessCallback( tess, GLU_TESS_BEGIN_DATA, GLCALLBACK( vrml_tess_begin ) );
gluTessCallback( tess, GLU_TESS_VERTEX_DATA, GLCALLBACK( vrml_tess_vertex ) );
gluTessCallback( tess, GLU_TESS_END_DATA, GLCALLBACK( vrml_tess_end ) );
gluTessCallback( tess, GLU_TESS_ERROR_DATA, GLCALLBACK( vrml_tess_err ) );
gluTessCallback( tess, GLU_TESS_COMBINE_DATA, GLCALLBACK( vrml_tess_combine ) );
gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE );
gluTessNormal( tess, 0, 0, 1 );
}
VRML_LAYER::~VRML_LAYER()
{
Clear();
if( tess )
{
gluDeleteTess( tess );
tess = NULL;
}
}
void VRML_LAYER::GetArcParams( int& aMaxSeg, double& aMinLength, double& aMaxLength )
{
aMaxSeg = maxArcSeg;
aMinLength = minSegLength;
aMaxLength = maxSegLength;
}
bool VRML_LAYER::SetArcParams( int aMaxSeg, double aMinLength, double aMaxLength )
{
if( aMaxSeg < 8 )
aMaxSeg = 8;
if( aMinLength <= 0 || aMaxLength <= aMinLength )
return false;
maxArcSeg = aMaxSeg;
minSegLength = aMinLength;
maxSegLength = aMaxLength;
return true;
}
// clear all data
void VRML_LAYER::Clear( void )
{
int i;
fix = false;
idx = 0;
for( i = contours.size(); i > 0; --i )
{
delete contours.back();
contours.pop_back();
}
while( !areas.empty() )
areas.pop_back();
for( i = vertices.size(); i > 0; --i )
{
delete vertices.back();
vertices.pop_back();
}
clearTmp();
}
// clear ephemeral data in between invocations of the tesselation routine
void VRML_LAYER::clearTmp( void )
{
unsigned int i;
Fault = false;
hidx = 0;
eidx = 0;
ord = 0;
glcmd = 0;
while( !triplets.empty() )
triplets.pop_back();
for( i = outline.size(); i > 0; --i )
{
delete outline.back();
outline.pop_back();
}
for( i = ordmap.size(); i > 0; --i )
ordmap.pop_back();
for( i = extra_verts.size(); i > 0; --i )
{
delete extra_verts.back();
extra_verts.pop_back();
}
// note: unlike outline and extra_verts,
// vlist is not responsible for memory management
for( i = vlist.size(); i > 0; --i )
vlist.pop_back();
// go through the vertex list and reset ephemeral parameters
for( i = 0; i < vertices.size(); ++i )
{
vertices[i]->o = -1;
}
}
// create a new contour to be populated; returns an index
// into the contour list or -1 if there are problems
int VRML_LAYER::NewContour( void )
{
if( fix )
return -1;
std::list<int>* contour = new std::list<int>;
if( !contour )
return -1;
contours.push_back( contour );
areas.push_back( 0.0 );
return contours.size() - 1;
}
// adds a vertex to the existing list and places its index in
// an existing contour; returns true if OK,
// false otherwise (indexed contour does not exist)
bool VRML_LAYER::AddVertex( int aContourID, double aXpos, double aYpos )
{
if( fix )
{
error = "AddVertex(): no more vertices may be added (Tesselate was previously executed)";
return false;
}
if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
{
error = "AddVertex(): aContour is not within a valid range";
return false;
}
VERTEX_3D* vertex = new VERTEX_3D;
if( !vertex )
{
error = "AddVertex(): a new vertex could not be allocated";
return false;
}
vertex->x = aXpos;
vertex->y = aYpos;
vertex->i = idx++;
vertex->o = -1;
VERTEX_3D* v2 = NULL;
if( contours[aContourID]->size() > 0 )
v2 = vertices[ contours[aContourID]->back() ];
vertices.push_back( vertex );
contours[aContourID]->push_back( vertex->i );
if( v2 )
areas[aContourID] += ( aXpos - v2->x ) * ( aYpos + v2->y );
return true;
}
// ensure the winding of a contour with respect to the normal (0, 0, 1);
// set 'hole' to true to ensure a hole (clockwise winding)
bool VRML_LAYER::EnsureWinding( int aContourID, bool aHoleFlag )
{
if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
{
error = "EnsureWinding(): aContour is outside the valid range";
return false;
}
std::list<int>* cp = contours[aContourID];
if( cp->size() < 3 )
{
error = "EnsureWinding(): there are fewer than 3 vertices";
return false;
}
double dir = areas[aContourID];
VERTEX_3D* vp0 = vertices[ cp->back() ];
VERTEX_3D* vp1 = vertices[ cp->front() ];
dir += ( vp1->x - vp0->x ) * ( vp1->y + vp0->y );
// if dir is positive, winding is CW
if( ( aHoleFlag && dir < 0 ) || ( !aHoleFlag && dir > 0 ) )
{
cp->reverse();
areas[aContourID] = -areas[aContourID];
}
return true;
}
bool VRML_LAYER::AppendCircle( double aXpos, double aYpos,
double aRadius, int aContourID,
bool aHoleFlag )
{
if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
{
error = "AppendCircle(): invalid contour (out of range)";
return false;
}
int nsides = M_PI * 2.0 * aRadius / minSegLength;
if( nsides > maxArcSeg )
{
if( nsides > 2 * maxArcSeg )
{
// use segments approx. maxAr
nsides = M_PI * 2.0 * aRadius / maxSegLength;
}
else
{
nsides /= 2;
}
}
if( nsides < MIN_NSIDES )
nsides = MIN_NSIDES;
// even numbers give prettier results for circles
if( nsides & 1 )
nsides += 1;
double da = M_PI * 2.0 / nsides;
bool fail = false;
if( aHoleFlag )
{
fail |= !AddVertex( aContourID, aXpos + aRadius, aYpos );
for( double angle = da; angle < M_PI * 2; angle += da )
fail |= !AddVertex( aContourID, aXpos + aRadius * cos( angle ),
aYpos - aRadius * sin( angle ) );
}
else
{
fail |= !AddVertex( aContourID, aXpos + aRadius, aYpos );
for( double angle = da; angle < M_PI * 2; angle += da )
fail |= !AddVertex( aContourID, aXpos + aRadius * cos( angle ),
aYpos + aRadius * sin( angle ) );
}
return !fail;
}
// adds a circle the existing list; if 'hole' is true the contour is
// a hole. Returns true if OK.
bool VRML_LAYER::AddCircle( double aXpos, double aYpos, double aRadius, bool aHoleFlag )
{
int pad = NewContour();
if( pad < 0 )
{
error = "AddCircle(): failed to add a contour";
return false;
}
return AppendCircle( aXpos, aYpos, aRadius, pad, aHoleFlag );
}
// adds a slotted pad with orientation given by angle; if 'hole' is true the
// contour is a hole. Returns true if OK.
bool VRML_LAYER::AddSlot( double aCenterX, double aCenterY,
double aSlotLength, double aSlotWidth,
double aAngle, bool aHoleFlag )
{
aAngle *= M_PI / 180.0;
if( aSlotWidth > aSlotLength )
{
aAngle += M_PI2;
std::swap( aSlotLength, aSlotWidth );
}
aSlotWidth /= 2.0;
aSlotLength = aSlotLength / 2.0 - aSlotWidth;
int csides = calcNSides( aSlotWidth, M_PI );
double capx, capy;
capx = aCenterX + cos( aAngle ) * aSlotLength;
capy = aCenterY + sin( aAngle ) * aSlotLength;
double ang, da;
int i;
int pad = NewContour();
if( pad < 0 )
{
error = "AddCircle(): failed to add a contour";
return false;
}
da = M_PI / csides;
bool fail = false;
if( aHoleFlag )
{
for( ang = aAngle + M_PI2, i = 0; i < csides; ang -= da, ++i )
fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
capy + aSlotWidth * sin( ang ) );
ang = aAngle - M_PI2;
fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
capy + aSlotWidth * sin( ang ) );
capx = aCenterX - cos( aAngle ) * aSlotLength;
capy = aCenterY - sin( aAngle ) * aSlotLength;
for( ang = aAngle - M_PI2, i = 0; i < csides; ang -= da, ++i )
fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
capy + aSlotWidth * sin( ang ) );
ang = aAngle + M_PI2;
fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
capy + aSlotWidth * sin( ang ) );
}
else
{
for( ang = aAngle - M_PI2, i = 0; i < csides; ang += da, ++i )
fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
capy + aSlotWidth * sin( ang ) );
ang = aAngle + M_PI2;
fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
capy + aSlotWidth * sin( ang ) );
capx = aCenterX - cos( aAngle ) * aSlotLength;
capy = aCenterY - sin( aAngle ) * aSlotLength;
for( ang = aAngle + M_PI2, i = 0; i < csides; ang += da, ++i )
fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
capy + aSlotWidth * sin( ang ) );
ang = aAngle - M_PI2;
fail |= !AddVertex( pad, capx + aSlotWidth * cos( ang ),
capy + aSlotWidth * sin( ang ) );
}
return !fail;
}
// adds an arc to the given center, start point, pen width, and angle (degrees).
bool VRML_LAYER::AppendArc( double aCenterX, double aCenterY, double aRadius,
double aStartAngle, double aAngle, int aContourID )
{
if( aContourID < 0 || (unsigned int) aContourID >= contours.size() )
{
error = "AppendArc(): invalid contour (out of range)";
return false;
}
aAngle = aAngle / 180.0 * M_PI;
aStartAngle = aStartAngle / 180.0 * M_PI;
int nsides = calcNSides( aRadius, aAngle );
double da = aAngle / nsides;
bool fail = false;
if( aAngle > 0 )
{
aAngle += aStartAngle;
for( double ang = aStartAngle; ang < aAngle; ang += da )
fail |= !AddVertex( aContourID, aCenterX + aRadius * cos( ang ),
aCenterY + aRadius * sin( ang ) );
}
else
{
aAngle += aStartAngle;
for( double ang = aStartAngle; ang > aAngle; ang += da )
fail |= !AddVertex( aContourID, aCenterX + aRadius * cos( ang ),
aCenterY + aRadius * sin( ang ) );
}
return !fail;
}
// adds an arc with the given center, start point, pen width, and angle (degrees).
bool VRML_LAYER::AddArc( double aCenterX, double aCenterY, double aStartX, double aStartY,
double aArcWidth, double aAngle, bool aHoleFlag )
{
aAngle *= M_PI / 180.0;
// we don't accept small angles; in fact, 1 degree ( 0.01745 ) is already
// way too small but we must set a limit somewhere
if( aAngle < 0.01745 && aAngle > -0.01745 )
{
error = "AddArc(): angle is too small: abs( angle ) < 1 degree";
return false;
}
double rad = sqrt( (aStartX - aCenterX) * (aStartX - aCenterX)
+ (aStartY - aCenterY) * (aStartY - aCenterY) );
aArcWidth /= 2.0; // this is the radius of the caps
// we will not accept an arc with an inner radius close to zero so we
// set a limit here. the end result will vary somewhat depending on
// the output units
if( aArcWidth >= ( rad * 1.01 ) )
{
error = "AddArc(): width/2 exceeds radius*1.01";
return false;
}
// calculate the radii of the outer and inner arcs
double orad = rad + aArcWidth;
double irad = rad - aArcWidth;
int osides = calcNSides( orad, aAngle );
int isides = calcNSides( irad, aAngle );
int csides = calcNSides( aArcWidth, M_PI );
double stAngle = atan2( aStartY - aCenterY, aStartX - aCenterX );
double endAngle = stAngle + aAngle;
// calculate ends of inner and outer arc
double oendx = aCenterX + orad* cos( endAngle );
double oendy = aCenterY + orad* sin( endAngle );
double ostx = aCenterX + orad* cos( stAngle );
double osty = aCenterY + orad* sin( stAngle );
double iendx = aCenterX + irad* cos( endAngle );
double iendy = aCenterY + irad* sin( endAngle );
double istx = aCenterX + irad* cos( stAngle );
double isty = aCenterY + irad* sin( stAngle );
if( ( aAngle < 0 && !aHoleFlag ) || ( aAngle > 0 && aHoleFlag ) )
{
aAngle = -aAngle;
std::swap( stAngle, endAngle );
std::swap( oendx, ostx );
std::swap( oendy, osty );
std::swap( iendx, istx );
std::swap( iendy, isty );
}
int arc = NewContour();
if( arc < 0 )
{
error = "AddArc(): could not create a contour";
return false;
}
// trace the outer arc:
int i;
double ang;
double da = aAngle / osides;
for( ang = stAngle, i = 0; i < osides; ang += da, ++i )
AddVertex( arc, aCenterX + orad * cos( ang ), aCenterY + orad * sin( ang ) );
// trace the first cap
double capx = ( iendx + oendx ) / 2.0;
double capy = ( iendy + oendy ) / 2.0;
if( aHoleFlag )
da = -M_PI / csides;
else
da = M_PI / csides;
for( ang = endAngle, i = 0; i < csides; ang += da, ++i )
AddVertex( arc, capx + aArcWidth * cos( ang ), capy + aArcWidth * sin( ang ) );
// trace the inner arc:
da = -aAngle / isides;
for( ang = endAngle, i = 0; i < isides; ang += da, ++i )
AddVertex( arc, aCenterX + irad * cos( ang ), aCenterY + irad * sin( ang ) );
// trace the final cap
capx = ( istx + ostx ) / 2.0;
capy = ( isty + osty ) / 2.0;
if( aHoleFlag )
da = -M_PI / csides;
else
da = M_PI / csides;
for( ang = stAngle + M_PI, i = 0; i < csides; ang += da, ++i )
AddVertex( arc, capx + aArcWidth * cos( ang ), capy + aArcWidth * sin( ang ) );
return true;
}
// tesselates the contours in preparation for a 3D output;
// returns true if all was fine, false otherwise
bool VRML_LAYER::Tesselate( VRML_LAYER* holes )
{
if( !tess )
{
error = "Tesselate(): GLU tesselator was not initialized";
return false;
}
pholes = holes;
Fault = false;
if( contours.size() < 1 || vertices.size() < 3 )
{
error = "Tesselate(): not enough vertices";
return false;
}
// finish the winding calculation on all vertices prior to setting 'fix'
if( !fix )
{
for( unsigned int i = 0; i < contours.size(); ++i )
{
if( contours[i]->size() < 3 )
continue;
VERTEX_3D* vp0 = vertices[ contours[i]->back() ];
VERTEX_3D* vp1 = vertices[ contours[i]->front() ];
areas[i] += ( vp1->x - vp0->x ) * ( vp1->y + vp0->y );
}
}
// prevent the addition of any further contours and contour vertices
fix = true;
// clear temporary internals which may have been used in a previous run
clearTmp();
// request an outline
gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_TRUE );
// adjust internal indices for extra points and holes
if( holes )
hidx = holes->GetSize();
else
hidx = 0;
eidx = idx + hidx;
// open the polygon
gluTessBeginPolygon( tess, this );
pushVertices( false );
// close the polygon
gluTessEndPolygon( tess );
if( Fault )
return false;
// push the (solid) outline to the tesselator
if( !pushOutline( holes ) )
return false;
// add the holes contained by this object
pushVertices( true );
// import external holes (if any)
if( hidx && ( holes->Import( idx, tess ) < 0 ) )
{
std::ostringstream ostr;
ostr << "Tesselate():FAILED: " << holes->GetError();
error = ostr.str();
return NULL;
}
if( Fault )
return false;
// erase the previous outline data and vertex order
// but preserve the extra vertices
for( int i = outline.size(); i > 0; --i )
{
delete outline.back();
outline.pop_back();
}
for( unsigned int i = ordmap.size(); i > 0; --i )
ordmap.pop_back();
// go through the vertex lists and reset ephemeral parameters
for( unsigned int i = 0; i < vertices.size(); ++i )
{
vertices[i]->o = -1;
}
for( unsigned int i = 0; i < extra_verts.size(); ++i )
{
extra_verts[i]->o = -1;
}
ord = 0;
// close the polygon; we now have all the data necessary for the tesselation
gluTessEndPolygon( tess );
// request a tesselated surface
gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE );
if( !pushOutline( holes ) )
return false;
gluTessEndPolygon( tess );
if( Fault )
return false;
return true;
}
bool VRML_LAYER::pushOutline( VRML_LAYER* holes )
{
// traverse the outline list to push all used vertices
if( outline.size() < 1 )
{
error = "pushOutline() failed: no vertices to push";
return false;
}
gluTessBeginPolygon( tess, this );
std::list<std::list<int>*>::const_iterator obeg = outline.begin();
std::list<std::list<int>*>::const_iterator oend = outline.end();
int pi;
std::list<int>::const_iterator begin;
std::list<int>::const_iterator end;
GLdouble pt[3];
VERTEX_3D* vp;
while( obeg != oend )
{
if( (*obeg)->size() < 3 )
{
++obeg;
continue;
}
gluTessBeginContour( tess );
begin = (*obeg)->begin();
end = (*obeg)->end();
while( begin != end )
{
pi = *begin;
if( pi < 0 || (unsigned int) pi > ordmap.size() )
{
error = "pushOutline():BUG: *outline.begin() is not a valid index to ordmap";
return false;
}
// retrieve the actual index
pi = ordmap[pi];
vp = getVertexByIndex( pi, holes );
if( !vp )
{
error = "pushOutline():: BUG: ordmap[n] is not a valid index to vertices[]";
return false;
}
pt[0] = vp->x;
pt[1] = vp->y;
pt[2] = 0.0;
gluTessVertex( tess, pt, vp );
++begin;
}
gluTessEndContour( tess );
++obeg;
}
return true;
}
// writes out the vertex list for a planar feature
bool VRML_LAYER::WriteVertices( double aZcoord, std::ofstream& aOutFile, int aPrecision )
{
if( ordmap.size() < 3 )
{
error = "WriteVertices(): not enough vertices";
return false;
}
if( aPrecision < 4 )
aPrecision = 4;
int i, j;
VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
if( !vp )
return false;
std::string strx, stry, strz;
FormatDoublet( vp->x, vp->y, aPrecision, strx, stry );
FormatSinglet( aZcoord, aPrecision, strz );
aOutFile << strx << " " << stry << " " << strz;
for( i = 1, j = ordmap.size(); i < j; ++i )
{
vp = getVertexByIndex( ordmap[i], pholes );
if( !vp )
return false;
FormatDoublet( vp->x, vp->y, aPrecision, strx, stry );
if( i & 1 )
aOutFile << ", " << strx << " " << stry << " " << strz;
else
aOutFile << ",\n" << strx << " " << stry << " " << strz;
}
return !aOutFile.fail();
}
// writes out the vertex list for a 3D feature; top and bottom are the
// Z values for the top and bottom; top must be > bottom
bool VRML_LAYER::Write3DVertices( double aTopZ, double aBottomZ,
std::ofstream& aOutFile, int aPrecision )
{
if( ordmap.size() < 3 )
{
error = "Write3DVertices(): insufficient vertices";
return false;
}
if( aPrecision < 4 )
aPrecision = 4;
if( aTopZ <= aBottomZ )
{
error = "Write3DVertices(): top <= bottom";
return false;
}
int i, j;
VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
if( !vp )
return false;
std::string strx, stry, strz;
FormatDoublet( vp->x, vp->y, aPrecision, strx, stry );
FormatSinglet( aTopZ, aPrecision, strz );
aOutFile << strx << " " << stry << " " << strz;
for( i = 1, j = ordmap.size(); i < j; ++i )
{
vp = getVertexByIndex( ordmap[i], pholes );
if( !vp )
return false;
FormatDoublet( vp->x, vp->y, aPrecision, strx, stry );
if( i & 1 )
aOutFile << ", " << strx << " " << stry << " " << strz;
else
aOutFile << ",\n" << strx << " " << stry << " " << strz;
}
// repeat for the bottom layer
vp = getVertexByIndex( ordmap[0], pholes );
FormatDoublet( vp->x, vp->y, aPrecision, strx, stry );
FormatSinglet( aBottomZ, aPrecision, strz );
bool endl;
if( i & 1 )
{
aOutFile << ", " << strx << " " << stry << " " << strz;
endl = false;
}
else
{
aOutFile << ",\n" << strx << " " << stry << " " << strz;
endl = true;
}
for( i = 1, j = ordmap.size(); i < j; ++i )
{
vp = getVertexByIndex( ordmap[i], pholes );
FormatDoublet( vp->x, vp->y, aPrecision, strx, stry );
if( endl )
{
aOutFile << ", " << strx << " " << stry << " " << strz;
endl = false;
}
else
{
aOutFile << ",\n" << strx << " " << stry << " " << strz;
endl = true;
}
}
return !aOutFile.fail();
}
// writes out the index list;
// 'top' indicates the vertex ordering and should be
// true for a polygon visible from above the PCB
bool VRML_LAYER::WriteIndices( bool aTopFlag, std::ofstream& aOutFile )
{
if( triplets.empty() )
{
error = "WriteIndices(): no triplets (triangular facets) to write";
return false;
}
// go through the triplet list and write out the indices based on order
std::list<TRIPLET_3D>::const_iterator tbeg = triplets.begin();
std::list<TRIPLET_3D>::const_iterator tend = triplets.end();
int i = 1;
if( aTopFlag )
aOutFile << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
else
aOutFile << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3 << ", -1";
++tbeg;
while( tbeg != tend )
{
if( (i++ & 7) == 4 )
{
i = 1;
if( aTopFlag )
aOutFile << ",\n" << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
else
aOutFile << ",\n" << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3 << ", -1";
}
else
{
if( aTopFlag )
aOutFile << ", " << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
else
aOutFile << ", " << tbeg->i2 << ", " << tbeg->i1 << ", " << tbeg->i3 << ", -1";
}
++tbeg;
}
return !aOutFile.fail();
}
// writes out the index list for a 3D feature
bool VRML_LAYER::Write3DIndices( std::ofstream& aOutFile )
{
if( triplets.empty() )
{
error = "Write3DIndices(): no triplets (triangular facets) to write";
return false;
}
if( outline.empty() )
{
error = "WriteIndices(): no outline available";
return false;
}
// go through the triplet list and write out the indices based on order
std::list<TRIPLET_3D>::const_iterator tbeg = triplets.begin();
std::list<TRIPLET_3D>::const_iterator tend = triplets.end();
int i = 1;
int idx2 = ordmap.size(); // index to the bottom vertices
// print out the top vertices
aOutFile << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
++tbeg;
while( tbeg != tend )
{
if( (i++ & 7) == 4 )
{
i = 1;
aOutFile << ",\n" << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
}
else
{
aOutFile << ", " << tbeg->i1 << ", " << tbeg->i2 << ", " << tbeg->i3 << ", -1";
}
++tbeg;
}
// print out the bottom vertices
tbeg = triplets.begin();
while( tbeg != tend )
{
if( (i++ & 7) == 4 )
{
i = 1;
aOutFile << ",\n" << (tbeg->i2 + idx2) << ", " << (tbeg->i1 + idx2) << ", " << (tbeg->i3 + idx2) << ", -1";
}
else
{
aOutFile << ", " << (tbeg->i2 + idx2) << ", " << (tbeg->i1 + idx2) << ", " << (tbeg->i3 + idx2) << ", -1";
}
++tbeg;
}
// print out indices for the walls joining top to bottom
int firstPoint;
int lastPoint;
int curPoint;
std::list<std::list<int>*>::const_iterator obeg = outline.begin();
std::list<std::list<int>*>::const_iterator oend = outline.end();
std::list<int>* cp;
std::list<int>::const_iterator cbeg;
std::list<int>::const_iterator cend;
i = 2;
while( obeg != oend )
{
cp = *obeg;
if( cp->size() < 3 )
{
++obeg;
continue;
}
cbeg = cp->begin();
cend = cp->end();
firstPoint = *(cbeg++);
lastPoint = firstPoint;
while( cbeg != cend )
{
curPoint = *(cbeg++);
if( (i++ & 3) == 2 )
{
i = 1;
aOutFile << ",\n" << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
}
else
{
aOutFile << ", " << curPoint << ", " << lastPoint << ", " << curPoint + idx2;
aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
}
lastPoint = curPoint;
}
if( (i++ & 3) == 2 )
{
aOutFile << ",\n" << firstPoint << ", " << lastPoint << ", " << firstPoint + idx2;
aOutFile << ", -1, " << firstPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
}
else
{
aOutFile << ", " << firstPoint << ", " << lastPoint << ", " << firstPoint + idx2;
aOutFile << ", -1, " << firstPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1";
}
++obeg;
}
return !aOutFile.fail();
}
// add a triangular facet (triplet) to the ouptut index list
bool VRML_LAYER::addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 )
{
double dx0 = p1->x - p0->x;
double dx1 = p2->x - p0->x;
double dy0 = p1->y - p0->y;
double dy1 = p2->y - p0->y;
// this number is chosen because we shall only write 6 decimal places
// on the VRML output
double err = 0.000001;
// test if the triangles are degenerate (parallel sides)
if( dx0 < err && dx0 > -err && dx1 < err && dx1 > -err )
return false;
if( dy0 < err && dy0 > -err && dy1 < err && dy1 > -err )
return false;
double sl0 = dy0 / dx0;
double sl1 = dy1 / dx1;
double dsl = sl1 - sl0;
if( dsl < err && dsl > -err )
return false;
triplets.push_back( TRIPLET_3D( p0->o, p1->o, p2->o ) );
return true;
}
// add an extra vertex (to be called only by the COMBINE callback)
VERTEX_3D* VRML_LAYER::AddExtraVertex( double aXpos, double aYpos )
{
VERTEX_3D* vertex = new VERTEX_3D;
if( !vertex )
{
error = "AddExtraVertex(): could not allocate a new vertex";
return NULL;
}
if( eidx == 0 )
eidx = idx + hidx;
vertex->x = aXpos;
vertex->y = aYpos;
vertex->i = eidx++;
vertex->o = -1;
extra_verts.push_back( vertex );
return vertex;
}
// start a GL command list
void VRML_LAYER::glStart( GLenum cmd )
{
glcmd = cmd;
while( !vlist.empty() )
vlist.pop_back();
}
// process a vertex
void VRML_LAYER::glPushVertex( VERTEX_3D* vertex )
{
if( vertex->o < 0 )
{
vertex->o = ord++;
ordmap.push_back( vertex->i );
}
vlist.push_back( vertex );
}
// end a GL command list
void VRML_LAYER::glEnd( void )
{
switch( glcmd )
{
case GL_LINE_LOOP:
{
// add the loop to the list of outlines
std::list<int>* loop = new std::list<int>;
if( !loop )
break;
for( unsigned int i = 0; i < vlist.size(); ++i )
{
loop->push_back( vlist[i]->o );
}
outline.push_back( loop );
}
break;
case GL_TRIANGLE_FAN:
processFan();
break;
case GL_TRIANGLE_STRIP:
processStrip();
break;
case GL_TRIANGLES:
processTri();
break;
default:
break;
}
while( !vlist.empty() )
vlist.pop_back();
glcmd = 0;
}
// set the error message
void VRML_LAYER::SetGLError( GLenum errorID )
{
error = "";
error = (const char*)gluGetString( errorID );
if( error.empty() )
{
std::ostringstream ostr;
ostr << "Unknown OpenGL error: " << errorID;
error = ostr.str();
}
}
// process a GL_TRIANGLE_FAN list
void VRML_LAYER::processFan( void )
{
if( vlist.size() < 3 )
return;
VERTEX_3D* p0 = vlist[0];
int i;
int end = vlist.size();
for( i = 2; i < end; ++i )
{
addTriplet( p0, vlist[i - 1], vlist[i] );
}
}
// process a GL_TRIANGLE_STRIP list
void VRML_LAYER::processStrip( void )
{
// note: (source: http://www.opengl.org/wiki/Primitive)
// GL_TRIANGLE_STRIP​: Every group of 3 adjacent vertices forms a triangle.
// The face direction of the strip is determined by the winding of the
// first triangle. Each successive triangle will have its effective face
// order reverse, so the system compensates for that by testing it in the
// opposite way. A vertex stream of n length will generate n-2 triangles.
if( vlist.size() < 3 )
return;
int i;
int end = vlist.size();
bool flip = false;
for( i = 2; i < end; ++i )
{
if( flip )
{
addTriplet( vlist[i - 1], vlist[i - 2], vlist[i] );
flip = false;
}
else
{
addTriplet( vlist[i - 2], vlist[i - 1], vlist[i] );
flip = true;
}
}
}
// process a GL_TRIANGLES list
void VRML_LAYER::processTri( void )
{
// notes:
// 1. each successive group of 3 vertices is a triangle
// 2. as per OpenGL specification, any incomplete triangles are to be ignored
if( vlist.size() < 3 )
return;
int i;
int end = vlist.size();
for( i = 2; i < end; i += 3 )
addTriplet( vlist[i - 2], vlist[i - 1], vlist[i] );
}
// push the internally held vertices
void VRML_LAYER::pushVertices( bool holes )
{
// push the internally held vertices
unsigned int i;
std::list<int>::const_iterator begin;
std::list<int>::const_iterator end;
GLdouble pt[3];
VERTEX_3D* vp;
for( i = 0; i < contours.size(); ++i )
{
if( contours[i]->size() < 3 )
continue;
if( ( holes && areas[i] <= 0.0 ) || ( !holes && areas[i] > 0.0 ) )
continue;
gluTessBeginContour( tess );
begin = contours[i]->begin();
end = contours[i]->end();
while( begin != end )
{
vp = vertices[ *begin ];
pt[0] = vp->x;
pt[1] = vp->y;
pt[2] = 0.0;
gluTessVertex( tess, pt, vp );
++begin;
}
gluTessEndContour( tess );
}
}
VERTEX_3D* VRML_LAYER::getVertexByIndex( int aPointIndex, VRML_LAYER* holes )
{
if( aPointIndex < 0 || (unsigned int) aPointIndex >= ( idx + hidx + extra_verts.size() ) )
{
error = "getVertexByIndex():BUG: invalid index";
return NULL;
}
if( aPointIndex < idx )
{
// vertex is in the vertices[] list
return vertices[ aPointIndex ];
}
else if( aPointIndex >= idx + hidx )
{
// vertex is in the extra_verts[] list
return extra_verts[aPointIndex - idx - hidx];
}
// vertex is in the holes object
if( !holes )
{
error = "getVertexByIndex():BUG: invalid index";
return NULL;
}
VERTEX_3D* vp = holes->GetVertexByIndex( aPointIndex );
if( !vp )
{
std::ostringstream ostr;
ostr << "getVertexByIndex():FAILED: " << holes->GetError();
error = ostr.str();
return NULL;
}
return vp;
}
// retrieve the total number of vertices
int VRML_LAYER::GetSize( void )
{
return vertices.size();
}
// Inserts all contours into the given tesselator; this results in the
// renumbering of all vertices from 'start'. Returns the end number.
// Take care when using this call since tesselators cannot work on
// the internal data concurrently
int VRML_LAYER::Import( int start, GLUtesselator* tess )
{
if( start < 0 )
{
error = "Import(): invalid index ( start < 0 )";
return -1;
}
if( !tess )
{
error = "Import(): NULL tesselator pointer";
return -1;
}
unsigned int i, j;
// renumber from 'start'
for( i = 0, j = vertices.size(); i < j; ++i )
{
vertices[i]->i = start++;
vertices[i]->o = -1;
}
// push each contour to the tesselator
VERTEX_3D* vp;
GLdouble pt[3];
std::list<int>::const_iterator cbeg;
std::list<int>::const_iterator cend;
for( i = 0; i < contours.size(); ++i )
{
if( contours[i]->size() < 3 )
continue;
cbeg = contours[i]->begin();
cend = contours[i]->end();
gluTessBeginContour( tess );
while( cbeg != cend )
{
vp = vertices[ *cbeg++ ];
pt[0] = vp->x;
pt[1] = vp->y;
pt[2] = 0.0;
gluTessVertex( tess, pt, vp );
}
gluTessEndContour( tess );
}
return start;
}
// return the vertex identified by index
VERTEX_3D* VRML_LAYER::GetVertexByIndex( int aPointIndex )
{
int i0 = vertices[0]->i;
if( aPointIndex < i0 || aPointIndex >= ( i0 + (int) vertices.size() ) )
{
error = "GetVertexByIndex(): invalid index";
return NULL;
}
return vertices[aPointIndex - i0];
}
// return the error string
const std::string& VRML_LAYER::GetError( void )
{
return error;
}
/*
* file: vrml_layer.h
*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2013 Cirilo Bernardo
*
* 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
*/
/**
* @file vrml_layer.h
*/
/*
* Classes and structures to support the tesselation of a
* PCB for VRML output.
*/
#ifndef VRML_BOARD_H
#define VRML_BOARD_H
#ifdef __WXMAC__
# ifdef __DARWIN__
# include <OpenGL/glu.h>
# else
# include <glu.h>
# endif
#else
# include <GL/glu.h>
#endif
#include <fstream>
#include <vector>
#include <list>
#include <utility>
#ifndef M_PI2
#define M_PI2 ( M_PI / 2.0 )
#endif
#ifndef M_PI4
#define M_PI4 ( M_PI / 4.0 )
#endif
struct GLUtesselator;
struct VERTEX_3D
{
double x;
double y;
int i; // vertex index
int o; // vertex order
};
struct TRIPLET_3D
{
int i1, i2, i3;
TRIPLET_3D( int p1, int p2, int p3 )
{
i1 = p1;
i2 = p2;
i3 = p3;
}
};
class VRML_LAYER
{
private:
// Arc parameters
int maxArcSeg; // maximum number of arc segments in a small circle
double minSegLength; // min. segment length
double maxSegLength; // max. segment length
bool fix; // when true, no more vertices may be added by the user
int idx; // vertex index (number of contained vertices)
int ord; // vertex order (number of ordered vertices)
std::vector<VERTEX_3D*> vertices; // vertices of all contours
std::vector<std::list<int>*> contours; // lists of vertices for each contour
std::vector< double > areas; // area of the contours (positive if winding is CCW)
std::list<TRIPLET_3D> triplets; // output facet triplet list (triplet of ORDER values)
std::list<std::list<int>*> outline; // indices for outline outputs (index by ORDER values)
std::vector<int> ordmap; // mapping of ORDER to INDEX
std::string error; // error message
int hidx; // number of vertices in the holes
int eidx; // index for extra vertices
std::vector<VERTEX_3D*> extra_verts; // extra vertices added for outlines and facets
std::vector<VERTEX_3D*> vlist; // vertex list for the GL command in progress
VRML_LAYER* pholes; // pointer to another layer object used for tesselation;
// this object is normally expected to hold only holes
GLUtesselator* tess; // local instance of the GLU tesselator
GLenum glcmd; // current GL command type ( fan, triangle, tri-strip, loop )
void clearTmp( void ); // clear ephemeral data used by the tesselation routine
// add a triangular facet (triplet) to the output index list
bool addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 );
// retrieve a vertex given its index; the vertex may be contained in the
// vertices vector, extra_verts vector, or foreign VRML_LAYER object
VERTEX_3D* getVertexByIndex( int aPointIndex, VRML_LAYER* holes );
void processFan( void ); // process a GL_TRIANGLE_FAN list
void processStrip( void ); // process a GL_TRIANGLE_STRIP list
void processTri( void ); // process a GL_TRIANGLES list
void pushVertices( bool holes ); // push the internal vertices
bool pushOutline( VRML_LAYER* holes ); // push the outline vertices
// calculate number of sides on an arc (angle is in radians)
int calcNSides( double aRadius, double aAngle );
public:
/// set to true when a fault is encountered during tesselation
bool Fault;
VRML_LAYER();
virtual ~VRML_LAYER();
/**
* Function GetArcParams
* retieves the parameters used in calculating the number of vertices in an arc
*
* @param aMaxSeg is the maximum number of segments for an arc with cords of length aMinLength
* @param aMinLength is the minimum length of cords in an arc
* @param aMaxLength is the maximum length of cords in an arc
*/
void GetArcParams( int& aMaxSeg, double& aMinLength, double& aMaxLength );
/**
* Function SetArcParams
* sets the parameters used in calculating the number of vertices in an arc.
* The default settings are reasonable for rendering for unit lengths of 1mm
*
* @param aMaxSeg is the maximum number of segments for an arc with cords of length aMinLength
* @param aMinLength is the minimum length of cords in an arc
* @param aMaxLength is the maximum length of cords in an arc
*
* @return bool: true if the parameters were accepted
*/
bool SetArcParams( int aMaxSeg, double aMinLength, double aMaxLength );
/**
* Function Clear
* erases all data except for arc parameters.
*/
void Clear( void );
/**
* Function GetSize
* returns the total number of vertices indexed
*/
int GetSize( void );
/**
* Function GetNConours
* returns the number of stored contours
*/
int GetNContours( void )
{
return contours.size();
}
/**
* Function NewContour
* creates a new list of vertices and returns an index to the list
*
* @return int: index to the list or -1 if the operation failed
*/
int NewContour( void );
/**
* Function AddVertex
* adds a point to the requested contour
*
* @param aContour is an index previously returned by a call to NewContour()
* @param aXpos is the X coordinate of the vertex
* @param aYpos is the Y coordinate of the vertex
*
* @return bool: true if the vertex was added
*/
bool AddVertex( int aContourID, double aXpos, double aYpos );
/**
* Function EnsureWinding
* checks the winding of a contour and ensures that it is a hole or
* a solid depending on the value of @param hole
*
* @param aContour is an index to a contour as returned by NewContour()
* @param aHoleFlag determines if the contour must be a hole
*
* @return bool: true if the operation suceeded
*/
bool EnsureWinding( int aContourID, bool aHoleFlag );
/**
* Function AppendCircle
* adds a circular contour to the specified (empty) contour
*
* @param aXpos is the X coordinate of the hole center
* @param aYpos is the Y coordinate of the hole center
* @param aRadius is the radius of the hole
* @param aContourID is the contour index
* @param aHoleFlag determines if the contour to be created is a cutout
*
* @return bool: true if the new contour was successfully created
*/
bool AppendCircle( double aXpos, double aYpos, double aRadius, int aContourID, bool aHoleFlag = false );
/**
* Function AddCircle
* creates a circular contour and adds it to the internal list
*
* @param aXpos is the X coordinate of the hole center
* @param aYpos is the Y coordinate of the hole center
* @param aRadius is the radius of the hole
* @param aHoleFlag determines if the contour to be created is a cutout
*
* @return bool: true if the new contour was successfully created
*/
bool AddCircle( double aXpos, double aYpos, double aRadius, bool aHoleFlag = false );
/**
* Function AddSlot
* creates and adds a slot feature to the list of contours
*
* @param aCenterX is the X coordinate of the slot's center
* @param aCenterY is the Y coordinate of the slot's center
* @param aSlotLength is the length of the slot along the major axis
* @param aSlotWidth is the width of the slot along the minor axis
* @param aAngle (degrees) is the orientation of the slot
* @param aHoleFlag determines whether the slot is a hole or a solid
*
* @return bool: true if the slot was successfully created
*/
bool AddSlot( double aCenterX, double aCenterY, double aSlotLength, double aSlotWidth,
double aAngle, bool aHoleFlag = false );
/**
* Function AppendArc
* adds an arc to the specified contour
*
* @param aCenterX is the X coordinate of the arc's center
* @param aCenterY is the Y coordinate of the arc's center
* @param aRadius is the radius of the arc
* @param aStartAngle (degrees) is the starting angle of the arc
* @param aAngle (degrees) is the measure of the arc
* @param aContourID is the contour's index
*
* @return bool: true if the slot was successfully created
*/
bool AppendArc( double aCenterX, double aCenterY, double aRadius,
double aStartAngle, double aAngle, int aContourID );
/**
* Function AddArc
* creates a slotted arc and adds it to the internal list of contours
*
* @param aCenterX is the X coordinate of the arc's center
* @param aCenterY is the Y coordinate of the arc's center
* @param aStartX is the X coordinate of the starting point
* @param aStartY is the Y coordinate of the starting point
* @param aArcWidth is the width of the arc
* @param aAngle is the included angle (degrees)
* @param aHoleFlag determines whether the arc is to be a hole or a solid
*
* @return bool: true if the feature was successfully created
*/
bool AddArc( double aCenterX, double aCenterY, double aStartX, double aStartY,
double aArcWidth, double aAngle, bool aHoleFlag = false );
/**
* Function Tesselate
* creates a list of outline vertices as well as the
* vertex sets required to render the surface.
*
* @param holes is an optional pointer to cutouts to be imposed on the
* surface.
*
* @return bool: true if the operation succeeded
*/
bool Tesselate( VRML_LAYER* holes = NULL );
/**
* Function WriteVertices
* writes out the list of vertices required to render a
* planar surface.
*
* @param aZcoord is the Z coordinate of the plane
* @param aOutFile is the file to write to
* @param aPrecision is the precision of the output coordinates
*
* @return bool: true if the operation succeeded
*/
bool WriteVertices( double aZcoord, std::ofstream& aOutFile, int aPrecision );
/**
* Function Write3DVertices
* writes out the list of vertices required to render an extruded solid
*
* @param aTopZ is the Z coordinate of the top plane
* @param aBottomZ is the Z coordinate of the bottom plane
* @param aOutFile is the file to write to
* @param aPrecision is the precision of the output coordinates
*
* @return bool: true if the operation succeeded
*/
bool Write3DVertices( double aTopZ, double aBottomZ, std::ofstream& aOutFile, int aPrecision );
/**
* Function WriteIndices
* writes out the vertex sets required to render a planar
* surface.
*
* @param aTopFlag is true if the surface is to be visible from above;
* if false the surface will be visible from below.
* @param aOutFile is the file to write to
*
* @return bool: true if the operation succeeded
*/
bool WriteIndices( bool aTopFlag, std::ofstream& aOutFile );
/**
* Function Write3DIndices
* writes out the vertex sets required to render an extruded solid
*
* @param aOutFile is the file to write to
*
* @return bool: true if the operation succeeded
*/
bool Write3DIndices( std::ofstream& aOutFile );
/**
* Function AddExtraVertex
* adds an extra vertex as required by the GLU tesselator
*
* @return VERTEX_3D*: is the new vertex or NULL if a vertex
* could not be created.
*/
VERTEX_3D* AddExtraVertex( double aXpos, double aYpos );
/**
* Function glStart
* is invoked by the GLU tesselator callback to notify this object
* of the type of GL command which is applicable to the upcoming
* vertex list.
*
* @param cmd is the GL command
*/
void glStart( GLenum cmd );
/**
* Function glPushVertex
* is invoked by the GLU tesselator callback; the supplied vertex is
* added to the internal list of vertices awaiting processing upon
* execution of glEnd()
*
* @param vertex is a vertex forming part of the GL command as previously
* set by glStart
*/
void glPushVertex( VERTEX_3D* vertex );
/**
* Function glEnd
* is invoked by the GLU tesselator callback to notify this object
* that the vertex list is complete and ready for processing
*/
void glEnd( void );
/**
* Function SetGLError
* sets the error message according to the specified OpenGL error
*/
void SetGLError( GLenum error_id );
/**
* Function Import
* inserts all contours into the given tesselator; this
* results in the renumbering of all vertices from @param start.
* Take care when using this call since tesselators cannot work on
* the internal data concurrently.
*
* @param start is the starting number for vertex indices
* @param tess is a pointer to a GLU Tesselator object
*
* @return int: the number of vertices exported
*/
int Import( int start, GLUtesselator* tess );
/**
* Function GetVertexByIndex
* returns a pointer to the requested vertex or
* NULL if no such vertex exists.
*
* @param aPointIndex is a vertex index
*
* @return VERTEX_3D*: the requested vertex or NULL
*/
VERTEX_3D* GetVertexByIndex( int aPointIndex );
/*
* Function GetError
* Returns the error message related to the last failed operation
*/
const std::string& GetError( void );
};
#endif // VRML_BOARD_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment