1786 lines
44 KiB
C++
1786 lines
44 KiB
C++
/*
|
||
* 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;
|
||
|
||
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;
|
||
}
|