diff options
Diffstat (limited to 'utils/idftools/vrml_layer.cpp')
-rw-r--r-- | utils/idftools/vrml_layer.cpp | 1788 |
1 files changed, 1788 insertions, 0 deletions
diff --git a/utils/idftools/vrml_layer.cpp b/utils/idftools/vrml_layer.cpp new file mode 100644 index 0000000..5974371 --- /dev/null +++ b/utils/idftools/vrml_layer.cpp @@ -0,0 +1,1788 @@ +/* + * 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 + */ + +// Wishlist: +// 1. crop anything outside the board outline on PTH, silk, and copper layers +// 2. on the PTH layer, handle cropped holes differently from others; +// these are assumed to be castellated edges and the profile is not +// a closed loop as assumed for all other outlines. +// 3. a scheme is needed to tell a castellated edge from a plain board edge + + +#include <sstream> +#include <string> +#include <iomanip> +#include <cmath> +#include <vrml_layer.h> + +#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], VERTEX_3D* vertex_data[4], + GLfloat weight[4], void** outData, void* user_data ) +{ + VRML_LAYER* lp = (VRML_LAYER*) user_data; + + // the plating is set to true only if all are plated + bool plated = vertex_data[0]->pth; + + if( !vertex_data[1]->pth ) + plated = false; + + if( vertex_data[2] && !vertex_data[2]->pth ) + plated = false; + + if( vertex_data[3] && !vertex_data[3]->pth ) + plated = false; + + *outData = lp->AddExtraVertex( coords[0], coords[1], plated ); +} + + +VRML_LAYER::VRML_LAYER() +{ + // arc parameters suitable to mm measurements + maxArcSeg = 48; + minSegLength = 0.1; + maxSegLength = 0.5; + offsetX = 0.0; + offsetY = 0.0; + + fix = false; + Fault = false; + idx = 0; + hidx = 0; + eidx = 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(); + } + + pth.clear(); + + areas.clear(); + + 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; + + triplets.clear(); + solid.clear(); + + for( i = outline.size(); i > 0; --i ) + { + delete outline.back(); + outline.pop_back(); + } + + ordmap.clear(); + + 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 + vlist.clear(); + + // 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( bool aPlatedHole ) +{ + 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 ); + + pth.push_back( aPlatedHole ); + + 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->pth = pth[ aContourID ]; + + 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, bool aPlatedHole ) +{ + int pad; + + if( aHoleFlag && aPlatedHole ) + pad = NewContour( true ); + else + pad = NewContour( false ); + + 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, bool aPlatedHole ) +{ + 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; + + if( aHoleFlag && aPlatedHole ) + pad = NewContour( true ); + else + pad = NewContour( false ); + + 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, bool aPlatedHole ) +{ + 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; + + if( aHoleFlag && aPlatedHole ) + arc = NewContour( true ); + else + arc = NewContour( false ); + + 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, bool aHolesOnly ) +{ + if( !tess ) + { + error = "Tesselate(): GLU tesselator was not initialized"; + return false; + } + + pholes = holes; + Fault = false; + + if( aHolesOnly ) + gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NEGATIVE ); + else + gluTessProperty( tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_POSITIVE ); + + + 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; + + if( aHolesOnly && ( checkNContours( true ) == 0 ) ) + { + error = "tesselate(): no hole contours"; + return false; + } + else if( !aHolesOnly && ( checkNContours( false ) == 0 ) ) + { + error = "tesselate(): no solid contours"; + return false; + } + + // open the polygon + gluTessBeginPolygon( tess, this ); + + if( aHolesOnly ) + { + pholes = NULL; // do not accept foreign holes + hidx = 0; + eidx = idx; + + // add holes + pushVertices( true ); + + gluTessEndPolygon( tess ); + + if( Fault ) + return false; + + return true; + } + + // add solid outlines + pushVertices( false ); + + // close the polygon + gluTessEndPolygon( tess ); + + if( Fault ) + return false; + + // if there are no outlines we cannot proceed + if( outline.empty() ) + { + error = "tesselate(): no points in result"; + return false; + } + + // at this point we have a solid outline; add it to the tesselator + gluTessBeginPolygon( tess, this ); + + if( !pushOutline( NULL ) ) + 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 false; + } + + if( Fault ) + return false; + + // erase the previous outline data and vertex order + // but preserve the extra vertices + while( !outline.empty() ) + { + delete outline.back(); + outline.pop_back(); + } + + ordmap.clear(); + ord = 0; + + // 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; + } + + // close the polygon; this creates the outline points + // and the point ordering list 'ordmap' + solid.clear(); + gluTessEndPolygon( tess ); + + // repeat the last operation but request a tesselated surface + // rather than an outline; this creates the triangles list. + gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE ); + + gluTessBeginPolygon( tess, this ); + + 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; + } + + std::list<std::list<int>*>::const_iterator obeg = outline.begin(); + std::list<std::list<int>*>::const_iterator oend = outline.end(); + + int nc = 0; // number of contours pushed + + 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() ) + { + gluTessEndContour( tess ); + 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 ) + { + gluTessEndContour( tess ); + 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; + ++nc; + } + + if( !nc ) + { + error = "pushOutline():: no valid contours available"; + return false; + } + + 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 + offsetX, vp->y + offsetY, 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 + offsetX, vp->y + offsetY, 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 + offsetX, vp->y + offsetY, 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 + offsetX, vp->y + offsetY, 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 + offsetX, vp->y + offsetY, 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 + offsetX, vp->y + offsetY, 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, bool aIncludePlatedHoles ) +{ + if( outline.empty() ) + { + error = "WriteIndices(): no outline available"; + return false; + } + + char mark; + bool holes_only = triplets.empty(); + + int i = 1; + int idx2 = ordmap.size(); // index to the bottom vertices + + if( !holes_only ) + { + mark = ','; + + // 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(); + + // 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; + } + } + else + mark = ' '; + + + // print out indices for the walls joining top to bottom + int lastPoint; + int curPoint; + int curContour = 0; + + 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; + ++curContour; + continue; + } + + cbeg = cp->begin(); + cend = cp->end(); + lastPoint = *(cbeg++); + + // skip all PTH vertices which are not in a solid outline + if( !aIncludePlatedHoles && !solid[curContour] + && getVertexByIndex( ordmap[lastPoint], pholes )->pth ) + { + ++obeg; + ++curContour; + continue; + } + + while( cbeg != cend ) + { + curPoint = *(cbeg++); + + if( !holes_only ) + { + if( (i++ & 3) == 2 ) + { + i = 1; + aOutFile << mark << "\n" << curPoint << ", " << lastPoint << ", " << curPoint + idx2; + aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1"; + } + else + { + aOutFile << mark << " " << curPoint << ", " << lastPoint << ", " << curPoint + idx2; + aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint << ", " << lastPoint + idx2 << ", -1"; + } + } + else + { + if( (i++ & 3) == 2 ) + { + i = 1; + aOutFile << mark << "\n" << curPoint << ", " << curPoint + idx2 << ", " << lastPoint; + aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1"; + } + else + { + aOutFile << mark << " " << curPoint << ", " << curPoint + idx2 << ", " << lastPoint; + aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1"; + } + } + + mark = ','; + lastPoint = curPoint; + } + + // check if the loop needs to be closed + cbeg = cp->begin(); + cend = --cp->end(); + + curPoint = *(cbeg); + lastPoint = *(cend); + + if( !holes_only ) + { + if( (i++ & 3) == 2 ) + { + 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"; + } + } + else + { + if( (i++ & 3) == 2 ) + { + aOutFile << ",\n" << curPoint << ", " << curPoint + idx2 << ", " << lastPoint; + aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1"; + } + else + { + aOutFile << ", " << curPoint << ", " << curPoint + idx2 << ", " << lastPoint; + aOutFile << ", -1, " << curPoint + idx2 << ", " << lastPoint + idx2 << ", " << lastPoint << ", -1"; + } + } + + ++obeg; + ++curContour; + } + + 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 9 decimal places + // at most on the VRML output + double err = 0.000000001; + + // 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, bool aPlatedHole ) +{ + 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; + vertex->pth = aPlatedHole; + + 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; + + double firstX = 0.0; + double firstY = 0.0; + double lastX = 0.0; + double lastY = 0.0; + double curX, curY; + double area = 0.0; + + if( vlist.size() > 0 ) + { + loop->push_back( vlist[0]->o ); + firstX = vlist[0]->x; + firstY = vlist[0]->y; + lastX = firstX; + lastY = firstY; + } + + for( size_t i = 1; i < vlist.size(); ++i ) + { + loop->push_back( vlist[i]->o ); + curX = vlist[i]->x; + curY = vlist[i]->y; + area += ( curX - lastX ) * ( curY + lastY ); + lastX = curX; + lastY = curY; + } + + area += ( firstX - lastX ) * ( firstY + lastY ); + + outline.push_back( loop ); + + if( area <= 0.0 ) + solid.push_back( true ); + else + solid.push_back( false ); + } + 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 ) +{ + const char * msg = (const char*)gluErrorString( errorID ); + + // If errorID is an illegal id, gluErrorString returns NULL + if( msg ) + error = msg; + else + error.clear(); + + 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] ); +} + + +int VRML_LAYER::checkNContours( bool holes ) +{ + int nc = 0; // number of contours + + if( contours.empty() ) + return 0; + + std::list<int>::const_iterator begin; + std::list<int>::const_iterator end; + + for( size_t i = 0; i < contours.size(); ++i ) + { + if( contours[i]->size() < 3 ) + continue; + + if( ( holes && areas[i] <= 0.0 ) || ( !holes && areas[i] > 0.0 ) ) + continue; + + ++nc; + } + + return nc; +} + + +// 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 ); + } + + return; +} + + +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; +} + + +void VRML_LAYER::SetVertexOffsets( double aXoffset, double aYoffset ) +{ + offsetX = aXoffset; + offsetY = aYoffset; + return; +} |