Commit 53cd19a6 authored by unknown's avatar unknown Committed by jean-pierre charras

VRML export rewritten

parent bd174ebf
...@@ -140,7 +140,6 @@ set( PCBNEW_EXPORTERS ...@@ -140,7 +140,6 @@ set( PCBNEW_EXPORTERS
exporters/gen_drill_report_files.cpp exporters/gen_drill_report_files.cpp
exporters/gen_modules_placefile.cpp exporters/gen_modules_placefile.cpp
exporters/gendrill_Excellon_writer.cpp exporters/gendrill_Excellon_writer.cpp
exporters/vrml_board.cpp
) )
set( PCBNEW_AUTOROUTER_SRCS set( PCBNEW_AUTOROUTER_SRCS
......
...@@ -568,7 +568,7 @@ bool Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, bool aUseThou ) ...@@ -568,7 +568,7 @@ bool Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, bool aUseThou )
ok = false; ok = false;
} }
catch( std::exception& e ) catch( const std::exception& e )
{ {
wxString msg; wxString msg;
msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( e.what() ); msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( e.what() );
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
/* /*
* NOTE: * NOTE:
* 1. for improved looks, create a DRILL layer for PTH drills. * 1. for improved looks, create a DRILL layer for PTH drills.
...@@ -42,24 +43,17 @@ ...@@ -42,24 +43,17 @@
* *
* 2. How can we suppress fiducials such as those in the corners of the pic-programmer demo? * 2. How can we suppress fiducials such as those in the corners of the pic-programmer demo?
* *
* 3. Export Graphics to Layer objects (see 3d_draw.cpp for clues) to ensure that custom */
* tracks/fills/logos are rendered.
* module->TransformGraphicShapesWithClearanceToPolygonSet /*
* * KNOWN BUGS:
* For mechanical correctness, we should use the following settings with arcs: * 1. silk outlines are sometimes mangled; this is somehow due to
* 1. max. deviation: the number of edges should be determined by the max. * many overlapping segments. This does not happen in 3Dviewer
* mechanical deviation and the minimum number of edges shall be 6. * so it is worth inspecting that code to see what is different.
* 2. for very large features we may introduce too many edges in a circle;
* to control this, we should specify a MAX number of edges or a threshold
* radius and a deviation for larger features
*
* For example, many mechanical fits are to within +/-0.05mm, so specifying
* a max. deviation of 0.02mm will yield a hole near the max. material
* condition. Calculating sides for a 10mm radius hole will yield about
* 312 points; such large holes (and arcs) will typically have a specified
* tolerance of +/-0.2mm in which case we can set the MAX edges to 32
* provided none of the important holes requires > 32 edges.
* *
* These artefacts can be suppressed for exploratory purposes by
* removing the line width parameter from the length calculation in
* export_vrml_line()
*/ */
#include <fctsys.h> #include <fctsys.h>
...@@ -70,6 +64,9 @@ ...@@ -70,6 +64,9 @@
#include <pgm_base.h> #include <pgm_base.h>
#include <3d_struct.h> #include <3d_struct.h>
#include <macros.h> #include <macros.h>
#include <exception>
#include <fstream>
#include <iomanip>
#include <pcbnew.h> #include <pcbnew.h>
...@@ -85,7 +82,13 @@ ...@@ -85,7 +82,13 @@
#include <vector> #include <vector>
#include <cmath> #include <cmath>
#include <vrml_board.h> #include <vrml_layer.h>
// minimum width (mm) of a VRML line
#define MIN_VRML_LINEWIDTH 0.12
// offset for art layers, mm (silk, paste, etc)
#define ART_OFFSET 0.02
/* helper function: /* helper function:
* some characters cannot be used in names, * some characters cannot be used in names,
...@@ -167,6 +170,9 @@ private: ...@@ -167,6 +170,9 @@ private:
double layer_z[NB_LAYERS]; double layer_z[NB_LAYERS];
VRML_COLOR colors[VRML_COLOR_LAST]; VRML_COLOR colors[VRML_COLOR_LAST];
int iMaxSeg; // max. sides to a small circle
double arcMinLen, arcMaxLen; // min and max lengths of an arc chord
public: public:
VRML_LAYER holes; VRML_LAYER holes;
...@@ -179,6 +185,8 @@ public: ...@@ -179,6 +185,8 @@ public:
VRML_LAYER bot_tin; VRML_LAYER bot_tin;
double scale; // board internal units to output scaling double scale; // board internal units to output scaling
double minLineWidth; // minimum width of a VRML line segment
int precision; // precision of output units
double tx; // global translation along X double tx; // global translation along X
double ty; // global translation along Y double ty; // global translation along Y
...@@ -193,6 +201,8 @@ public: ...@@ -193,6 +201,8 @@ public:
for( int i = 0; i < NB_LAYERS; ++i ) for( int i = 0; i < NB_LAYERS; ++i )
layer_z[i] = 0; layer_z[i] = 0;
holes.GetArcParams( iMaxSeg, arcMinLen, arcMaxLen );
// this default only makes sense if the output is in mm // this default only makes sense if the output is in mm
board_thickness = 1.6; board_thickness = 1.6;
...@@ -208,6 +218,8 @@ public: ...@@ -208,6 +218,8 @@ public:
// pad silver // pad silver
colors[ VRML_COLOR_TIN ] = VRML_COLOR( .749, .756, .761, .749, .756, .761, colors[ VRML_COLOR_TIN ] = VRML_COLOR( .749, .756, .761, .749, .756, .761,
0, 0, 0, 0.8, 0, 0.8 ); 0, 0, 0, 0.8, 0, 0.8 );
precision = 5;
} }
VRML_COLOR& GetColor( VRML_COLOR_INDEX aIndex ) VRML_COLOR& GetColor( VRML_COLOR_INDEX aIndex )
...@@ -234,31 +246,51 @@ public: ...@@ -234,31 +246,51 @@ public:
layer_z[aLayer] = aValue; layer_z[aLayer] = aValue;
} }
void SetMaxDev( double dev ) // set the scaling of the VRML world
bool SetScale( double aWorldScale )
{ {
holes.SetMaxDev( dev ); if( aWorldScale < 0.001 || aWorldScale > 10.0 )
board.SetMaxDev( dev ); throw( std::runtime_error( "WorldScale out of range (valid range is 0.001 to 10.0)" ) );
top_copper.SetMaxDev( dev );
bot_copper.SetMaxDev( dev ); scale = aWorldScale * MM_PER_IU;
top_silk.SetMaxDev( dev ); minLineWidth = aWorldScale * MIN_VRML_LINEWIDTH;
bot_silk.SetMaxDev( dev );
top_tin.SetMaxDev( dev ); // set the precision of the VRML coordinates
bot_tin.SetMaxDev( dev ); if( aWorldScale < 0.01 )
precision = 8;
else if( aWorldScale < 0.1 )
precision = 7;
else if( aWorldScale< 1.0 )
precision = 6;
else if( aWorldScale < 10.0 )
precision = 5;
else
precision = 4;
double smin = arcMinLen * aWorldScale;
double smax = arcMaxLen * aWorldScale;
holes.SetArcParams( iMaxSeg, smin, smax );
board.SetArcParams( iMaxSeg, smin, smax );
top_copper.SetArcParams( iMaxSeg, smin, smax);
bot_copper.SetArcParams( iMaxSeg, smin, smax);
top_silk.SetArcParams( iMaxSeg, smin, smax );
bot_silk.SetArcParams( iMaxSeg, smin, smax );
top_tin.SetArcParams( iMaxSeg, smin, smax );
bot_tin.SetArcParams( iMaxSeg, smin, smax );
return true;
} }
}; };
// static var. for dealing with text // static var. for dealing with text
namespace VRMLEXPORT static MODEL_VRML* model_vrml;
{
static MODEL_VRML* model_vrml;
bool GetLayer( MODEL_VRML& aModel, LAYER_NUM layer, VRML_LAYER** vlayer );
}
// select the VRML layer object to draw on; return true if // select the VRML layer object to draw on; return true if
// a layer has been selected. // a layer has been selected.
bool VRMLEXPORT::GetLayer( MODEL_VRML& aModel, LAYER_NUM layer, VRML_LAYER** vlayer ) static bool GetLayer( MODEL_VRML& aModel, LAYER_NUM layer, VRML_LAYER** vlayer )
{ {
switch( layer ) switch( layer )
{ {
...@@ -286,9 +318,9 @@ bool VRMLEXPORT::GetLayer( MODEL_VRML& aModel, LAYER_NUM layer, VRML_LAYER** vla ...@@ -286,9 +318,9 @@ bool VRMLEXPORT::GetLayer( MODEL_VRML& aModel, LAYER_NUM layer, VRML_LAYER** vla
} }
static void write_triangle_bag( FILE* output_file, VRML_COLOR& color, static void write_triangle_bag( std::ofstream& output_file, VRML_COLOR& color,
VRML_LAYER* layer, bool plane, bool top, VRML_LAYER* layer, bool plane, bool top,
double top_z, double bottom_z ) double top_z, double bottom_z, int aPrecision )
{ {
/* A lot of nodes are not required, but blender sometimes chokes /* A lot of nodes are not required, but blender sometimes chokes
* without them */ * without them */
...@@ -328,7 +360,7 @@ static void write_triangle_bag( FILE* output_file, VRML_COLOR& color, ...@@ -328,7 +360,7 @@ static void write_triangle_bag( FILE* output_file, VRML_COLOR& color,
while( marker_found < 4 ) while( marker_found < 4 )
{ {
if( shape_boiler[lineno] ) if( shape_boiler[lineno] )
fputs( shape_boiler[lineno], output_file ); output_file << shape_boiler[lineno];
else else
{ {
marker_found++; marker_found++;
...@@ -336,37 +368,34 @@ static void write_triangle_bag( FILE* output_file, VRML_COLOR& color, ...@@ -336,37 +368,34 @@ static void write_triangle_bag( FILE* output_file, VRML_COLOR& color,
switch( marker_found ) switch( marker_found )
{ {
case 1: // Material marker case 1: // Material marker
fprintf( output_file, output_file << " diffuseColor " << std::setprecision(3);
" diffuseColor %g %g %g\n", output_file << color.diffuse_red << " ";
color.diffuse_red, output_file << color.diffuse_grn << " ";
color.diffuse_grn, output_file << color.diffuse_blu << "\n";
color.diffuse_blu );
fprintf( output_file, output_file << " specularColor ";
" specularColor %g %g %g\n", output_file << color.spec_red << " ";
color.spec_red, output_file << color.spec_grn << " ";
color.spec_grn, output_file << color.spec_blu << "\n";
color.spec_blu );
fprintf( output_file, output_file << " emissiveColor ";
" emissiveColor %g %g %g\n", output_file << color.emit_red << " ";
color.emit_red, output_file << color.emit_grn << " ";
color.emit_grn, output_file << color.emit_blu << "\n";
color.emit_blu );
fprintf( output_file, output_file << " ambientIntensity " << color.ambient << "\n";
" ambientIntensity %g\n", color.ambient ); output_file << " transparency " << color.transp << "\n";
fprintf( output_file, output_file << " shininess " << color.shiny << "\n";
" transparency %g\n", color.transp );
fprintf( output_file,
" shininess %g\n", color.shiny );
break; break;
case 2: case 2:
if( plane ) if( plane )
layer->WriteVertices( top_z, output_file ); layer->WriteVertices( top_z, output_file, aPrecision );
else else
layer->Write3DVertices( top_z, bottom_z, output_file ); layer->Write3DVertices( top_z, bottom_z, output_file, aPrecision );
fprintf( output_file, "\n" ); output_file << "\n";
break; break;
case 3: case 3:
...@@ -376,7 +405,7 @@ static void write_triangle_bag( FILE* output_file, VRML_COLOR& color, ...@@ -376,7 +405,7 @@ static void write_triangle_bag( FILE* output_file, VRML_COLOR& color,
else else
layer->Write3DIndices( output_file ); layer->Write3DIndices( output_file );
fprintf( output_file, "\n" ); output_file << "\n";
break; break;
default: default:
...@@ -389,49 +418,54 @@ static void write_triangle_bag( FILE* output_file, VRML_COLOR& color, ...@@ -389,49 +418,54 @@ static void write_triangle_bag( FILE* output_file, VRML_COLOR& color,
} }
static void write_layers( MODEL_VRML& aModel, FILE* output_file, BOARD* aPcb ) static void write_layers( MODEL_VRML& aModel, std::ofstream& output_file, BOARD* aPcb )
{ {
// VRML_LAYER board; // VRML_LAYER board;
aModel.board.Tesselate( &aModel.holes ); aModel.board.Tesselate( &aModel.holes );
double brdz = aModel.board_thickness / 2.0 - 40000 * aModel.scale; double brdz = aModel.board_thickness / 2.0
- ( Millimeter2iu( ART_OFFSET / 2.0 ) ) * aModel.scale;
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_PCB ), write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_PCB ),
&aModel.board, false, false, brdz, -brdz ); &aModel.board, false, false, brdz, -brdz, aModel.precision );
// VRML_LAYER top_copper; // VRML_LAYER top_copper;
aModel.top_copper.Tesselate( &aModel.holes ); aModel.top_copper.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TRACK ), write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TRACK ),
&aModel.top_copper, true, true, &aModel.top_copper, true, true,
aModel.GetLayerZ( LAST_COPPER_LAYER ), 0 ); aModel.GetLayerZ( LAST_COPPER_LAYER ), 0, aModel.precision );
// VRML_LAYER top_tin; // VRML_LAYER top_tin;
aModel.top_tin.Tesselate( &aModel.holes ); aModel.top_tin.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TIN ), write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TIN ),
&aModel.top_tin, true, true, &aModel.top_tin, true, true,
aModel.GetLayerZ( LAST_COPPER_LAYER ), 0 ); aModel.GetLayerZ( LAST_COPPER_LAYER )
+ Millimeter2iu( ART_OFFSET / 2.0 ) * aModel.scale,
0, aModel.precision );
// VRML_LAYER bot_copper; // VRML_LAYER bot_copper;
aModel.bot_copper.Tesselate( &aModel.holes ); aModel.bot_copper.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TRACK ), write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TRACK ),
&aModel.bot_copper, true, false, &aModel.bot_copper, true, false,
aModel.GetLayerZ( FIRST_COPPER_LAYER ), 0 ); aModel.GetLayerZ( FIRST_COPPER_LAYER ), 0, aModel.precision );
// VRML_LAYER bot_tin; // VRML_LAYER bot_tin;
aModel.bot_tin.Tesselate( &aModel.holes ); aModel.bot_tin.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TIN ), write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TIN ),
&aModel.bot_tin, true, false, &aModel.bot_tin, true, false,
aModel.GetLayerZ( FIRST_COPPER_LAYER ), 0 ); aModel.GetLayerZ( FIRST_COPPER_LAYER )
- Millimeter2iu( ART_OFFSET / 2.0 ) * aModel.scale,
0, aModel.precision );
// VRML_LAYER top_silk; // VRML_LAYER top_silk;
aModel.top_silk.Tesselate( &aModel.holes ); aModel.top_silk.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_SILK ), write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_SILK ),
&aModel.top_silk, true, true, &aModel.top_silk, true, true,
aModel.GetLayerZ( SILKSCREEN_N_FRONT ), 0 ); aModel.GetLayerZ( SILKSCREEN_N_FRONT ), 0, aModel.precision );
// VRML_LAYER bot_silk; // VRML_LAYER bot_silk;
aModel.bot_silk.Tesselate( &aModel.holes ); aModel.bot_silk.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_SILK ), write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_SILK ),
&aModel.bot_silk, true, false, &aModel.bot_silk, true, false,
aModel.GetLayerZ( SILKSCREEN_N_BACK ), 0 ); aModel.GetLayerZ( SILKSCREEN_N_BACK ), 0, aModel.precision );
} }
...@@ -454,7 +488,7 @@ static void compute_layer_Zs( MODEL_VRML& aModel, BOARD* pcb ) ...@@ -454,7 +488,7 @@ static void compute_layer_Zs( MODEL_VRML& aModel, BOARD* pcb )
/* To avoid rounding interference, we apply an epsilon to each /* To avoid rounding interference, we apply an epsilon to each
* successive layer */ * successive layer */
double epsilon_z = Millimeter2iu( 0.02 ) * aModel.scale; double epsilon_z = Millimeter2iu( ART_OFFSET ) * aModel.scale;
aModel.SetLayerZ( SOLDERPASTE_N_BACK, -half_thickness - epsilon_z * 4 ); aModel.SetLayerZ( SOLDERPASTE_N_BACK, -half_thickness - epsilon_z * 4 );
aModel.SetLayerZ( ADHESIVE_N_BACK, -half_thickness - epsilon_z * 3 ); aModel.SetLayerZ( ADHESIVE_N_BACK, -half_thickness - epsilon_z * 3 );
aModel.SetLayerZ( SILKSCREEN_N_BACK, -half_thickness - epsilon_z * 2 ); aModel.SetLayerZ( SILKSCREEN_N_BACK, -half_thickness - epsilon_z * 2 );
...@@ -477,18 +511,22 @@ static void export_vrml_line( MODEL_VRML& aModel, LAYER_NUM layer, ...@@ -477,18 +511,22 @@ static void export_vrml_line( MODEL_VRML& aModel, LAYER_NUM layer,
{ {
VRML_LAYER* vlayer; VRML_LAYER* vlayer;
if( !VRMLEXPORT::GetLayer( aModel, layer, &vlayer ) ) if( !GetLayer( aModel, layer, &vlayer ) )
return; return;
if( width < aModel.minLineWidth)
width = aModel.minLineWidth;
starty = -starty; starty = -starty;
endy = -endy; endy = -endy;
double angle = atan2( endy - starty, endx - startx ); double angle = atan2( endy - starty, endx - startx ) * 180.0 / M_PI;
double length = Distance( startx, starty, endx, endy ) + width; double length = Distance( startx, starty, endx, endy ) + width;
double cx = ( startx + endx ) / 2.0; double cx = ( startx + endx ) / 2.0;
double cy = ( starty + endy ) / 2.0; double cy = ( starty + endy ) / 2.0;
vlayer->AddSlot( cx, cy, length, width, angle, 1, false ); if( !vlayer->AddSlot( cx, cy, length, width, angle, false ) )
throw( std::runtime_error( vlayer->GetError() ) );
} }
...@@ -498,9 +536,12 @@ static void export_vrml_circle( MODEL_VRML& aModel, LAYER_NUM layer, ...@@ -498,9 +536,12 @@ static void export_vrml_circle( MODEL_VRML& aModel, LAYER_NUM layer,
{ {
VRML_LAYER* vlayer; VRML_LAYER* vlayer;
if( !VRMLEXPORT::GetLayer( aModel, layer, &vlayer ) ) if( !GetLayer( aModel, layer, &vlayer ) )
return; return;
if( width < aModel.minLineWidth )
width = aModel.minLineWidth;
starty = -starty; starty = -starty;
endy = -endy; endy = -endy;
...@@ -509,11 +550,13 @@ static void export_vrml_circle( MODEL_VRML& aModel, LAYER_NUM layer, ...@@ -509,11 +550,13 @@ static void export_vrml_circle( MODEL_VRML& aModel, LAYER_NUM layer,
radius = Distance( startx, starty, endx, endy ) + ( width / 2); radius = Distance( startx, starty, endx, endy ) + ( width / 2);
hole = radius - width; hole = radius - width;
vlayer->AddCircle( startx, starty, radius, 1, false ); if( !vlayer->AddCircle( startx, starty, radius, false ) )
throw( std::runtime_error( vlayer->GetError() ) );
if( hole > 0.0001 ) if( hole > 0.0001 )
{ {
vlayer->AddCircle( startx, starty, hole, 1, true ); if( !vlayer->AddCircle( startx, starty, hole, true ) )
throw( std::runtime_error( vlayer->GetError() ) );
} }
} }
...@@ -525,16 +568,19 @@ static void export_vrml_arc( MODEL_VRML& aModel, LAYER_NUM layer, ...@@ -525,16 +568,19 @@ static void export_vrml_arc( MODEL_VRML& aModel, LAYER_NUM layer,
{ {
VRML_LAYER* vlayer; VRML_LAYER* vlayer;
if( !VRMLEXPORT::GetLayer( aModel, layer, &vlayer ) ) if( !GetLayer( aModel, layer, &vlayer ) )
return; return;
if( width < aModel.minLineWidth )
width = aModel.minLineWidth;
centery = -centery; centery = -centery;
arc_starty = -arc_starty; arc_starty = -arc_starty;
arc_angle *= -M_PI / 180; if( !vlayer->AddArc( centerx, centery, arc_startx, arc_starty,
width, arc_angle, false ) )
throw( std::runtime_error( vlayer->GetError() ) );
vlayer->AddArc( centerx, centery, arc_startx, arc_starty,
width, arc_angle, 1, false );
} }
...@@ -577,13 +623,13 @@ static void export_vrml_drawsegment( MODEL_VRML& aModel, DRAWSEGMENT* drawseg ) ...@@ -577,13 +623,13 @@ static void export_vrml_drawsegment( MODEL_VRML& aModel, DRAWSEGMENT* drawseg )
* for coupling the vrml_text_callback with the common parameters */ * for coupling the vrml_text_callback with the common parameters */
static void vrml_text_callback( int x0, int y0, int xf, int yf ) static void vrml_text_callback( int x0, int y0, int xf, int yf )
{ {
LAYER_NUM s_text_layer = VRMLEXPORT::model_vrml->s_text_layer; LAYER_NUM s_text_layer = model_vrml->s_text_layer;
int s_text_width = VRMLEXPORT::model_vrml->s_text_width; int s_text_width = model_vrml->s_text_width;
double scale = VRMLEXPORT::model_vrml->scale; double scale = model_vrml->scale;
double tx = VRMLEXPORT::model_vrml->tx; double tx = model_vrml->tx;
double ty = VRMLEXPORT::model_vrml->ty; double ty = model_vrml->ty;
export_vrml_line( *VRMLEXPORT::model_vrml, s_text_layer, export_vrml_line( *model_vrml, s_text_layer,
x0 * scale + tx, y0 * scale + ty, x0 * scale + tx, y0 * scale + ty,
xf * scale + tx, yf * scale + ty, xf * scale + tx, yf * scale + ty,
s_text_width * scale ); s_text_width * scale );
...@@ -592,8 +638,8 @@ static void vrml_text_callback( int x0, int y0, int xf, int yf ) ...@@ -592,8 +638,8 @@ static void vrml_text_callback( int x0, int y0, int xf, int yf )
static void export_vrml_pcbtext( MODEL_VRML& aModel, TEXTE_PCB* text ) static void export_vrml_pcbtext( MODEL_VRML& aModel, TEXTE_PCB* text )
{ {
VRMLEXPORT::model_vrml->s_text_layer = text->GetLayer(); model_vrml->s_text_layer = text->GetLayer();
VRMLEXPORT::model_vrml->s_text_width = text->GetThickness(); model_vrml->s_text_width = text->GetThickness();
wxSize size = text->GetSize(); wxSize size = text->GetSize();
...@@ -707,12 +753,12 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb ) ...@@ -707,12 +753,12 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
while( i < nvert ) while( i < nvert )
{ {
aModel.board.AddVertex( seg, bufferPcbOutlines[i].x * scale + dx,
-(bufferPcbOutlines[i].y * scale + dy) );
if( bufferPcbOutlines[i].end_contour ) if( bufferPcbOutlines[i].end_contour )
break; break;
aModel.board.AddVertex( seg, bufferPcbOutlines[i].x * scale + dx,
-(bufferPcbOutlines[i].y * scale + dy) );
++i; ++i;
} }
...@@ -739,12 +785,12 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb ) ...@@ -739,12 +785,12 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
while( i < nvert ) while( i < nvert )
{ {
aModel.holes.AddVertex( seg, allLayerHoles[i].x * scale + dx,
-(allLayerHoles[i].y * scale + dy) );
if( allLayerHoles[i].end_contour ) if( allLayerHoles[i].end_contour )
break; break;
aModel.holes.AddVertex( seg, allLayerHoles[i].x * scale + dx,
-(allLayerHoles[i].y * scale + dy) );
++i; ++i;
} }
...@@ -766,31 +812,26 @@ static void export_round_padstack( MODEL_VRML& aModel, BOARD* pcb, ...@@ -766,31 +812,26 @@ static void export_round_padstack( MODEL_VRML& aModel, BOARD* pcb,
if( top_layer != LAST_COPPER_LAYER || bottom_layer != FIRST_COPPER_LAYER ) if( top_layer != LAST_COPPER_LAYER || bottom_layer != FIRST_COPPER_LAYER )
thru = false; thru = false;
if( thru && hole > 0 )
aModel.holes.AddCircle( x, -y, hole, true );
while( 1 ) while( 1 )
{ {
if( layer == FIRST_COPPER_LAYER ) if( layer == FIRST_COPPER_LAYER )
{ {
aModel.bot_copper.AddCircle( x, -y, r, 1 ); aModel.bot_copper.AddCircle( x, -y, r );
if( hole > 0 && !thru )
aModel.bot_copper.AddCircle( x, -y, hole, true );
if( hole > 0 )
{
if( thru )
aModel.holes.AddCircle( x, -y, hole, 1, true );
else
aModel.bot_copper.AddCircle( x, -y, hole, 1, true );
}
} }
else if( layer == LAST_COPPER_LAYER ) else if( layer == LAST_COPPER_LAYER )
{ {
aModel.top_copper.AddCircle( x, -y, r, 1 ); aModel.top_copper.AddCircle( x, -y, r );
if( hole > 0 && !thru )
aModel.top_copper.AddCircle( x, -y, hole, true );
if( hole > 0 )
{
if( thru )
aModel.holes.AddCircle( x, -y, hole, 1, true );
else
aModel.top_copper.AddCircle( x, -y, hole, 1, true );
}
} }
if( layer == bottom_layer ) if( layer == bottom_layer )
...@@ -856,7 +897,7 @@ static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb ) ...@@ -856,7 +897,7 @@ static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb )
VRML_LAYER* vl; VRML_LAYER* vl;
if( !VRMLEXPORT::GetLayer( aModel, zone->GetLayer(), &vl ) ) if( !GetLayer( aModel, zone->GetLayer(), &vl ) )
continue; continue;
if( !zone->IsFilled() ) if( !zone->IsFilled() )
...@@ -881,11 +922,13 @@ static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb ) ...@@ -881,11 +922,13 @@ static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb )
{ {
x = poly.GetX(i) * scale + dx; x = poly.GetX(i) * scale + dx;
y = -(poly.GetY(i) * scale + dy); y = -(poly.GetY(i) * scale + dy);
vl->AddVertex( seg, x, y );
if( poly.IsEndContour(i) ) if( poly.IsEndContour(i) )
break; break;
if( !vl->AddVertex( seg, x, y ) )
throw( std::runtime_error( vl->GetError() ) );
++i; ++i;
} }
...@@ -911,8 +954,8 @@ static void export_vrml_text_module( TEXTE_MODULE* module ) ...@@ -911,8 +954,8 @@ static void export_vrml_text_module( TEXTE_MODULE* module )
if( module->IsMirrored() ) if( module->IsMirrored() )
NEGATE( size.x ); // Text is mirrored NEGATE( size.x ); // Text is mirrored
VRMLEXPORT::model_vrml->s_text_layer = module->GetLayer(); model_vrml->s_text_layer = module->GetLayer();
VRMLEXPORT::model_vrml->s_text_width = module->GetThickness(); model_vrml->s_text_width = module->GetThickness();
DrawGraphicText( NULL, NULL, module->GetTextPosition(), BLACK, DrawGraphicText( NULL, NULL, module->GetTextPosition(), BLACK,
module->GetText(), module->GetDrawRotation(), size, module->GetText(), module->GetDrawRotation(), size,
...@@ -952,10 +995,10 @@ static void export_vrml_edge_module( MODEL_VRML& aModel, EDGE_MODULE* aOutline, ...@@ -952,10 +995,10 @@ static void export_vrml_edge_module( MODEL_VRML& aModel, EDGE_MODULE* aOutline,
{ {
VRML_LAYER* vl; VRML_LAYER* vl;
if( !VRMLEXPORT::GetLayer( aModel, layer, &vl ) ) if( !GetLayer( aModel, layer, &vl ) )
break; break;
int nvert = aOutline->GetPolyPoints().size(); int nvert = aOutline->GetPolyPoints().size() - 1;
int i = 0; int i = 0;
if( nvert < 3 ) break; if( nvert < 3 ) break;
...@@ -974,7 +1017,9 @@ static void export_vrml_edge_module( MODEL_VRML& aModel, EDGE_MODULE* aOutline, ...@@ -974,7 +1017,9 @@ static void export_vrml_edge_module( MODEL_VRML& aModel, EDGE_MODULE* aOutline,
x = corner.x * aModel.scale + aModel.tx; x = corner.x * aModel.scale + aModel.tx;
y = - ( corner.y * aModel.scale + aModel.ty ); y = - ( corner.y * aModel.scale + aModel.ty );
vl->AddVertex( seg, x, y );
if( !vl->AddVertex( seg, x, y ) )
throw( std::runtime_error( vl->GetError() ) );
++i; ++i;
} }
...@@ -988,8 +1033,7 @@ static void export_vrml_edge_module( MODEL_VRML& aModel, EDGE_MODULE* aOutline, ...@@ -988,8 +1033,7 @@ static void export_vrml_edge_module( MODEL_VRML& aModel, EDGE_MODULE* aOutline,
} }
static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aLayer, static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aTinLayer, D_PAD* aPad )
VRML_LAYER* aTinLayer, D_PAD* aPad )
{ {
// The (maybe offset) pad position // The (maybe offset) pad position
wxPoint pad_pos = aPad->ShapePos(); wxPoint pad_pos = aPad->ShapePos();
...@@ -1006,15 +1050,18 @@ static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aLayer, ...@@ -1006,15 +1050,18 @@ static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aLayer,
switch( aPad->GetShape() ) switch( aPad->GetShape() )
{ {
case PAD_CIRCLE: case PAD_CIRCLE:
aLayer->AddCircle( pad_x, -pad_y, pad_w, 1, true );
aTinLayer->AddCircle( pad_x, -pad_y, pad_w, 1, false ); if( !aTinLayer->AddCircle( pad_x, -pad_y, pad_w, false ) )
throw( std::runtime_error( aTinLayer->GetError() ) );
break; break;
case PAD_OVAL: case PAD_OVAL:
aLayer->AddSlot( pad_x, -pad_y, pad_w * 2.0, pad_h * 2.0,
DECIDEG2RAD( aPad->GetOrientation() ), 1, true ); if( !aTinLayer->AddSlot( pad_x, -pad_y, pad_w * 2.0, pad_h * 2.0,
aTinLayer->AddSlot( pad_x, -pad_y, pad_w * 2.0, pad_h * 2.0, aPad->GetOrientation()/10.0, false ) )
DECIDEG2RAD( aPad->GetOrientation() ), 1, false ); throw( std::runtime_error( aTinLayer->GetError() ) );
break; break;
case PAD_RECT: case PAD_RECT:
...@@ -1039,32 +1086,32 @@ static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aLayer, ...@@ -1039,32 +1086,32 @@ static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aLayer,
coord[i * 2 + 1] += pad_y; coord[i * 2 + 1] += pad_y;
} }
int lines = aLayer->NewContour(); int lines;
lines = aTinLayer->NewContour();
if( lines < 0 ) if( lines < 0 )
return; throw( std::runtime_error( aTinLayer->GetError() ) );
aLayer->AddVertex( lines, coord[2], -coord[3] ); if( !aTinLayer->AddVertex( lines, coord[0], -coord[1] ) )
aLayer->AddVertex( lines, coord[6], -coord[7] ); throw( std::runtime_error( aTinLayer->GetError() ) );
aLayer->AddVertex( lines, coord[4], -coord[5] );
aLayer->AddVertex( lines, coord[0], -coord[1] );
aLayer->EnsureWinding( lines, true );
lines = aTinLayer->NewContour(); if( !aTinLayer->AddVertex( lines, coord[4], -coord[5] ) )
throw( std::runtime_error( aTinLayer->GetError() ) );
if( lines < 0 ) if( !aTinLayer->AddVertex( lines, coord[6], -coord[7] ) )
return; throw( std::runtime_error( aTinLayer->GetError() ) );
if( !aTinLayer->AddVertex( lines, coord[2], -coord[3] ) )
throw( std::runtime_error( aTinLayer->GetError() ) );
aTinLayer->AddVertex( lines, coord[0], -coord[1] ); if( !aTinLayer->EnsureWinding( lines, false ) )
aTinLayer->AddVertex( lines, coord[4], -coord[5] ); throw( std::runtime_error( aTinLayer->GetError() ) );
aTinLayer->AddVertex( lines, coord[6], -coord[7] );
aTinLayer->AddVertex( lines, coord[2], -coord[3] );
aTinLayer->EnsureWinding( lines, false );
} }
break; break;
default: default:
; break;
} }
} }
...@@ -1084,12 +1131,12 @@ static void export_vrml_pad( MODEL_VRML& aModel, BOARD* pcb, D_PAD* aPad ) ...@@ -1084,12 +1131,12 @@ static void export_vrml_pad( MODEL_VRML& aModel, BOARD* pcb, D_PAD* aPad )
{ {
// Oblong hole (slot) // Oblong hole (slot)
aModel.holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0, hole_drill_h * 2.0, aModel.holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0, hole_drill_h * 2.0,
DECIDEG2RAD( aPad->GetOrientation() ), 1, true ); aPad->GetOrientation()/10.0, true );
} }
else else
{ {
// Drill a round hole // Drill a round hole
aModel.holes.AddCircle( hole_x, -hole_y, hole_drill, 1, true ); aModel.holes.AddCircle( hole_x, -hole_y, hole_drill, true );
} }
} }
...@@ -1098,12 +1145,12 @@ static void export_vrml_pad( MODEL_VRML& aModel, BOARD* pcb, D_PAD* aPad ) ...@@ -1098,12 +1145,12 @@ static void export_vrml_pad( MODEL_VRML& aModel, BOARD* pcb, D_PAD* aPad )
if( layer_mask & LAYER_BACK ) if( layer_mask & LAYER_BACK )
{ {
export_vrml_padshape( aModel, &aModel.bot_copper, &aModel.bot_tin, aPad ); export_vrml_padshape( aModel, &aModel.bot_tin, aPad );
} }
if( layer_mask & LAYER_FRONT ) if( layer_mask & LAYER_FRONT )
{ {
export_vrml_padshape( aModel, &aModel.top_copper, &aModel.top_tin, aPad ); export_vrml_padshape( aModel, &aModel.top_tin, aPad );
} }
} }
...@@ -1150,7 +1197,7 @@ static void compose_quat( double q1[4], double q2[4], double qr[4] ) ...@@ -1150,7 +1197,7 @@ static void compose_quat( double q1[4], double q2[4], double qr[4] )
static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule, static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule,
FILE* aOutputFile, std::ofstream& aOutputFile,
double aVRMLModelsToBiu, double aVRMLModelsToBiu,
bool aExport3DFiles, const wxString& a3D_Subdir ) bool aExport3DFiles, const wxString& a3D_Subdir )
{ {
...@@ -1237,12 +1284,13 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule ...@@ -1237,12 +1284,13 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule
compose_quat( q1, q2, q1 ); compose_quat( q1, q2, q1 );
from_quat( q1, rot ); from_quat( q1, rot );
fprintf( aOutputFile, "Transform {\n" ); aOutputFile << "Transform {\n";
// A null rotation would fail the acos! // A null rotation would fail the acos!
if( rot[3] != 0.0 ) if( rot[3] != 0.0 )
{ {
fprintf( aOutputFile, " rotation %g %g %g %g\n", rot[0], rot[1], rot[2], rot[3] ); aOutputFile << " rotation " << std::setprecision( 3 );
aOutputFile << rot[0] << " " << rot[1] << " " << rot[2] << " " << rot[3] << "\n";
} }
// adjust 3D shape local offset position // adjust 3D shape local offset position
...@@ -1258,15 +1306,15 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule ...@@ -1258,15 +1306,15 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule
RotatePoint( &offsetx, &offsety, aModule->GetOrientation() ); RotatePoint( &offsetx, &offsety, aModule->GetOrientation() );
fprintf( aOutputFile, " translation %g %g %g\n", aOutputFile << " translation " << std::setprecision( aModel.precision );
(offsetx + aModule->GetPosition().x) * aModel.scale + aModel.tx, aOutputFile << (( offsetx + aModule->GetPosition().x) * aModel.scale + aModel.tx ) << " ";
-(offsety + aModule->GetPosition().y) * aModel.scale - aModel.ty, aOutputFile << ( -(offsety + aModule->GetPosition().y) * aModel.scale - aModel.ty ) << " ";
(offsetz * aModel.scale ) + aModel.GetLayerZ( aModule->GetLayer() ) ); aOutputFile << ( (offsetz * aModel.scale ) + aModel.GetLayerZ( aModule->GetLayer() ) ) << "\n";
fprintf( aOutputFile, " scale %g %g %g\n", aOutputFile << " scale ";
vrmlm->m_MatScale.x * aVRMLModelsToBiu, aOutputFile << ( vrmlm->m_MatScale.x * aVRMLModelsToBiu ) << " ";
vrmlm->m_MatScale.y * aVRMLModelsToBiu, aOutputFile << ( vrmlm->m_MatScale.y * aVRMLModelsToBiu ) << " ";
vrmlm->m_MatScale.z * aVRMLModelsToBiu ); aOutputFile << ( vrmlm->m_MatScale.z * aVRMLModelsToBiu ) << "\n";
if( fname.EndsWith( wxT( "x3d" ) ) ) if( fname.EndsWith( wxT( "x3d" ) ) )
{ {
...@@ -1276,18 +1324,25 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule ...@@ -1276,18 +1324,25 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule
{ {
// embed x3d model in vrml format // embed x3d model in vrml format
parser->Load( fname ); parser->Load( fname );
fprintf( aOutputFile,
" children [\n %s ]\n", TO_UTF8( parser->VRML_representation() ) ); try
fprintf( aOutputFile, " }\n" ); {
aOutputFile << " children [\n ";
aOutputFile << TO_UTF8( parser->VRML_representation() ) << " ]\n";
aOutputFile << " }\n";
}
catch( const std::exception& e )
{
delete parser; delete parser;
throw;
}
} }
} }
else else
{ {
fprintf( aOutputFile, aOutputFile << " children [\n Inline {\n url \"";
" children [\n Inline {\n url \"%s\"\n } ]\n", aOutputFile << TO_UTF8( fname ) << "\"\n } ]\n";
TO_UTF8( fname ) ); aOutputFile << " }\n";
fprintf( aOutputFile, " }\n" );
} }
} }
} }
...@@ -1298,17 +1353,18 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName, ...@@ -1298,17 +1353,18 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName,
const wxString& a3D_Subdir ) const wxString& a3D_Subdir )
{ {
wxString msg; wxString msg;
FILE* output_file;
BOARD* pcb = GetBoard(); BOARD* pcb = GetBoard();
bool ok = true;
MODEL_VRML model3d; MODEL_VRML model3d;
VRMLEXPORT::model_vrml = &model3d; model_vrml = &model3d;
std::ofstream output_file;
output_file = wxFopen( aFullFileName, wxT( "wt" ) );
if( output_file == NULL ) try
return false; {
output_file.exceptions( std::ofstream::failbit );
output_file.open( TO_UTF8( aFullFileName ), std::ios_base::out );
// Switch the locale to standard C (needed to print floating point numbers like 1.3) // Switch the locale to standard C (needed to print floating point numbers like 1.3)
SetLocaleTo_C_standard(); SetLocaleTo_C_standard();
...@@ -1318,27 +1374,25 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName, ...@@ -1318,27 +1374,25 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName,
name.Replace( wxT( "\\" ), wxT( "/" ) ); name.Replace( wxT( "\\" ), wxT( "/" ) );
ChangeIllegalCharacters( name, false ); ChangeIllegalCharacters( name, false );
fprintf( output_file, "#VRML V2.0 utf8\n"
"WorldInfo {\n"
" title \"%s - Generated by Pcbnew\"\n"
"}\n", TO_UTF8( name ) );
// Global VRML scale to export to a different scale. output_file << "#VRML V2.0 utf8\n";
model3d.scale = aMMtoWRMLunit / MM_PER_IU; output_file << "WorldInfo {\n";
output_file << " title \"" << TO_UTF8( name ) << " - Generated by Pcbnew\"\n";
output_file << "}\n";
// Set the mechanical deviation limit (in this case 0.02mm) // Set the VRML world scale factor
// XXX - NOTE: the value should be set via the GUI model3d.SetScale( aMMtoWRMLunit );
model3d.SetMaxDev( 20000 * model3d.scale );
fprintf( output_file, "Transform {\n" ); output_file << "Transform {\n";
// compute the offset to center the board on (0, 0, 0) // compute the offset to center the board on (0, 0, 0)
// XXX - NOTE: we should allow the user a GUI option to specify the offset // XXX - NOTE: we should allow the user a GUI option to specify the offset
EDA_RECT bbbox = pcb->ComputeBoundingBox(); EDA_RECT bbbox = pcb->ComputeBoundingBox();
model3d.SetOffset( -model3d.scale * bbbox.Centre().x, -model3d.scale * bbbox.Centre().y ); model3d.SetOffset( -model3d.scale * bbbox.Centre().x,
-model3d.scale * bbbox.Centre().y );
fprintf( output_file, " children [\n" ); output_file << " children [\n";
// Preliminary computation: the z value for each layer // Preliminary computation: the z value for each layer
compute_layer_Zs( model3d, pcb ); compute_layer_Zs( model3d, pcb );
...@@ -1373,13 +1427,23 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName, ...@@ -1373,13 +1427,23 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName,
write_layers( model3d, output_file, pcb ); write_layers( model3d, output_file, pcb );
// Close the outer 'transform' node // Close the outer 'transform' node
fputs( "]\n}\n", output_file ); output_file << "]\n}\n";
}
catch( const std::exception& e )
{
wxString msg;
msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( e.what() );
wxMessageBox( msg );
ok = false;
}
// End of work // End of work
fclose( output_file ); output_file.exceptions( std::ios_base::goodbit );
output_file.close();
SetLocaleTo_Default(); // revert to the current locale SetLocaleTo_Default(); // revert to the current locale
return true; return ok;
} }
......
/*
* file: vrml_board.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
*/
/*
* NOTES ON OUTPUT PRECISION:
*
* If we use %.6f then we have no need for special unit dependent formatting:
*
* inch: .0254 microns
* mm: 0.001 microns
* m: 1 micron
*
*/
#include <sstream>
#include <string>
#include <iomanip>
#include <cmath>
#include <fctsys.h>
#include <vrml_board.h>
#ifndef CALLBACK
#define CALLBACK
#endif
#define GLCALLBACK(x) (( void (CALLBACK*)() )&(x))
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 );
}
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 CalcNSides( double rad, double dev )
{
if( dev <= 0 || rad <= 0 )
return 6;
int csides;
double n = dev / rad;
// note: in the following, the first comparison and csides is chosen to
// yield a maximum of 360 segments; in practice we probably want a smaller limit.
if( n < 0.0001523048 )
csides = 360;
else if( n >= 0.5 ) // 0.5 yields an angle >= 60 deg. (6 or fewer sides)
csides = 6;
else
csides = M_PI * 2.0 / acos( 1.0 - n ) + 1;
if( csides < 6 )
csides = 6;
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], void* vertex_data[4],
GLfloat weight[4], void** outData, void* user_data )
{
VRML_LAYER* lp = (VRML_LAYER*) user_data;
*outData = lp->AddExtraVertex( coords[0], coords[1] );
}
VRML_LAYER::VRML_LAYER()
{
fix = false;
Fault = false;
idx = 0;
ord = 0;
glcmd = 0;
pholes = NULL;
maxdev = 0.02;
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;
}
}
// 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();
}
while( !areas.empty() )
areas.pop_back();
for( i = vertices.size(); i > 0; --i )
{
delete vertices.back();
vertices.pop_back();
}
clearTmp();
}
// set the max. deviation of an arc segment
bool VRML_LAYER::SetMaxDev( double max )
{
// assure max. dev > 2 microns regardless of the
// prevailing units ( inch, mm, m, 0.1 inch )
if( max < 0.000002 )
{
error = "SetMaxDev(): specified value is < 0.000002";
return false;
}
maxdev = max;
return true;
}
// 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;
while( !triplets.empty() )
triplets.pop_back();
for( i = outline.size(); i > 0; --i )
{
delete outline.back();
outline.pop_back();
}
for( i = ordmap.size(); i > 0; --i )
ordmap.pop_back();
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
for( i = vlist.size(); i > 0; --i )
vlist.pop_back();
// 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( void )
{
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 );
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 aContour, double x, double y )
{
if( fix )
{
error = "AddVertex(): no more vertices may be added (Tesselate was previously executed)";
return false;
}
if( aContour < 0 || (unsigned int) aContour >= 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 = x;
vertex->y = y;
vertex->i = idx++;
vertex->o = -1;
VERTEX_3D* v2 = NULL;
if( contours[aContour]->size() > 0 )
v2 = vertices[ contours[aContour]->back() ];
vertices.push_back( vertex );
contours[aContour]->push_back( vertex->i );
if( v2 )
areas[aContour] += ( x - v2->x ) * ( y + 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 aContour, bool hole )
{
if( aContour < 0 || (unsigned int) aContour >= contours.size() )
{
error = "EnsureWinding(): aContour is outside the valid range";
return false;
}
std::list<int>* cp = contours[aContour];
if( cp->size() < 3 )
{
error = "EnsureWinding(): there are fewer than 3 vertices";
return false;
}
double dir = areas[aContour];
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( ( hole && dir < 0 ) || ( !hole && dir > 0 ) )
{
cp->reverse();
areas[aContour] = -areas[aContour];
}
return true;
}
// adds a circle the existing list; if 'hole' is true the contour is
// a hole. Returns true if OK.
bool VRML_LAYER::AddCircle( double x, double y, double rad, int csides, bool hole )
{
int pad = NewContour();
if( pad < 0 )
{
error = "AddCircle(): failed to add a contour";
return false;
}
if( csides < 6 )
csides = CalcNSides( rad, maxdev );
// even numbers give prettier results
if( csides & 1 )
csides += 1;
double da = M_PI * 2.0 / csides;
bool fail = false;
if( hole )
{
for( double angle = 0; angle < M_PI * 2; angle += da )
fail |= !AddVertex( pad, x + rad * cos( angle ), y - rad * sin( angle ) );
}
else
{
for( double angle = 0; angle < M_PI * 2; angle += da )
fail |= !AddVertex( pad, x + rad * cos( angle ), y + rad * sin( angle ) );
}
return !fail;
}
// 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 cx, double cy, double length, double width,
double angle, int csides, bool hole )
{
if( width > length )
{
angle += M_PI2;
std::swap( length, width );
}
width /= 2.0;
length = length / 2.0 - width;
if( csides < 6 )
csides = CalcNSides( width, maxdev );
if( csides & 1 )
csides += 1;
csides /= 2;
double capx, capy;
capx = cx + cos( angle ) * length;
capy = cy + sin( angle ) * length;
double ang, da;
int i;
int pad = NewContour();
if( pad < 0 )
{
error = "AddCircle(): failed to add a contour";
return false;
}
da = M_PI / csides;
bool fail = false;
if( hole )
{
for( ang = angle + M_PI2, i = 0; i < csides; ang -= da, ++i )
fail |= !AddVertex( pad, capx + width * cos( ang ), capy + width * sin( ang ) );
ang = angle - M_PI2;
fail |= !AddVertex( pad, capx + width * cos( ang ), capy + width * sin( ang ) );
capx = cx - cos( angle ) * length;
capy = cy - sin( angle ) * length;
for( ang = angle - M_PI2, i = 0; i < csides; ang -= da, ++i )
fail |= !AddVertex( pad, capx + width * cos( ang ), capy + width * sin( ang ) );
ang = angle + M_PI2;
fail |= !AddVertex( pad, capx + width * cos( ang ), capy + width * sin( ang ) );
}
else
{
for( ang = angle - M_PI2, i = 0; i < csides; ang += da, ++i )
fail |= !AddVertex( pad, capx + width * cos( ang ), capy + width * sin( ang ) );
ang = angle + M_PI2;
fail |= !AddVertex( pad, capx + width * cos( ang ), capy + width * sin( ang ) );
capx = cx - cos( angle ) * length;
capy = cy - sin( angle ) * length;
for( ang = angle + M_PI2, i = 0; i < csides; ang += da, ++i )
fail |= !AddVertex( pad, capx + width * cos( ang ), capy + width * sin( ang ) );
ang = angle - M_PI2;
fail |= !AddVertex( pad, capx + width * cos( ang ), capy + width * sin( ang ) );
}
return !fail;
}
// adds an arc with the given center, start point, pen width, and angle.
bool VRML_LAYER::AddArc( double cx, double cy, double startx, double starty,
double width, double angle, int csides, bool hole )
{
// 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( angle < 0.01745 && angle > -0.01745 )
{
error = "AddArc(): angle is too small: abs( angle ) < 0.01745";
return false;
}
double rad = sqrt( (startx - cx) * (startx - cx) + (starty - cy) * (starty - cy) );
width /= 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( width >= ( 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 + width;
double irad = rad - width;
int osides = csides * angle / ( M_PI * 2.0 );
int isides = csides * angle / ( M_PI * 2.0 );
if( osides < 0 )
osides = -osides;
if( osides < 3 )
{
osides = CalcNSides( orad, maxdev ) * angle / ( M_PI * 2.0 );
if( osides < 0 )
osides = -osides;
if( osides < 3 )
osides = 3;
}
if( isides < 0 )
isides = -isides;
if( isides < 3 )
{
isides = CalcNSides( irad, maxdev ) * angle / ( M_PI * 2.0 );
if( isides < 0 )
isides = -isides;
if( isides < 3 )
isides = 3;
}
if( csides < 6 )
csides = CalcNSides( width, maxdev );
if( csides & 1 )
csides += 1;
csides /= 2;
double stAngle = atan2( starty - cy, startx - cx );
double endAngle = stAngle + angle;
// calculate ends of inner and outer arc
double oendx = cx + orad* cos( endAngle );
double oendy = cy + orad* sin( endAngle );
double ostx = cx + orad* cos( stAngle );
double osty = cy + orad* sin( stAngle );
double iendx = cx + irad* cos( endAngle );
double iendy = cy + irad* sin( endAngle );
double istx = cx + irad* cos( stAngle );
double isty = cy + irad* sin( stAngle );
if( ( angle < 0 && !hole ) || ( angle > 0 && hole ) )
{
angle = -angle;
std::swap( stAngle, endAngle );
std::swap( oendx, ostx );
std::swap( oendy, osty );
std::swap( iendx, istx );
std::swap( iendy, isty );
}
int arc = NewContour();
if( arc < 0 )
{
error = "AddArc(): could not create a contour";
return false;
}
// trace the outer arc:
int i;
double ang;
double da = angle / osides;
for( ang = stAngle, i = 0; i < osides; ang += da, ++i )
AddVertex( arc, cx + orad * cos( ang ), cy + orad * sin( ang ) );
// trace the first cap
double capx = ( iendx + oendx ) / 2.0;
double capy = ( iendy + oendy ) / 2.0;
if( hole )
da = -M_PI / csides;
else
da = M_PI / csides;
for( ang = endAngle + da, i = 2; i < csides; ang += da, ++i )
AddVertex( arc, capx + width * cos( ang ), capy + width * sin( ang ) );
// trace the inner arc:
da = -angle / isides;
for( ang = endAngle, i = 0; i < isides; ang += da, ++i )
AddVertex( arc, cx + irad * cos( ang ), cy + irad * sin( ang ) );
// trace the final cap
capx = ( istx + ostx ) / 2.0;
capy = ( isty + osty ) / 2.0;
if( hole )
da = -M_PI / csides;
else
da = M_PI / csides;
for( ang = stAngle + M_PI + da, i = 2; i < csides; ang += da, ++i )
AddVertex( arc, capx + width * cos( ang ), capy + width * 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 )
{
if( !tess )
{
error = "Tesselate(): GLU tesselator was not initialized";
return false;
}
pholes = holes;
Fault = false;
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;
// open the polygon
gluTessBeginPolygon( tess, this );
pushVertices( false );
// close the polygon
gluTessEndPolygon( tess );
if( Fault )
return false;
// push the (solid) outline to the tesselator
if( !pushOutline( holes ) )
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
for( int i = outline.size(); i > 0; --i )
{
delete outline.back();
outline.pop_back();
}
for( unsigned int i = ordmap.size(); i > 0; --i )
ordmap.pop_back();
// 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;
}
ord = 0;
// close the polygon; we now have all the data necessary for the tesselation
gluTessEndPolygon( tess );
// request a tesselated surface
gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE );
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;
}
gluTessBeginPolygon( tess, this );
std::list<std::list<int>*>::const_iterator obeg = outline.begin();
std::list<std::list<int>*>::const_iterator oend = outline.end();
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() )
{
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 )
{
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;
}
return true;
}
// writes out the vertex list;
// 'z' is the Z coordinate of every point
bool VRML_LAYER::WriteVertices( double z, FILE* fp )
{
if( !fp )
{
error = "WriteVertices(): invalid file pointer";
return false;
}
if( ordmap.size() < 3 )
{
error = "WriteVertices(): not enough vertices";
return false;
}
int i, j;
VERTEX_3D* vp = getVertexByIndex( ordmap[0], pholes );
if( !vp )
return false;
std::string strx, stry, strz;
FormatDoublet( vp->x, vp->y, 6, strx, stry );
FormatSinglet( z, 6, strz );
fprintf( fp, "%s %s %s", strx.c_str(), stry.c_str(), strz.c_str() );
for( i = 1, j = ordmap.size(); i < j; ++i )
{
vp = getVertexByIndex( ordmap[i], pholes );
if( !vp )
return false;
FormatDoublet( vp->x, vp->y, 6, strx, stry );
if( i & 1 )
fprintf( fp, ", %s %s %s", strx.c_str(), stry.c_str(), strz.c_str() );
else
fprintf( fp, ",\n%s %s %s", strx.c_str(), stry.c_str(), strz.c_str() );
}
return true;
}
// 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 top, double bottom, FILE* fp )
{
if( !fp )
{
error = "Write3DVertices(): NULL file pointer";
return false;
}
if( ordmap.size() < 3 )
{
error = "Write3DVertices(): insufficient vertices";
return false;
}
if( top <= bottom )
{
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, vp->y, 6, strx, stry );
FormatSinglet( top, 6, strz );
fprintf( fp, "%s %s %s", strx.c_str(), stry.c_str(), strz.c_str() );
for( i = 1, j = ordmap.size(); i < j; ++i )
{
vp = getVertexByIndex( ordmap[i], pholes );
if( !vp )
return false;
FormatDoublet( vp->x, vp->y, 6, strx, stry );
if( i & 1 )
fprintf( fp, ", %s %s %s", strx.c_str(), stry.c_str(), strz.c_str() );
else
fprintf( fp, ",\n%s %s %s", strx.c_str(), stry.c_str(), strz.c_str() );
}
// repeat for the bottom layer
vp = getVertexByIndex( ordmap[0], pholes );
FormatDoublet( vp->x, vp->y, 6, strx, stry );
FormatSinglet( bottom, 6, strz );
bool endl;
if( i & 1 )
{
fprintf( fp, ", %s %s %s", strx.c_str(), stry.c_str(), strz.c_str() );
endl = false;
}
else
{
fprintf( fp, ",\n%s %s %s", strx.c_str(), stry.c_str(), strz.c_str() );
endl = true;
}
for( i = 1, j = ordmap.size(); i < j; ++i )
{
vp = getVertexByIndex( ordmap[i], pholes );
FormatDoublet( vp->x, vp->y, 6, strx, stry );
if( endl )
{
fprintf( fp, ", %s %s %s", strx.c_str(), stry.c_str(), strz.c_str() );
endl = false;
}
else
{
fprintf( fp, ",\n%s %s %s", strx.c_str(), stry.c_str(), strz.c_str() );
endl = true;
}
}
return true;
}
// 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 top, FILE* fp )
{
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( top )
fprintf( fp, "%d, %d, %d, -1", tbeg->i1, tbeg->i2, tbeg->i3 );
else
fprintf( fp, "%d, %d, %d, -1", tbeg->i2, tbeg->i1, tbeg->i3 );
++tbeg;
while( tbeg != tend )
{
if( (i++ & 7) == 4 )
{
i = 1;
if( top )
fprintf( fp, ",\n%d, %d, %d, -1", tbeg->i1, tbeg->i2, tbeg->i3 );
else
fprintf( fp, ",\n%d, %d, %d, -1", tbeg->i2, tbeg->i1, tbeg->i3 );
}
else
{
if( top )
fprintf( fp, ", %d, %d, %d, -1", tbeg->i1, tbeg->i2, tbeg->i3 );
else
fprintf( fp, ", %d, %d, %d, -1", tbeg->i2, tbeg->i1, tbeg->i3 );
}
++tbeg;
}
return true;
}
// writes out the index list for a 3D feature
bool VRML_LAYER::Write3DIndices( FILE* fp )
{
if( triplets.empty() )
{
error = "Write3DIndices(): no triplets (triangular facets) to write";
return false;
}
if( outline.empty() )
{
error = "WriteIndices(): no outline available";
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;
int idx2 = ordmap.size(); // index to the bottom vertices
// print out the top vertices
fprintf( fp, "%d, %d, %d, -1", tbeg->i1, tbeg->i2, tbeg->i3 );
++tbeg;
while( tbeg != tend )
{
if( (i++ & 7) == 4 )
{
i = 1;
fprintf( fp, ",\n%d, %d, %d, -1", tbeg->i1, tbeg->i2, tbeg->i3 );
}
else
{
fprintf( fp, ", %d, %d, %d, -1", tbeg->i1, tbeg->i2, tbeg->i3 );
}
++tbeg;
}
// print out the bottom vertices
tbeg = triplets.begin();
while( tbeg != tend )
{
if( (i++ & 7) == 4 )
{
i = 1;
fprintf( fp, ",\n%d, %d, %d, -1", tbeg->i2 + idx2, tbeg->i1 + idx2, tbeg->i3 + idx2 );
}
else
{
fprintf( fp, ", %d, %d, %d, -1", tbeg->i2 + idx2, tbeg->i1 + idx2, tbeg->i3 + idx2 );
}
++tbeg;
}
int firstPoint;
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;
while( obeg != oend )
{
cp = *obeg;
if( cp->size() < 3 )
{
++obeg;
continue;
}
cbeg = cp->begin();
cend = cp->end();
firstPoint = *(cbeg++);
lastPoint = firstPoint;
while( cbeg != cend )
{
curPoint = *(cbeg++);
fprintf( fp, ",\n %d, %d, %d, -1, %d, %d, %d, -1",
curPoint, lastPoint, curPoint + idx2,
curPoint + idx2, lastPoint, lastPoint + idx2 );
lastPoint = curPoint;
}
fprintf( fp, ",\n %d, %d, %d, -1, %d, %d, %d, -1",
firstPoint, lastPoint, firstPoint + idx2,
firstPoint + idx2, lastPoint, lastPoint + idx2 );
++obeg;
}
return true;
}
// 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 6 decimal places
// on the VRML output
double err = 0.000001;
// 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 x, double y )
{
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 = x;
vertex->y = y;
vertex->i = eidx++;
vertex->o = -1;
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;
for( unsigned int i = 0; i < vlist.size(); ++i )
{
loop->push_back( vlist[i]->o );
}
outline.push_back( loop );
}
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 )
{
error = "";
error = (const char*)gluGetString( errorID );
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] );
}
// 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 );
}
}
VERTEX_3D* VRML_LAYER::getVertexByIndex( int index, VRML_LAYER* holes )
{
if( index < 0 || (unsigned int) index >= ( idx + hidx + extra_verts.size() ) )
{
error = "getVertexByIndex():BUG: invalid index";
return NULL;
}
if( index < idx )
{
// vertex is in the vertices[] list
return vertices[ index ];
}
else if( index >= idx + hidx )
{
// vertex is in the extra_verts[] list
return extra_verts[index - idx - hidx];
}
// vertex is in the holes object
if( !holes )
{
error = "getVertexByIndex():BUG: invalid index";
return NULL;
}
VERTEX_3D* vp = holes->GetVertexByIndex( index );
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 index )
{
int i0 = vertices[0]->i;
if( index < i0 || index >= ( i0 + (int) vertices.size() ) )
{
error = "GetVertexByIndex(): invalid index";
return NULL;
}
return vertices[index - i0];
}
// return the error string
const std::string& VRML_LAYER::GetError( void )
{
return error;
}
/*
* file: vrml_board.h
*
* 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
*/
/**
* @file vrml_board.h
*/
/*
* Classes and structures to support the tesselation of a
* PCB for VRML output.
*/
#ifndef VRML_BOARD_H
#define VRML_BOARD_H
#ifdef __WXMAC__
# ifdef __DARWIN__
# include <OpenGL/glu.h>
# else
# include <glu.h>
# endif
#else
# include <GL/glu.h>
#endif
#include <cstdio>
#include <vector>
#include <list>
#include <utility>
#ifndef M_PI2
#define M_PI2 ( M_PI / 2.0 )
#endif
#ifndef M_PI4
#define M_PI4 ( M_PI / 4.0 )
#endif
class GLUtesselator;
struct VERTEX_3D
{
double x;
double y;
int i; // vertex index
int o; // vertex order
};
struct TRIPLET_3D
{
int i1, i2, i3;
TRIPLET_3D( int p1, int p2, int p3 )
{
i1 = p1;
i2 = p2;
i3 = p3;
}
};
class VRML_LAYER
{
private:
bool fix; // when true, no more vertices may be added by the user
int idx; // vertex index (number of contained vertices)
int ord; // vertex order (number of ordered vertices)
std::vector<VERTEX_3D*> vertices; // vertices of all contours
std::vector<std::list<int>*> contours; // lists of vertices for each contour
std::vector< double > areas; // area of the contours (positive if winding is CCW)
std::list<TRIPLET_3D> triplets; // output facet triplet list (triplet of ORDER values)
std::list<std::list<int>*> outline; // indices for outline outputs (index by ORDER values)
std::vector<int> ordmap; // mapping of ORDER to INDEX
std::string error; // error message
double maxdev; // max. deviation from circle when calculating N sides
int hidx; // number of vertices in the holes
int eidx; // index for extra vertices
std::vector<VERTEX_3D*> extra_verts; // extra vertices added for outlines and facets
std::vector<VERTEX_3D*> vlist; // vertex list for the GL command in progress
VRML_LAYER* pholes; // pointer to another layer object used for tesselation;
// this object is normally expected to hold only holes
GLUtesselator* tess; // local instance of the GLU tesselator
GLenum glcmd; // current GL command type ( fan, triangle, tri-strip, loop )
void clearTmp( void ); // clear ephemeral data used by the tesselation routine
// add a triangular facet (triplet) to the output index list
bool addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 );
// retrieve a vertex given its index; the vertex may be contained in the
// vertices vector, extra_verts vector, or foreign VRML_LAYER object
VERTEX_3D* getVertexByIndex( int index, VRML_LAYER* holes );
void processFan( void ); // process a GL_TRIANGLE_FAN list
void processStrip( void ); // process a GL_TRIANGLE_STRIP list
void processTri( void ); // process a GL_TRIANGLES list
void pushVertices( bool holes ); // push the internal vertices
bool pushOutline( VRML_LAYER* holes ); // push the outline vertices
public:
/// set to true when a fault is encountered during tesselation
bool Fault;
VRML_LAYER();
virtual ~VRML_LAYER();
/**
* Function Clear
* erases all data.
*/
void Clear( void );
/**
* Function GetSize
* returns the total number of vertices indexed
*/
int GetSize( void );
/**
* Function SetMaxDev
* sets the maximum deviation from a circle; this parameter is
* used for the automatic calculation of segments within a
* circle or an arc.
*
* @param max is the maximum deviation from a perfect circle or arc;
* minimum value is 0.000002 units
*
* @return bool: true if the value was accepted
*/
bool SetMaxDev( double max );
/**
* Function NewContour
* creates a new list of vertices and returns an index to the list
*
* @return int: index to the list or -1 if the operation failed
*/
int NewContour( void );
/**
* Function AddVertex
* adds a point to the requested contour
*
* @param aContour is an index previously returned by a call to NewContour()
* @param x is the X coordinate of the vertex
* @param y is the Y coordinate of the vertex
*
* @return bool: true if the vertex was added
*/
bool AddVertex( int aContour, double x, double y );
/**
* Function EnsureWinding
* checks the winding of a contour and ensures that it is a hole or
* a solid depending on the value of @param hole
*
* @param aContour is an index to a contour as returned by NewContour()
* @param hole determines if the contour must be a hole
*
* @return bool: true if the operation suceeded
*/
bool EnsureWinding( int aContour, bool hole );
/**
* Function AddCircle
* creates a circular contour and adds it to the internal list
*
* @param x is the X coordinate of the hole center
* @param y is the Y coordinate of the hole center
* @param rad is the radius of the hole
* @param csides is the number of sides (segments) in a circle;
* use a value of 1 to automatically calculate a suitable number.
* @param hole determines if the contour to be created is a cutout
*
* @return bool: true if the new contour was successfully created
*/
bool AddCircle( double x, double y, double rad, int csides, bool hole = false );
/**
* Function AddSlot
* creates and adds a slot feature to the list of contours
*
* @param cx is the X coordinate of the slot
* @param cy is the Y coordinate of the slot
* @param length is the length of the slot along the major axis
* @param width is the width of the slot along the minor axis
* @param angle (radians) is the orientation of the slot
* @param csides is the number of sides to a circle; use 1 to
* take advantage of automatic calculations.
* @param hole determines whether the slot is a hole or a solid
*
* @return bool: true if the slot was successfully created
*/
bool AddSlot( double cx, double cy, double length, double width,
double angle, int csides, bool hole = false );
/**
* Function AddArc
* creates an arc and adds it to the internal list of contours
*
* @param cx is the X coordinate of the arc's center
* @param cy is the Y coordinate of the arc's center
* @param startx is the X coordinate of the starting point
* @param starty is the Y coordinate of the starting point
* @param width is the width of the arc
* @param angle is the included angle
* @param csides is the number of segments in a circle; use 1
* to take advantage of automatic calculations of this number
* @param hole determined whether the arc is to be a hole or a solid
*
* @return bool: true if the feature was successfully created
*/
bool AddArc( double cx, double cy, double startx, double starty,
double width, double angle, int csides, bool hole = false );
/**
* Function Tesselate
* creates a list of outline vertices as well as the
* vertex sets required to render the surface.
*
* @param holes is a pointer to cutouts to be imposed on the
* surface.
*
* @return bool: true if the operation succeeded
*/
bool Tesselate( VRML_LAYER* holes );
/**
* Function WriteVertices
* writes out the list of vertices required to render a
* planar surface.
*
* @param z is the Z coordinate of the plane
* @param fp is the file to write to
*
* @return bool: true if the operation succeeded
*/
bool WriteVertices( double z, FILE* fp );
/**
* Function Write3DVertices
* writes out the list of vertices required to render an extruded solid
*
* @param top is the Z coordinate of the top plane
* @param bottom is the Z coordinate of the bottom plane
* @param fp is the file to write to
*
* @return bool: true if the operation succeeded
*/
bool Write3DVertices( double top, double bottom, FILE* fp );
/**
* Function WriteIndices
* writes out the vertex sets required to render a planar
* surface.
*
* @param top is true if the surface is to be visible from above;
* if false the surface will be visible from below.
* @param fp is the file to write to
*
* @return bool: true if the operation succeeded
*/
bool WriteIndices( bool top, FILE* fp );
/**
* Function Write3DIndices
* writes out the vertex sets required to render an extruded solid
*
* @param fp is the file to write to
*
* @return bool: true if the operation succeeded
*/
bool Write3DIndices( FILE* fp );
/**
* Function AddExtraVertex
* adds an extra vertex as required by the GLU tesselator
*
* @return VERTEX_3D*: is the new vertex or NULL if a vertex
* could not be created.
*/
VERTEX_3D* AddExtraVertex( double x, double y );
/**
* Function glStart
* is invoked by the GLU tesselator callback to notify this object
* of the type of GL command which is applicable to the upcoming
* vertex list.
*
* @param cmd is the GL command
*/
void glStart( GLenum cmd );
/**
* Function glPushVertex
* is invoked by the GLU tesselator callback; the supplied vertex is
* added to the internal list of vertices awaiting processing upon
* execution of glEnd()
*
* @param vertex is a vertex forming part of the GL command as previously
* set by glStart
*/
void glPushVertex( VERTEX_3D* vertex );
/**
* Function glEnd
* is invoked by the GLU tesselator callback to notify this object
* that the vertex list is complete and ready for processing
*/
void glEnd( void );
/**
* Function SetGLError
* sets the error message according to the specified OpenGL error
*/
void SetGLError( GLenum error_id );
/**
* Function Import
* inserts all contours into the given tesselator; this
* results in the renumbering of all vertices from @param start.
* Take care when using this call since tesselators cannot work on
* the internal data concurrently.
*
* @param start is the starting number for vertex indices
* @param tess is a pointer to a GLU Tesselator object
*
* @return int: the number of vertices exported
*/
int Import( int start, GLUtesselator* tess );
/**
* Function GetVertexByIndex
* returns a pointer to the requested vertex or
* NULL if no such vertex exists.
*
* @param ptindex is a vertex index
*
* @return VERTEX_3D*: the requested vertex or NULL
*/
VERTEX_3D* GetVertexByIndex( int ptindex );
/*
* Function GetError
* Returns the error message related to the last failed operation
*/
const std::string& GetError( void );
};
#endif // VRML_BOARD_H
...@@ -775,7 +775,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli ...@@ -775,7 +775,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5) aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< aOutline->front()->startPoint.x << " " << aOutline->front()->startPoint.x << " "
<< aOutline->front()->startPoint.y << " " << aOutline->front()->startPoint.y << " "
<< setprecision(5) << -aOutline->front()->angle << "\n"; << setprecision(2) << -aOutline->front()->angle << "\n";
} }
} }
else else
...@@ -799,7 +799,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli ...@@ -799,7 +799,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1) aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< (aOutline->front()->startPoint.x / IDF_THOU_TO_MM) << " " << (aOutline->front()->startPoint.x / IDF_THOU_TO_MM) << " "
<< (aOutline->front()->startPoint.y / IDF_THOU_TO_MM) << " " << (aOutline->front()->startPoint.y / IDF_THOU_TO_MM) << " "
<< setprecision(5) << -aOutline->front()->angle << "\n"; << setprecision(2) << -aOutline->front()->angle << "\n";
} }
} }
...@@ -819,7 +819,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli ...@@ -819,7 +819,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5) aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->startPoint.x << " " << (*bo)->startPoint.x << " "
<< (*bo)->startPoint.y << " " << (*bo)->startPoint.y << " "
<< setprecision(5) << -(*bo)->angle << "\n"; << setprecision(2) << -(*bo)->angle << "\n";
} }
} }
else else
...@@ -835,7 +835,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli ...@@ -835,7 +835,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1) aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->startPoint.x / IDF_THOU_TO_MM) << " " << ((*bo)->startPoint.x / IDF_THOU_TO_MM) << " "
<< ((*bo)->startPoint.y / IDF_THOU_TO_MM) << " " << ((*bo)->startPoint.y / IDF_THOU_TO_MM) << " "
<< setprecision(5) << -(*bo)->angle << "\n"; << setprecision(2) << -(*bo)->angle << "\n";
} }
} }
...@@ -869,7 +869,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli ...@@ -869,7 +869,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5) aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->endPoint.x << " " << (*bo)->endPoint.x << " "
<< (*bo)->endPoint.y << " " << (*bo)->endPoint.y << " "
<< setprecision(5) << (*bo)->angle << "\n"; << setprecision(2) << (*bo)->angle << "\n";
} }
} }
else else
...@@ -893,7 +893,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli ...@@ -893,7 +893,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1) aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " " << ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " "
<< ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " " << ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " "
<< setprecision(5) << (*bo)->angle << "\n"; << setprecision(2) << (*bo)->angle << "\n";
} }
} }
...@@ -915,7 +915,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli ...@@ -915,7 +915,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5) aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->endPoint.x << " " << (*bo)->endPoint.x << " "
<< (*bo)->endPoint.y << " " << (*bo)->endPoint.y << " "
<< setprecision(5) << (*bo)->angle << "\n"; << setprecision(2) << (*bo)->angle << "\n";
} }
} }
else else
...@@ -931,7 +931,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli ...@@ -931,7 +931,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1) aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " " << ((*bo)->endPoint.x / IDF_THOU_TO_MM) << " "
<< ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " " << ((*bo)->endPoint.y / IDF_THOU_TO_MM) << " "
<< setprecision(5) << (*bo)->angle << "\n"; << setprecision(2) << (*bo)->angle << "\n";
} }
} }
...@@ -1281,7 +1281,7 @@ bool BOARD_OUTLINE::addOutline( IDF_OUTLINE* aOutline ) ...@@ -1281,7 +1281,7 @@ bool BOARD_OUTLINE::addOutline( IDF_OUTLINE* aOutline )
outlines.push_back( aOutline ); outlines.push_back( aOutline );
} }
catch( std::exception& e ) catch( const std::exception& e )
{ {
errormsg = e.what(); errormsg = e.what();
......
...@@ -2382,7 +2382,7 @@ void IDF3_BOARD::readBoardFile( const std::string& aFileName, bool aNoSubstitute ...@@ -2382,7 +2382,7 @@ void IDF3_BOARD::readBoardFile( const std::string& aFileName, bool aNoSubstitute
} }
} }
} }
catch( std::exception& e ) catch( const std::exception& e )
{ {
brd.exceptions ( std::ios_base::goodbit ); brd.exceptions ( std::ios_base::goodbit );
...@@ -2689,7 +2689,7 @@ void IDF3_BOARD::readLibFile( const std::string& aFileName ) ...@@ -2689,7 +2689,7 @@ void IDF3_BOARD::readLibFile( const std::string& aFileName )
while( lib.good() ) readLibSection( lib, state, this ); while( lib.good() ) readLibSection( lib, state, this );
} }
catch( std::exception& e ) catch( const std::exception& e )
{ {
lib.exceptions ( std::ios_base::goodbit ); lib.exceptions ( std::ios_base::goodbit );
...@@ -2773,7 +2773,7 @@ bool IDF3_BOARD::ReadFile( const wxString& aFullFileName, bool aNoSubstituteOutl ...@@ -2773,7 +2773,7 @@ bool IDF3_BOARD::ReadFile( const wxString& aFullFileName, bool aNoSubstituteOutl
// read the board file // read the board file
readBoardFile( bfname, aNoSubstituteOutlines ); readBoardFile( bfname, aNoSubstituteOutlines );
} }
catch( std::exception& e ) catch( const std::exception& e )
{ {
Clear(); Clear();
errormsg = e.what(); errormsg = e.what();
...@@ -2821,7 +2821,7 @@ bool IDF3_BOARD::writeLibFile( const std::string& aFileName ) ...@@ -2821,7 +2821,7 @@ bool IDF3_BOARD::writeLibFile( const std::string& aFileName )
} }
} }
catch( std::exception& e ) catch( const std::exception& e )
{ {
lib.exceptions( std::ios_base::goodbit ); lib.exceptions( std::ios_base::goodbit );
...@@ -3065,7 +3065,7 @@ void IDF3_BOARD::writeBoardFile( const std::string& aFileName ) ...@@ -3065,7 +3065,7 @@ void IDF3_BOARD::writeBoardFile( const std::string& aFileName )
} }
} }
catch( std::exception& e ) catch( const std::exception& e )
{ {
brd.exceptions( std::ios_base::goodbit ); brd.exceptions( std::ios_base::goodbit );
...@@ -3137,7 +3137,7 @@ bool IDF3_BOARD::WriteFile( const wxString& aFullFileName, bool aUnitMM, bool aF ...@@ -3137,7 +3137,7 @@ bool IDF3_BOARD::WriteFile( const wxString& aFullFileName, bool aUnitMM, bool aF
writeBoardFile( bfname ); writeBoardFile( bfname );
} }
catch( std::exception& e ) catch( const std::exception& e )
{ {
errormsg = e.what(); errormsg = e.what();
...@@ -3901,7 +3901,7 @@ IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( wxString aFullFileName ) ...@@ -3901,7 +3901,7 @@ IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( wxString aFullFileName )
} }
} // while( true ) } // while( true )
} }
catch( std::exception& e ) catch( const std::exception& e )
{ {
delete cp; delete cp;
......
...@@ -230,8 +230,7 @@ void VRML_LAYER::Clear( void ) ...@@ -230,8 +230,7 @@ void VRML_LAYER::Clear( void )
contours.pop_back(); contours.pop_back();
} }
while( !areas.empty() ) areas.clear();
areas.pop_back();
for( i = vertices.size(); i > 0; --i ) for( i = vertices.size(); i > 0; --i )
{ {
...@@ -254,8 +253,7 @@ void VRML_LAYER::clearTmp( void ) ...@@ -254,8 +253,7 @@ void VRML_LAYER::clearTmp( void )
ord = 0; ord = 0;
glcmd = 0; glcmd = 0;
while( !triplets.empty() ) triplets.clear();
triplets.pop_back();
for( i = outline.size(); i > 0; --i ) for( i = outline.size(); i > 0; --i )
{ {
...@@ -263,8 +261,7 @@ void VRML_LAYER::clearTmp( void ) ...@@ -263,8 +261,7 @@ void VRML_LAYER::clearTmp( void )
outline.pop_back(); outline.pop_back();
} }
for( i = ordmap.size(); i > 0; --i ) ordmap.clear();
ordmap.pop_back();
for( i = extra_verts.size(); i > 0; --i ) for( i = extra_verts.size(); i > 0; --i )
{ {
...@@ -274,8 +271,7 @@ void VRML_LAYER::clearTmp( void ) ...@@ -274,8 +271,7 @@ void VRML_LAYER::clearTmp( void )
// note: unlike outline and extra_verts, // note: unlike outline and extra_verts,
// vlist is not responsible for memory management // vlist is not responsible for memory management
for( i = vlist.size(); i > 0; --i ) vlist.clear();
vlist.pop_back();
// go through the vertex list and reset ephemeral parameters // go through the vertex list and reset ephemeral parameters
for( i = 0; i < vertices.size(); ++i ) for( i = 0; i < vertices.size(); ++i )
...@@ -743,6 +739,7 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes ) ...@@ -743,6 +739,7 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes )
// open the polygon // open the polygon
gluTessBeginPolygon( tess, this ); gluTessBeginPolygon( tess, this );
// add solid outlines
pushVertices( false ); pushVertices( false );
// close the polygon // close the polygon
...@@ -751,8 +748,10 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes ) ...@@ -751,8 +748,10 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes )
if( Fault ) if( Fault )
return false; return false;
// push the (solid) outline to the tesselator // at this point we have a solid outline; add it to the tesselator
if( !pushOutline( holes ) ) gluTessBeginPolygon( tess, this );
if( !pushOutline( NULL ) )
return false; return false;
// add the holes contained by this object // add the holes contained by this object
...@@ -772,14 +771,14 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes ) ...@@ -772,14 +771,14 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes )
// erase the previous outline data and vertex order // erase the previous outline data and vertex order
// but preserve the extra vertices // but preserve the extra vertices
for( int i = outline.size(); i > 0; --i ) while( !outline.empty() )
{ {
delete outline.back(); delete outline.back();
outline.pop_back(); outline.pop_back();
} }
for( unsigned int i = ordmap.size(); i > 0; --i ) ordmap.clear();
ordmap.pop_back(); ord = 0;
// go through the vertex lists and reset ephemeral parameters // go through the vertex lists and reset ephemeral parameters
for( unsigned int i = 0; i < vertices.size(); ++i ) for( unsigned int i = 0; i < vertices.size(); ++i )
...@@ -792,14 +791,16 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes ) ...@@ -792,14 +791,16 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes )
extra_verts[i]->o = -1; extra_verts[i]->o = -1;
} }
ord = 0; // close the polygon; this creates the outline points
// and the point ordering list 'ordmap'
// close the polygon; we now have all the data necessary for the tesselation
gluTessEndPolygon( tess ); gluTessEndPolygon( tess );
// request a tesselated surface // 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 ); gluTessProperty( tess, GLU_TESS_BOUNDARY_ONLY, GL_FALSE );
gluTessBeginPolygon( tess, this );
if( !pushOutline( holes ) ) if( !pushOutline( holes ) )
return false; return false;
...@@ -821,8 +822,6 @@ bool VRML_LAYER::pushOutline( VRML_LAYER* holes ) ...@@ -821,8 +822,6 @@ bool VRML_LAYER::pushOutline( VRML_LAYER* holes )
return false; return false;
} }
gluTessBeginPolygon( tess, this );
std::list<std::list<int>*>::const_iterator obeg = outline.begin(); std::list<std::list<int>*>::const_iterator obeg = outline.begin();
std::list<std::list<int>*>::const_iterator oend = outline.end(); std::list<std::list<int>*>::const_iterator oend = outline.end();
...@@ -851,6 +850,7 @@ bool VRML_LAYER::pushOutline( VRML_LAYER* holes ) ...@@ -851,6 +850,7 @@ bool VRML_LAYER::pushOutline( VRML_LAYER* holes )
if( pi < 0 || (unsigned int) pi > ordmap.size() ) if( pi < 0 || (unsigned int) pi > ordmap.size() )
{ {
gluTessEndContour( tess );
error = "pushOutline():BUG: *outline.begin() is not a valid index to ordmap"; error = "pushOutline():BUG: *outline.begin() is not a valid index to ordmap";
return false; return false;
} }
...@@ -862,6 +862,7 @@ bool VRML_LAYER::pushOutline( VRML_LAYER* holes ) ...@@ -862,6 +862,7 @@ bool VRML_LAYER::pushOutline( VRML_LAYER* holes )
if( !vp ) if( !vp )
{ {
gluTessEndContour( tess );
error = "pushOutline():: BUG: ordmap[n] is not a valid index to vertices[]"; error = "pushOutline():: BUG: ordmap[n] is not a valid index to vertices[]";
return false; return false;
} }
...@@ -1194,9 +1195,9 @@ bool VRML_LAYER::addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 ) ...@@ -1194,9 +1195,9 @@ bool VRML_LAYER::addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 )
double dy0 = p1->y - p0->y; double dy0 = p1->y - p0->y;
double dy1 = p2->y - p0->y; double dy1 = p2->y - p0->y;
// this number is chosen because we shall only write 6 decimal places // this number is chosen because we shall only write 9 decimal places
// on the VRML output // at most on the VRML output
double err = 0.000001; double err = 0.000000001;
// test if the triangles are degenerate (parallel sides) // test if the triangles are degenerate (parallel sides)
......
...@@ -96,6 +96,7 @@ private: ...@@ -96,6 +96,7 @@ private:
bool fix; // when true, no more vertices may be added by the user bool fix; // when true, no more vertices may be added by the user
int idx; // vertex index (number of contained vertices) int idx; // vertex index (number of contained vertices)
int ord; // vertex order (number of ordered vertices) int ord; // vertex order (number of ordered vertices)
unsigned int idxout; // outline index to first point in 3D outline
std::vector<VERTEX_3D*> vertices; // vertices of all contours std::vector<VERTEX_3D*> vertices; // vertices of all contours
std::vector<std::list<int>*> contours; // lists of vertices for each contour std::vector<std::list<int>*> contours; // lists of vertices for each contour
std::vector< double > areas; // area of the contours (positive if winding is CCW) std::vector< double > areas; // area of the contours (positive if winding is CCW)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment