mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-09-14 10:13:19 +02:00
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
2033 lines
49 KiB
C++
2033 lines
49 KiB
C++
/*
|
||
* 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;
|
||
}
|