kicad-source/utils/idftools/vrml_layer.cpp
Seth Hillbrand 0b2d4d4879 Revise Copyright statement to align with TLF
Recommendation is to avoid using the year nomenclature as this
information is already encoded in the git repo.  Avoids needing to
repeatly update.

Also updates AUTHORS.txt from current repo with contributor names
2025-01-01 14:12:04 -08:00

2033 lines
49 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* file: vrml_layer.cpp
*
* This program source code file is part of KiCad, a free EDA CAD application.
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* Copyright (C) 2013-2017 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>
#include <trigo.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()
{
ResetArcParams();
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::ResetArcParams()
{
// arc parameters suitable to mm measurements
maxArcSeg = 48;
minSegLength = 0.1;
maxSegLength = 0.5;
}
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;
}
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();
}
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;
}
}
int VRML_LAYER::NewContour( bool aPlatedHole )
{
if( fix )
return -1;
std::list<int>* contour = new std::list<int>;
contours.push_back( contour );
areas.push_back( 0.0 );
pth.push_back( aPlatedHole );
return contours.size() - 1;
}
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;
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;
}
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;
}
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 );
}
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;
}
bool VRML_LAYER::AddPolygon( const std::vector< wxRealPoint >& aPolySet, double aCenterX,
double aCenterY, double aAngle )
{
int pad = NewContour( false );
if( pad < 0 )
{
error = "AddPolygon(): failed to add a contour";
return false;
}
for( auto corner : aPolySet )
{
// The sense of polygon rotations is reversed
RotatePoint( &corner.x, &corner.y, -EDA_ANGLE( aAngle, DEGREES_T ) );
AddVertex( pad, aCenterX + corner.x, aCenterY + corner.y );
}
if( !EnsureWinding( pad, false ) )
return false;
return true;
}
// 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;
}
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;
}
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;
}
bool VRML_LAYER::WriteVertices( double aZcoord, std::ostream& 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();
}
bool VRML_LAYER::Write3DVertices( double aTopZ, double aBottomZ, std::ostream& 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();
}
bool VRML_LAYER::WriteIndices( bool aTopFlag, std::ostream& 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();
}
bool VRML_LAYER::Write3DIndices( std::ostream& 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();
}
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 dx2 = p2->x - p1->x;
double dy0 = p1->y - p0->y;
double dy1 = p2->y - p0->y;
double dy2 = p2->y - p1->y;
dx0 *= dx0;
dx1 *= dx1;
dx2 *= dx2;
dy0 *= dy0;
dy1 *= dy1;
dy2 *= dy2;
// 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 (equal points)
if( ( dx0 + dy0 ) < err )
return false;
if( ( dx1 + dy1 ) < err )
return false;
if( ( dx2 + dy2 ) < err )
return false;
triplets.emplace_back( p0->o, p1->o, p2->o );
return true;
}
VERTEX_3D* VRML_LAYER::AddExtraVertex( double aXpos, double aYpos, bool aPlatedHole )
{
VERTEX_3D* vertex = new VERTEX_3D;
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;
}
void VRML_LAYER::glStart( GLenum cmd )
{
glcmd = cmd;
while( !vlist.empty() )
vlist.pop_back();
}
void VRML_LAYER::glPushVertex( VERTEX_3D* vertex )
{
if( vertex->o < 0 )
{
vertex->o = ord++;
ordmap.push_back( vertex->i );
}
vlist.push_back( vertex );
}
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>;
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;
}
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();
}
}
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] );
}
}
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;
}
}
}
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;
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;
}
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;
}
int VRML_LAYER::GetSize( void )
{
return vertices.size();
}
int VRML_LAYER::Import( int start, GLUtesselator* aTesselator )
{
if( start < 0 )
{
error = "Import(): invalid index ( start < 0 )";
return -1;
}
if( !aTesselator )
{
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( aTesselator );
while( cbeg != cend )
{
vp = vertices[ *cbeg++ ];
pt[0] = vp->x;
pt[1] = vp->y;
pt[2] = 0.0;
gluTessVertex( aTesselator, pt, vp );
}
gluTessEndContour( aTesselator );
}
return start;
}
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];
}
const std::string& VRML_LAYER::GetError( void )
{
return error;
}
void VRML_LAYER::SetVertexOffsets( double aXoffset, double aYoffset )
{
offsetX = aXoffset;
offsetY = aYoffset;
return;
}
bool VRML_LAYER::Get3DTriangles( std::vector< double >& aVertexList,
std::vector< int > &aIndexPlane, std::vector< int > &aIndexSide,
double aTopZ, double aBotZ )
{
aVertexList.clear();
aIndexPlane.clear();
aIndexSide.clear();
if( ordmap.size() < 3 || outline.empty() )
return false;
if( aTopZ <= aBotZ )
{
double tmp = aBotZ;
aBotZ = aTopZ;
aTopZ = tmp;
}
VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
if( !vp )
return false;
size_t i;
size_t vsize = ordmap.size();
// top vertices
for( i = 0; i < vsize; ++i )
{
vp = getVertexByIndex( ordmap[i], pholes );
if( !vp )
{
aVertexList.clear();
return false;
}
aVertexList.push_back( vp->x + offsetX );
aVertexList.push_back( vp->y + offsetY );
aVertexList.push_back( aTopZ );
}
// bottom vertices
for( i = 0; i < vsize; ++i )
{
vp = getVertexByIndex( ordmap[i], pholes );
aVertexList.push_back( vp->x + offsetX );
aVertexList.push_back( vp->y + offsetY );
aVertexList.push_back( aBotZ );
}
// create the index lists .. it is difficult to estimate the list size
// a priori so instead we use a vector to help
bool holes_only = triplets.empty();
if( !holes_only )
{
// 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();
std::vector< int > aIndexBot;
while( tbeg != tend )
{
// top vertices
aIndexPlane.push_back( (int) tbeg->i1 );
aIndexPlane.push_back( (int) tbeg->i2 );
aIndexPlane.push_back( (int) tbeg->i3 );
// bottom vertices
aIndexBot.push_back( (int) ( tbeg->i2 + vsize ) );
aIndexBot.push_back( (int) ( tbeg->i1 + vsize ) );
aIndexBot.push_back( (int) ( tbeg->i3 + vsize ) );
++tbeg;
}
aIndexPlane.insert( aIndexPlane.end(), aIndexBot.begin(), aIndexBot.end() );
}
// compile indices for the walls joining top to bottom
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();
lastPoint = *(cbeg++);
while( cbeg != cend )
{
curPoint = *(cbeg++);
if( !holes_only )
{
aIndexSide.push_back( curPoint );
aIndexSide.push_back( lastPoint );
aIndexSide.push_back( (int)( curPoint + vsize ) );
aIndexSide.push_back( (int)( curPoint + vsize ) );
aIndexSide.push_back( lastPoint );
aIndexSide.push_back( (int)( lastPoint + vsize ) );
}
else
{
aIndexSide.push_back( curPoint );
aIndexSide.push_back( (int)( curPoint + vsize ) );
aIndexSide.push_back( lastPoint );
aIndexSide.push_back( (int)( curPoint + vsize ) );
aIndexSide.push_back( (int)( lastPoint + vsize ) );
aIndexSide.push_back( lastPoint );
}
lastPoint = curPoint;
}
// check if the loop needs to be closed
cbeg = cp->begin();
cend = --cp->end();
curPoint = *(cbeg);
lastPoint = *(cend);
if( !holes_only )
{
aIndexSide.push_back( curPoint );
aIndexSide.push_back( lastPoint );
aIndexSide.push_back( (int)( curPoint + vsize ) );
aIndexSide.push_back( (int)( curPoint + vsize ) );
aIndexSide.push_back( lastPoint );
aIndexSide.push_back( (int)( lastPoint + vsize ) );
}
else
{
aIndexSide.push_back( curPoint );
aIndexSide.push_back( (int)( curPoint + vsize ) );
aIndexSide.push_back( lastPoint );
aIndexSide.push_back( (int)( curPoint + vsize ) );
aIndexSide.push_back( (int)( lastPoint + vsize ) );
aIndexSide.push_back( lastPoint );
}
++obeg;
}
return true;
}
bool VRML_LAYER::Get2DTriangles( std::vector< double >& aVertexList,
std::vector< int > &aIndexPlane, double aHeight, bool aTopPlane )
{
aVertexList.clear();
aIndexPlane.clear();
if( ordmap.size() < 3 || outline.empty() )
return false;
VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
if( !vp )
return false;
size_t i;
size_t vsize = ordmap.size();
// vertices
for( i = 0; i < vsize; ++i )
{
vp = getVertexByIndex( ordmap[i], pholes );
if( !vp )
{
aVertexList.clear();
return false;
}
aVertexList.push_back( vp->x + offsetX );
aVertexList.push_back( vp->y + offsetY );
aVertexList.push_back( aHeight );
}
// create the index lists .. it is difficult to estimate the list size
// a priori so instead we use a vector to help
if( triplets.empty() )
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();
if( aTopPlane )
{
while( tbeg != tend )
{
// top vertices
aIndexPlane.push_back( (int) tbeg->i1 );
aIndexPlane.push_back( (int) tbeg->i2 );
aIndexPlane.push_back( (int) tbeg->i3 );
++tbeg;
}
}
else
{
while( tbeg != tend )
{
// bottom vertices
aIndexPlane.push_back( (int) ( tbeg->i2 ) );
aIndexPlane.push_back( (int) ( tbeg->i1 ) );
aIndexPlane.push_back( (int) ( tbeg->i3 ) );
++tbeg;
}
}
return true;
}