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
exporters/gen_drill_report_files.cpp
exporters/gen_modules_placefile.cpp
exporters/gendrill_Excellon_writer.cpp
exporters/vrml_board.cpp
)
set( PCBNEW_AUTOROUTER_SRCS
......
......@@ -568,7 +568,7 @@ bool Export_IDF3( BOARD* aPcb, const wxString& aFullFileName, bool aUseThou )
ok = false;
}
catch( std::exception& e )
catch( const std::exception& e )
{
wxString msg;
msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( e.what() );
......
......@@ -24,6 +24,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* NOTE:
* 1. for improved looks, create a DRILL layer for PTH drills.
......@@ -42,24 +43,17 @@
*
* 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
*
* For mechanical correctness, we should use the following settings with arcs:
* 1. max. deviation: the number of edges should be determined by the max.
* mechanical deviation and the minimum number of edges shall be 6.
* 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.
*/
/*
* KNOWN BUGS:
* 1. silk outlines are sometimes mangled; this is somehow due to
* many overlapping segments. This does not happen in 3Dviewer
* so it is worth inspecting that code to see what is different.
*
* 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>
......@@ -70,6 +64,9 @@
#include <pgm_base.h>
#include <3d_struct.h>
#include <macros.h>
#include <exception>
#include <fstream>
#include <iomanip>
#include <pcbnew.h>
......@@ -85,7 +82,13 @@
#include <vector>
#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:
* some characters cannot be used in names,
......@@ -167,6 +170,9 @@ private:
double layer_z[NB_LAYERS];
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:
VRML_LAYER holes;
......@@ -179,6 +185,8 @@ public:
VRML_LAYER bot_tin;
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 ty; // global translation along Y
......@@ -193,6 +201,8 @@ public:
for( int i = 0; i < NB_LAYERS; ++i )
layer_z[i] = 0;
holes.GetArcParams( iMaxSeg, arcMinLen, arcMaxLen );
// this default only makes sense if the output is in mm
board_thickness = 1.6;
......@@ -208,6 +218,8 @@ public:
// pad silver
colors[ VRML_COLOR_TIN ] = VRML_COLOR( .749, .756, .761, .749, .756, .761,
0, 0, 0, 0.8, 0, 0.8 );
precision = 5;
}
VRML_COLOR& GetColor( VRML_COLOR_INDEX aIndex )
......@@ -234,31 +246,51 @@ public:
layer_z[aLayer] = aValue;
}
void SetMaxDev( double dev )
// set the scaling of the VRML world
bool SetScale( double aWorldScale )
{
holes.SetMaxDev( dev );
board.SetMaxDev( dev );
top_copper.SetMaxDev( dev );
bot_copper.SetMaxDev( dev );
top_silk.SetMaxDev( dev );
bot_silk.SetMaxDev( dev );
top_tin.SetMaxDev( dev );
bot_tin.SetMaxDev( dev );
if( aWorldScale < 0.001 || aWorldScale > 10.0 )
throw( std::runtime_error( "WorldScale out of range (valid range is 0.001 to 10.0)" ) );
scale = aWorldScale * MM_PER_IU;
minLineWidth = aWorldScale * MIN_VRML_LINEWIDTH;
// set the precision of the VRML coordinates
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
namespace VRMLEXPORT
{
static MODEL_VRML* model_vrml;
bool GetLayer( MODEL_VRML& aModel, LAYER_NUM layer, VRML_LAYER** vlayer );
}
static MODEL_VRML* model_vrml;
// select the VRML layer object to draw on; return true if
// 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 )
{
......@@ -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,
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
* without them */
......@@ -328,7 +360,7 @@ static void write_triangle_bag( FILE* output_file, VRML_COLOR& color,
while( marker_found < 4 )
{
if( shape_boiler[lineno] )
fputs( shape_boiler[lineno], output_file );
output_file << shape_boiler[lineno];
else
{
marker_found++;
......@@ -336,37 +368,34 @@ static void write_triangle_bag( FILE* output_file, VRML_COLOR& color,
switch( marker_found )
{
case 1: // Material marker
fprintf( output_file,
" diffuseColor %g %g %g\n",
color.diffuse_red,
color.diffuse_grn,
color.diffuse_blu );
fprintf( output_file,
" specularColor %g %g %g\n",
color.spec_red,
color.spec_grn,
color.spec_blu );
fprintf( output_file,
" emissiveColor %g %g %g\n",
color.emit_red,
color.emit_grn,
color.emit_blu );
fprintf( output_file,
" ambientIntensity %g\n", color.ambient );
fprintf( output_file,
" transparency %g\n", color.transp );
fprintf( output_file,
" shininess %g\n", color.shiny );
output_file << " diffuseColor " << std::setprecision(3);
output_file << color.diffuse_red << " ";
output_file << color.diffuse_grn << " ";
output_file << color.diffuse_blu << "\n";
output_file << " specularColor ";
output_file << color.spec_red << " ";
output_file << color.spec_grn << " ";
output_file << color.spec_blu << "\n";
output_file << " emissiveColor ";
output_file << color.emit_red << " ";
output_file << color.emit_grn << " ";
output_file << color.emit_blu << "\n";
output_file << " ambientIntensity " << color.ambient << "\n";
output_file << " transparency " << color.transp << "\n";
output_file << " shininess " << color.shiny << "\n";
break;
case 2:
if( plane )
layer->WriteVertices( top_z, output_file );
layer->WriteVertices( top_z, output_file, aPrecision );
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;
case 3:
......@@ -376,7 +405,7 @@ static void write_triangle_bag( FILE* output_file, VRML_COLOR& color,
else
layer->Write3DIndices( output_file );
fprintf( output_file, "\n" );
output_file << "\n";
break;
default:
......@@ -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;
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 ),
&aModel.board, false, false, brdz, -brdz );
&aModel.board, false, false, brdz, -brdz, aModel.precision );
// VRML_LAYER top_copper;
aModel.top_copper.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TRACK ),
&aModel.top_copper, true, true,
aModel.GetLayerZ( LAST_COPPER_LAYER ), 0 );
aModel.GetLayerZ( LAST_COPPER_LAYER ), 0, aModel.precision );
// VRML_LAYER top_tin;
aModel.top_tin.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TIN ),
&aModel.top_tin, true, true,
aModel.GetLayerZ( LAST_COPPER_LAYER ), 0 );
&aModel.top_tin, true, true,
aModel.GetLayerZ( LAST_COPPER_LAYER )
+ Millimeter2iu( ART_OFFSET / 2.0 ) * aModel.scale,
0, aModel.precision );
// VRML_LAYER bot_copper;
aModel.bot_copper.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TRACK ),
&aModel.bot_copper, true, false,
aModel.GetLayerZ( FIRST_COPPER_LAYER ), 0 );
aModel.GetLayerZ( FIRST_COPPER_LAYER ), 0, aModel.precision );
// VRML_LAYER bot_tin;
aModel.bot_tin.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_TIN ),
&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;
aModel.top_silk.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_SILK ),
&aModel.top_silk, true, true,
aModel.GetLayerZ( SILKSCREEN_N_FRONT ), 0 );
aModel.GetLayerZ( SILKSCREEN_N_FRONT ), 0, aModel.precision );
// VRML_LAYER bot_silk;
aModel.bot_silk.Tesselate( &aModel.holes );
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_SILK ),
&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 )
/* To avoid rounding interference, we apply an epsilon to each
* 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( ADHESIVE_N_BACK, -half_thickness - epsilon_z * 3 );
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,
{
VRML_LAYER* vlayer;
if( !VRMLEXPORT::GetLayer( aModel, layer, &vlayer ) )
if( !GetLayer( aModel, layer, &vlayer ) )
return;
if( width < aModel.minLineWidth)
width = aModel.minLineWidth;
starty = -starty;
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 cx = ( startx + endx ) / 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,
{
VRML_LAYER* vlayer;
if( !VRMLEXPORT::GetLayer( aModel, layer, &vlayer ) )
if( !GetLayer( aModel, layer, &vlayer ) )
return;
if( width < aModel.minLineWidth )
width = aModel.minLineWidth;
starty = -starty;
endy = -endy;
......@@ -509,11 +550,13 @@ static void export_vrml_circle( MODEL_VRML& aModel, LAYER_NUM layer,
radius = Distance( startx, starty, endx, endy ) + ( width / 2);
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 )
{
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,
{
VRML_LAYER* vlayer;
if( !VRMLEXPORT::GetLayer( aModel, layer, &vlayer ) )
if( !GetLayer( aModel, layer, &vlayer ) )
return;
if( width < aModel.minLineWidth )
width = aModel.minLineWidth;
centery = -centery;
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 )
* for coupling the vrml_text_callback with the common parameters */
static void vrml_text_callback( int x0, int y0, int xf, int yf )
{
LAYER_NUM s_text_layer = VRMLEXPORT::model_vrml->s_text_layer;
int s_text_width = VRMLEXPORT::model_vrml->s_text_width;
double scale = VRMLEXPORT::model_vrml->scale;
double tx = VRMLEXPORT::model_vrml->tx;
double ty = VRMLEXPORT::model_vrml->ty;
LAYER_NUM s_text_layer = model_vrml->s_text_layer;
int s_text_width = model_vrml->s_text_width;
double scale = model_vrml->scale;
double tx = model_vrml->tx;
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,
xf * scale + tx, yf * scale + ty,
s_text_width * scale );
......@@ -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 )
{
VRMLEXPORT::model_vrml->s_text_layer = text->GetLayer();
VRMLEXPORT::model_vrml->s_text_width = text->GetThickness();
model_vrml->s_text_layer = text->GetLayer();
model_vrml->s_text_width = text->GetThickness();
wxSize size = text->GetSize();
......@@ -707,12 +753,12 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
while( i < nvert )
{
aModel.board.AddVertex( seg, bufferPcbOutlines[i].x * scale + dx,
-(bufferPcbOutlines[i].y * scale + dy) );
if( bufferPcbOutlines[i].end_contour )
break;
aModel.board.AddVertex( seg, bufferPcbOutlines[i].x * scale + dx,
-(bufferPcbOutlines[i].y * scale + dy) );
++i;
}
......@@ -739,12 +785,12 @@ static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
while( i < nvert )
{
aModel.holes.AddVertex( seg, allLayerHoles[i].x * scale + dx,
-(allLayerHoles[i].y * scale + dy) );
if( allLayerHoles[i].end_contour )
break;
aModel.holes.AddVertex( seg, allLayerHoles[i].x * scale + dx,
-(allLayerHoles[i].y * scale + dy) );
++i;
}
......@@ -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 )
thru = false;
if( thru && hole > 0 )
aModel.holes.AddCircle( x, -y, hole, true );
while( 1 )
{
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 )
{
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 )
......@@ -856,7 +897,7 @@ static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb )
VRML_LAYER* vl;
if( !VRMLEXPORT::GetLayer( aModel, zone->GetLayer(), &vl ) )
if( !GetLayer( aModel, zone->GetLayer(), &vl ) )
continue;
if( !zone->IsFilled() )
......@@ -881,11 +922,13 @@ static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb )
{
x = poly.GetX(i) * scale + dx;
y = -(poly.GetY(i) * scale + dy);
vl->AddVertex( seg, x, y );
if( poly.IsEndContour(i) )
break;
if( !vl->AddVertex( seg, x, y ) )
throw( std::runtime_error( vl->GetError() ) );
++i;
}
......@@ -911,8 +954,8 @@ static void export_vrml_text_module( TEXTE_MODULE* module )
if( module->IsMirrored() )
NEGATE( size.x ); // Text is mirrored
VRMLEXPORT::model_vrml->s_text_layer = module->GetLayer();
VRMLEXPORT::model_vrml->s_text_width = module->GetThickness();
model_vrml->s_text_layer = module->GetLayer();
model_vrml->s_text_width = module->GetThickness();
DrawGraphicText( NULL, NULL, module->GetTextPosition(), BLACK,
module->GetText(), module->GetDrawRotation(), size,
......@@ -952,10 +995,10 @@ static void export_vrml_edge_module( MODEL_VRML& aModel, EDGE_MODULE* aOutline,
{
VRML_LAYER* vl;
if( !VRMLEXPORT::GetLayer( aModel, layer, &vl ) )
if( !GetLayer( aModel, layer, &vl ) )
break;
int nvert = aOutline->GetPolyPoints().size();
int nvert = aOutline->GetPolyPoints().size() - 1;
int i = 0;
if( nvert < 3 ) break;
......@@ -974,7 +1017,9 @@ static void export_vrml_edge_module( MODEL_VRML& aModel, EDGE_MODULE* aOutline,
x = corner.x * aModel.scale + aModel.tx;
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;
}
......@@ -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,
VRML_LAYER* aTinLayer, D_PAD* aPad )
static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aTinLayer, D_PAD* aPad )
{
// The (maybe offset) pad position
wxPoint pad_pos = aPad->ShapePos();
......@@ -1006,15 +1050,18 @@ static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aLayer,
switch( aPad->GetShape() )
{
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;
case PAD_OVAL:
aLayer->AddSlot( pad_x, -pad_y, pad_w * 2.0, pad_h * 2.0,
DECIDEG2RAD( aPad->GetOrientation() ), 1, true );
aTinLayer->AddSlot( pad_x, -pad_y, pad_w * 2.0, pad_h * 2.0,
DECIDEG2RAD( aPad->GetOrientation() ), 1, false );
if( !aTinLayer->AddSlot( pad_x, -pad_y, pad_w * 2.0, pad_h * 2.0,
aPad->GetOrientation()/10.0, false ) )
throw( std::runtime_error( aTinLayer->GetError() ) );
break;
case PAD_RECT:
......@@ -1039,32 +1086,32 @@ static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aLayer,
coord[i * 2 + 1] += pad_y;
}
int lines = aLayer->NewContour();
int lines;
lines = aTinLayer->NewContour();
if( lines < 0 )
return;
throw( std::runtime_error( aTinLayer->GetError() ) );
aLayer->AddVertex( lines, coord[2], -coord[3] );
aLayer->AddVertex( lines, coord[6], -coord[7] );
aLayer->AddVertex( lines, coord[4], -coord[5] );
aLayer->AddVertex( lines, coord[0], -coord[1] );
aLayer->EnsureWinding( lines, true );
if( !aTinLayer->AddVertex( lines, coord[0], -coord[1] ) )
throw( std::runtime_error( aTinLayer->GetError() ) );
lines = aTinLayer->NewContour();
if( !aTinLayer->AddVertex( lines, coord[4], -coord[5] ) )
throw( std::runtime_error( aTinLayer->GetError() ) );
if( lines < 0 )
return;
if( !aTinLayer->AddVertex( lines, coord[6], -coord[7] ) )
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] );
aTinLayer->AddVertex( lines, coord[4], -coord[5] );
aTinLayer->AddVertex( lines, coord[6], -coord[7] );
aTinLayer->AddVertex( lines, coord[2], -coord[3] );
aTinLayer->EnsureWinding( lines, false );
if( !aTinLayer->EnsureWinding( lines, false ) )
throw( std::runtime_error( aTinLayer->GetError() ) );
}
break;
default:
;
break;
}
}
......@@ -1084,12 +1131,12 @@ static void export_vrml_pad( MODEL_VRML& aModel, BOARD* pcb, D_PAD* aPad )
{
// Oblong hole (slot)
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
{
// 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 )
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 )
{
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] )
static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule,
FILE* aOutputFile,
std::ofstream& aOutputFile,
double aVRMLModelsToBiu,
bool aExport3DFiles, const wxString& a3D_Subdir )
{
......@@ -1237,12 +1284,13 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule
compose_quat( q1, q2, q1 );
from_quat( q1, rot );
fprintf( aOutputFile, "Transform {\n" );
aOutputFile << "Transform {\n";
// A null rotation would fail the acos!
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
......@@ -1258,15 +1306,15 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule
RotatePoint( &offsetx, &offsety, aModule->GetOrientation() );
fprintf( aOutputFile, " translation %g %g %g\n",
(offsetx + aModule->GetPosition().x) * aModel.scale + aModel.tx,
-(offsety + aModule->GetPosition().y) * aModel.scale - aModel.ty,
(offsetz * aModel.scale ) + aModel.GetLayerZ( aModule->GetLayer() ) );
aOutputFile << " translation " << std::setprecision( aModel.precision );
aOutputFile << (( offsetx + aModule->GetPosition().x) * aModel.scale + aModel.tx ) << " ";
aOutputFile << ( -(offsety + aModule->GetPosition().y) * aModel.scale - aModel.ty ) << " ";
aOutputFile << ( (offsetz * aModel.scale ) + aModel.GetLayerZ( aModule->GetLayer() ) ) << "\n";
fprintf( aOutputFile, " scale %g %g %g\n",
vrmlm->m_MatScale.x * aVRMLModelsToBiu,
vrmlm->m_MatScale.y * aVRMLModelsToBiu,
vrmlm->m_MatScale.z * aVRMLModelsToBiu );
aOutputFile << " scale ";
aOutputFile << ( vrmlm->m_MatScale.x * aVRMLModelsToBiu ) << " ";
aOutputFile << ( vrmlm->m_MatScale.y * aVRMLModelsToBiu ) << " ";
aOutputFile << ( vrmlm->m_MatScale.z * aVRMLModelsToBiu ) << "\n";
if( fname.EndsWith( wxT( "x3d" ) ) )
{
......@@ -1276,18 +1324,25 @@ static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule
{
// embed x3d model in vrml format
parser->Load( fname );
fprintf( aOutputFile,
" children [\n %s ]\n", TO_UTF8( parser->VRML_representation() ) );
fprintf( aOutputFile, " }\n" );
delete parser;
try
{
aOutputFile << " children [\n ";
aOutputFile << TO_UTF8( parser->VRML_representation() ) << " ]\n";
aOutputFile << " }\n";
}
catch( const std::exception& e )
{
delete parser;
throw;
}
}
}
else
{
fprintf( aOutputFile,
" children [\n Inline {\n url \"%s\"\n } ]\n",
TO_UTF8( fname ) );
fprintf( aOutputFile, " }\n" );
aOutputFile << " children [\n Inline {\n url \"";
aOutputFile << TO_UTF8( fname ) << "\"\n } ]\n";
aOutputFile << " }\n";
}
}
}
......@@ -1297,89 +1352,98 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName,
double aMMtoWRMLunit, bool aExport3DFiles,
const wxString& a3D_Subdir )
{
wxString msg;
FILE* output_file;
BOARD* pcb = GetBoard();
wxString msg;
BOARD* pcb = GetBoard();
bool ok = true;
MODEL_VRML model3d;
VRMLEXPORT::model_vrml = &model3d;
model_vrml = &model3d;
std::ofstream output_file;
output_file = wxFopen( aFullFileName, wxT( "wt" ) );
try
{
output_file.exceptions( std::ofstream::failbit );
output_file.open( TO_UTF8( aFullFileName ), std::ios_base::out );
if( output_file == NULL )
return false;
// Switch the locale to standard C (needed to print floating point numbers like 1.3)
SetLocaleTo_C_standard();
// Switch the locale to standard C (needed to print floating point numbers like 1.3)
SetLocaleTo_C_standard();
// Begin with the usual VRML boilerplate
wxString name = aFullFileName;
// Begin with the usual VRML boilerplate
wxString name = aFullFileName;
name.Replace( wxT( "\\" ), wxT( "/" ) );
ChangeIllegalCharacters( name, false );
name.Replace( wxT( "\\" ), wxT( "/" ) );
ChangeIllegalCharacters( name, false );
fprintf( output_file, "#VRML V2.0 utf8\n"
"WorldInfo {\n"
" title \"%s - Generated by Pcbnew\"\n"
"}\n", TO_UTF8( name ) );
output_file << "#VRML V2.0 utf8\n";
output_file << "WorldInfo {\n";
output_file << " title \"" << TO_UTF8( name ) << " - Generated by Pcbnew\"\n";
output_file << "}\n";
// Global VRML scale to export to a different scale.
model3d.scale = aMMtoWRMLunit / MM_PER_IU;
// Set the VRML world scale factor
model3d.SetScale( aMMtoWRMLunit );
// Set the mechanical deviation limit (in this case 0.02mm)
// XXX - NOTE: the value should be set via the GUI
model3d.SetMaxDev( 20000 * model3d.scale );
output_file << "Transform {\n";
fprintf( output_file, "Transform {\n" );
// 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
EDA_RECT bbbox = pcb->ComputeBoundingBox();
// 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
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 );
output_file << " children [\n";
fprintf( output_file, " children [\n" );
// Preliminary computation: the z value for each layer
compute_layer_Zs( model3d, pcb );
// Preliminary computation: the z value for each layer
compute_layer_Zs( model3d, pcb );
// board edges and cutouts
export_vrml_board( model3d, pcb );
// board edges and cutouts
export_vrml_board( model3d, pcb );
// Drawing and text on the board
export_vrml_drawings( model3d, pcb );
// Drawing and text on the board
export_vrml_drawings( model3d, pcb );
// Export vias and trackage
export_vrml_tracks( model3d, pcb );
// Export vias and trackage
export_vrml_tracks( model3d, pcb );
// Export zone fills
export_vrml_zones( model3d, pcb);
// Export zone fills
export_vrml_zones( model3d, pcb);
/* scaling factor to convert 3D models to board units (decimils)
* Usually we use Wings3D to create thems.
* One can consider the 3D units is 0.1 inch (2.54 mm)
* So the scaling factor from 0.1 inch to board units
* is 2.54 * aMMtoWRMLunit
*/
double wrml_3D_models_scaling_factor = 2.54 * aMMtoWRMLunit;
/* scaling factor to convert 3D models to board units (decimils)
* Usually we use Wings3D to create thems.
* One can consider the 3D units is 0.1 inch (2.54 mm)
* So the scaling factor from 0.1 inch to board units
* is 2.54 * aMMtoWRMLunit
*/
double wrml_3D_models_scaling_factor = 2.54 * aMMtoWRMLunit;
// Export footprints
for( MODULE* module = pcb->m_Modules; module != 0; module = module->Next() )
export_vrml_module( model3d, pcb, module, output_file,
wrml_3D_models_scaling_factor,
aExport3DFiles, a3D_Subdir );
// Export footprints
for( MODULE* module = pcb->m_Modules; module != 0; module = module->Next() )
export_vrml_module( model3d, pcb, module, output_file,
wrml_3D_models_scaling_factor,
aExport3DFiles, a3D_Subdir );
// write out the board and all layers
write_layers( model3d, output_file, pcb );
// write out the board and all layers
write_layers( model3d, output_file, pcb );
// Close the outer 'transform' node
output_file << "]\n}\n";
}
catch( const std::exception& e )
{
wxString msg;
msg << _( "IDF Export Failed:\n" ) << FROM_UTF8( e.what() );
wxMessageBox( msg );
// Close the outer 'transform' node
fputs( "]\n}\n", output_file );
ok = false;
}
// End of work
fclose( output_file );
output_file.exceptions( std::ios_base::goodbit );
output_file.close();
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
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< aOutline->front()->startPoint.x << " "
<< aOutline->front()->startPoint.y << " "
<< setprecision(5) << -aOutline->front()->angle << "\n";
<< setprecision(2) << -aOutline->front()->angle << "\n";
}
}
else
......@@ -799,7 +799,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< (aOutline->front()->startPoint.x / 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
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->startPoint.x << " "
<< (*bo)->startPoint.y << " "
<< setprecision(5) << -(*bo)->angle << "\n";
<< setprecision(2) << -(*bo)->angle << "\n";
}
}
else
......@@ -835,7 +835,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->startPoint.x / 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
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->endPoint.x << " "
<< (*bo)->endPoint.y << " "
<< setprecision(5) << (*bo)->angle << "\n";
<< setprecision(2) << (*bo)->angle << "\n";
}
}
else
......@@ -893,7 +893,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->endPoint.x / 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
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(5)
<< (*bo)->endPoint.x << " "
<< (*bo)->endPoint.y << " "
<< setprecision(5) << (*bo)->angle << "\n";
<< setprecision(2) << (*bo)->angle << "\n";
}
}
else
......@@ -931,7 +931,7 @@ void BOARD_OUTLINE::writeOutline( std::ofstream& aBoardFile, IDF_OUTLINE* aOutli
aBoardFile << aIndex << " " << setiosflags(ios::fixed) << setprecision(1)
<< ((*bo)->endPoint.x / 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 )
outlines.push_back( aOutline );
}
catch( std::exception& e )
catch( const std::exception& e )
{
errormsg = e.what();
......
......@@ -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 );
......@@ -2689,7 +2689,7 @@ void IDF3_BOARD::readLibFile( const std::string& aFileName )
while( lib.good() ) readLibSection( lib, state, this );
}
catch( std::exception& e )
catch( const std::exception& e )
{
lib.exceptions ( std::ios_base::goodbit );
......@@ -2773,7 +2773,7 @@ bool IDF3_BOARD::ReadFile( const wxString& aFullFileName, bool aNoSubstituteOutl
// read the board file
readBoardFile( bfname, aNoSubstituteOutlines );
}
catch( std::exception& e )
catch( const std::exception& e )
{
Clear();
errormsg = e.what();
......@@ -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 );
......@@ -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 );
......@@ -3137,7 +3137,7 @@ bool IDF3_BOARD::WriteFile( const wxString& aFullFileName, bool aUnitMM, bool aF
writeBoardFile( bfname );
}
catch( std::exception& e )
catch( const std::exception& e )
{
errormsg = e.what();
......@@ -3901,7 +3901,7 @@ IDF3_COMP_OUTLINE* IDF3_BOARD::GetComponentOutline( wxString aFullFileName )
}
} // while( true )
}
catch( std::exception& e )
catch( const std::exception& e )
{
delete cp;
......
......@@ -230,8 +230,7 @@ void VRML_LAYER::Clear( void )
contours.pop_back();
}
while( !areas.empty() )
areas.pop_back();
areas.clear();
for( i = vertices.size(); i > 0; --i )
{
......@@ -254,8 +253,7 @@ void VRML_LAYER::clearTmp( void )
ord = 0;
glcmd = 0;
while( !triplets.empty() )
triplets.pop_back();
triplets.clear();
for( i = outline.size(); i > 0; --i )
{
......@@ -263,8 +261,7 @@ void VRML_LAYER::clearTmp( void )
outline.pop_back();
}
for( i = ordmap.size(); i > 0; --i )
ordmap.pop_back();
ordmap.clear();
for( i = extra_verts.size(); i > 0; --i )
{
......@@ -274,8 +271,7 @@ void VRML_LAYER::clearTmp( void )
// note: unlike outline and extra_verts,
// vlist is not responsible for memory management
for( i = vlist.size(); i > 0; --i )
vlist.pop_back();
vlist.clear();
// go through the vertex list and reset ephemeral parameters
for( i = 0; i < vertices.size(); ++i )
......@@ -743,6 +739,7 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes )
// open the polygon
gluTessBeginPolygon( tess, this );
// add solid outlines
pushVertices( false );
// close the polygon
......@@ -751,8 +748,10 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes )
if( Fault )
return false;
// push the (solid) outline to the tesselator
if( !pushOutline( holes ) )
// 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
......@@ -772,14 +771,14 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes )
// erase the previous outline data and vertex order
// but preserve the extra vertices
for( int i = outline.size(); i > 0; --i )
while( !outline.empty() )
{
delete outline.back();
outline.pop_back();
}
for( unsigned int i = ordmap.size(); i > 0; --i )
ordmap.pop_back();
ordmap.clear();
ord = 0;
// go through the vertex lists and reset ephemeral parameters
for( unsigned int i = 0; i < vertices.size(); ++i )
......@@ -792,14 +791,16 @@ bool VRML_LAYER::Tesselate( VRML_LAYER* holes )
extra_verts[i]->o = -1;
}
ord = 0;
// close the polygon; we now have all the data necessary for the tesselation
// close the polygon; this creates the outline points
// and the point ordering list 'ordmap'
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 );
gluTessBeginPolygon( tess, this );
if( !pushOutline( holes ) )
return false;
......@@ -821,8 +822,6 @@ bool VRML_LAYER::pushOutline( VRML_LAYER* holes )
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();
......@@ -851,6 +850,7 @@ bool VRML_LAYER::pushOutline( VRML_LAYER* holes )
if( pi < 0 || (unsigned int) pi > ordmap.size() )
{
gluTessEndContour( tess );
error = "pushOutline():BUG: *outline.begin() is not a valid index to ordmap";
return false;
}
......@@ -862,6 +862,7 @@ bool VRML_LAYER::pushOutline( VRML_LAYER* holes )
if( !vp )
{
gluTessEndContour( tess );
error = "pushOutline():: BUG: ordmap[n] is not a valid index to vertices[]";
return false;
}
......@@ -1194,9 +1195,9 @@ bool VRML_LAYER::addTriplet( VERTEX_3D* p0, VERTEX_3D* p1, VERTEX_3D* p2 )
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;
// this number is chosen because we shall only write 9 decimal places
// at most on the VRML output
double err = 0.000000001;
// test if the triangles are degenerate (parallel sides)
......
......@@ -96,6 +96,7 @@ 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)
unsigned int idxout; // outline index to first point in 3D outline
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)
......
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