From 53cd19a69b6343a953e3a707f4bf8fb34c762f08 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 8 Jun 2014 12:35:42 +0200 Subject: [PATCH] VRML export rewritten --- pcbnew/CMakeLists.txt | 1 - pcbnew/exporters/export_idf.cpp | 2 +- pcbnew/exporters/export_vrml.cpp | 512 +++++----- pcbnew/exporters/vrml_board.cpp | 1501 ------------------------------ pcbnew/exporters/vrml_board.h | 379 -------- utils/idftools/idf_outlines.cpp | 18 +- utils/idftools/idf_parser.cpp | 14 +- utils/idftools/vrml_layer.cpp | 45 +- utils/idftools/vrml_layer.h | 1 + 9 files changed, 329 insertions(+), 2144 deletions(-) delete mode 100644 pcbnew/exporters/vrml_board.cpp delete mode 100644 pcbnew/exporters/vrml_board.h diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 2b71524a1f..d01059707e 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -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 diff --git a/pcbnew/exporters/export_idf.cpp b/pcbnew/exporters/export_idf.cpp index a236d4a91a..64242e77d4 100644 --- a/pcbnew/exporters/export_idf.cpp +++ b/pcbnew/exporters/export_idf.cpp @@ -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() ); diff --git a/pcbnew/exporters/export_vrml.cpp b/pcbnew/exporters/export_vrml.cpp index 2522e813e2..5b8de962d1 100644 --- a/pcbnew/exporters/export_vrml.cpp +++ b/pcbnew/exporters/export_vrml.cpp @@ -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 @@ -70,6 +64,9 @@ #include #include <3d_struct.h> #include +#include +#include +#include #include @@ -85,7 +82,13 @@ #include #include -#include +#include + +// 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(); - - if( lines < 0 ) - return; - - 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 ); + int lines; lines = aTinLayer->NewContour(); if( lines < 0 ) - return; + 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->AddVertex( lines, coord[0], -coord[1] ) ) + throw( std::runtime_error( aTinLayer->GetError() ) ); + + if( !aTinLayer->AddVertex( lines, coord[4], -coord[5] ) ) + throw( std::runtime_error( aTinLayer->GetError() ) ); + + 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() ) ); + + 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; } diff --git a/pcbnew/exporters/vrml_board.cpp b/pcbnew/exporters/vrml_board.cpp deleted file mode 100644 index e8b13de36b..0000000000 --- a/pcbnew/exporters/vrml_board.cpp +++ /dev/null @@ -1,1501 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include - -#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* contour = new std::list; - - 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* 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*>::const_iterator obeg = outline.begin(); - std::list*>::const_iterator oend = outline.end(); - - int pi; - std::list::const_iterator begin; - std::list::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::const_iterator tbeg = triplets.begin(); - std::list::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::const_iterator tbeg = triplets.begin(); - std::list::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*>::const_iterator obeg = outline.begin(); - std::list*>::const_iterator oend = outline.end(); - std::list* cp; - std::list::const_iterator cbeg; - std::list::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* loop = new std::list; - - 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::const_iterator begin; - std::list::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::const_iterator cbeg; - std::list::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; -} diff --git a/pcbnew/exporters/vrml_board.h b/pcbnew/exporters/vrml_board.h deleted file mode 100644 index 9cb4a26f4f..0000000000 --- a/pcbnew/exporters/vrml_board.h +++ /dev/null @@ -1,379 +0,0 @@ -/* - * 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 -# else -# include -# endif -#else -# include -#endif - -#include -#include -#include -#include - -#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 vertices; // vertices of all contours - std::vector*> contours; // lists of vertices for each contour - std::vector< double > areas; // area of the contours (positive if winding is CCW) - std::list triplets; // output facet triplet list (triplet of ORDER values) - std::list*> outline; // indices for outline outputs (index by ORDER values) - std::vector 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 extra_verts; // extra vertices added for outlines and facets - std::vector 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 diff --git a/utils/idftools/idf_outlines.cpp b/utils/idftools/idf_outlines.cpp index 43d1a4cbb2..456b427c27 100644 --- a/utils/idftools/idf_outlines.cpp +++ b/utils/idftools/idf_outlines.cpp @@ -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(); diff --git a/utils/idftools/idf_parser.cpp b/utils/idftools/idf_parser.cpp index a1ac7da92c..afb2320754 100644 --- a/utils/idftools/idf_parser.cpp +++ b/utils/idftools/idf_parser.cpp @@ -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; diff --git a/utils/idftools/vrml_layer.cpp b/utils/idftools/vrml_layer.cpp index 7cb03dd54a..32ed105210 100644 --- a/utils/idftools/vrml_layer.cpp +++ b/utils/idftools/vrml_layer.cpp @@ -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*>::const_iterator obeg = outline.begin(); std::list*>::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) diff --git a/utils/idftools/vrml_layer.h b/utils/idftools/vrml_layer.h index 5f7f456a59..5316b62506 100644 --- a/utils/idftools/vrml_layer.h +++ b/utils/idftools/vrml_layer.h @@ -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 vertices; // vertices of all contours std::vector*> contours; // lists of vertices for each contour std::vector< double > areas; // area of the contours (positive if winding is CCW)