summaryrefslogtreecommitdiff
path: root/utils/idftools/vrml_layer.cpp
diff options
context:
space:
mode:
authorsaurabhb172020-02-26 16:40:14 +0530
committerGitHub2020-02-26 16:40:14 +0530
commit02c614b4e64b68758f223391cb5357b3eec78cac (patch)
treead18839d8b4eb1f13419d07878cc4ec4c9b70032 /utils/idftools/vrml_layer.cpp
parentb77f5d9d8097c38159c6f60917995d6af13bbe1c (diff)
parent07a8c86216b6b1f694b136ec64c281d62941952e (diff)
downloadKiCad-eSim-02c614b4e64b68758f223391cb5357b3eec78cac.tar.gz
KiCad-eSim-02c614b4e64b68758f223391cb5357b3eec78cac.tar.bz2
KiCad-eSim-02c614b4e64b68758f223391cb5357b3eec78cac.zip
Merge pull request #6 from saurabhb17/master
minor additions
Diffstat (limited to 'utils/idftools/vrml_layer.cpp')
-rw-r--r--utils/idftools/vrml_layer.cpp1788
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;
+}