Commit b02f12f4 authored by Cirilo Bernardo's avatar Cirilo Bernardo Committed by jean-pierre charras

Commit patch about new better vrml export. patch to fix mirrored page layout...

Commit patch about new better vrml export. patch to fix mirrored page layout when print mirrored is enabled ( page layout should not be mirrored)
parents 290a60a4 714d5b28
......@@ -94,10 +94,24 @@ void EDA_DRAW_FRAME::DrawWorkSheet( wxDC* aDC, BASE_SCREEN* aScreen, int aLineWi
TITLE_BLOCK t_block = GetTitleBlock();
EDA_COLOR_T color = RED;
wxPoint origin = aDC->GetDeviceOrigin();
if( aScreen->m_IsPrinting && origin.y > 0 )
{
aDC->SetDeviceOrigin( 0, 0 );
aDC->SetAxisOrientation( true, false );
}
DrawPageLayout( aDC, m_canvas->GetClipBox(), pageInfo,
GetScreenDesc(), aFilename, t_block,
aScreen->m_NumberOfScreens, aScreen->m_ScreenNumber,
aLineWidth, aScalar, color, color );
if( aScreen->m_IsPrinting && origin.y > 0 )
{
aDC->SetDeviceOrigin( origin.x, origin.y );
aDC->SetAxisOrientation( true, true );
}
}
......
......@@ -177,6 +177,7 @@ set( PCBNEW_CLASS_SRCS
export_gencad.cpp
export_idf.cpp
export_vrml.cpp
vrml_board.cpp
files.cpp
gen_drill_report_files.cpp
gen_modules_placefile.cpp
......
......@@ -2,6 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2009-2013 Lorenzo Mercantonio
* Copyright (C) 2014 Cirilo Bernado
* Copyright (C) 2013 Jean-Pierre Charras jp.charras at wanadoo.fr
* Copyright (C) 2004-2013 KiCad Developers, see change_log.txt for contributors.
*
......@@ -22,6 +23,44 @@
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
* NOTE:
* 1. for improved looks, create a DRILL layer for PTH drills.
* To render the improved board, render the vertical outline only
* for the board (no added drill holes), then render the
* outline only for PTH, and finally render the top and bottom
* of the board. NOTE: if we don't want extra eye-candy then
* we must maintain the current board export.
* Additional bits needed for improved eyecandy:
* + CalcOutline: calculates only the outline of a VRML_LAYER or
* a VERTICAL_HOLES
* + WriteVerticalIndices: writes the indices of only the vertical
* facets of a VRML_LAYER or a VRML_HOLES.
* + WriteVerticalVertices: writes only the outline vertices to
* form vertical walls; applies to VRML_LAYER and VRML_HOLES
*
* 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.
*
* 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.
*
*/
#include <fctsys.h>
#include <kicad_string.h>
#include <wxPcbStruct.h>
......@@ -36,6 +75,7 @@
#include <class_board.h>
#include <class_module.h>
#include <class_track.h>
#include <class_zone.h>
#include <class_edge_mod.h>
#include <class_pcb_text.h>
#include <convert_from_iu.h>
......@@ -44,183 +84,210 @@
#include <vector>
#include <cmath>
// Number of segments to approximate a circle per segments:
#define SEGM_COUNT_PER_360 32
// basic angle to approximate a circle per segments
static const double INC_ANGLE = M_PI*2 / SEGM_COUNT_PER_360;
#include <vrml_board.h>
/* helper function:
* some characters cannot be used in names,
* this function change them to "_"
*/
static void ChangeIllegalCharacters( wxString & aFileName, bool aDirSepIsIllegal );
static void ChangeIllegalCharacters( wxString& aFileName, bool aDirSepIsIllegal );
// I use this a lot...
static const double PI2 = M_PI / 2;
struct POINT_3D
struct VRML_COLOR
{
double x, y, z;
};
float diffuse_red;
float diffuse_grn;
float diffuse_blu;
struct POINT_2D
{
POINT_2D( double _x = 0, double _y = 0 ) : x( _x ), y( _y )
{ }
double x, y;
};
float spec_red;
float spec_grn;
float spec_blu;
// Absolutely not optimized triangle bag :D
struct TRIANGLE
{
TRIANGLE( double x1, double y1, double z1,
double x2, double y2, double z2,
double x3, double y3, double z3 )
{
p1.x = x1; p1.y = y1; p1.z = z1;
p2.x = x2; p2.y = y2; p2.z = z2;
p3.x = x3; p3.y = y3; p3.z = z3;
}
TRIANGLE() { }
POINT_3D p1, p2, p3;
};
typedef std::vector<TRIANGLE> TRIANGLEBAG;
float emit_red;
float emit_grn;
float emit_blu;
// A flat triangle fan
struct FLAT_FAN
{
POINT_2D c;
std::vector<POINT_2D> pts;
void add( double x, double y )
{
pts.push_back( POINT_2D( x, y ) );
}
void bag( LAYER_NUM layer, bool close = true );
};
float ambient;
float transp;
float shiny;
// A flat quad ring
struct FLAT_RING
{
std::vector<POINT_2D> inner;
std::vector<POINT_2D> outer;
void add_inner( double x, double y )
VRML_COLOR()
{
inner.push_back( POINT_2D( x, y ) );
// default green
diffuse_red = 0.13;
diffuse_grn = 0.81;
diffuse_blu = 0.22;
spec_red = 0.13;
spec_grn = 0.81;
spec_blu = 0.22;
emit_red = 0.0;
emit_grn = 0.0;
emit_blu = 0.0;
ambient = 1.0;
transp = 0;
shiny = 0.2;
}
void add_outer( double x, double y )
VRML_COLOR( float dr, float dg, float db,
float sr, float sg, float sb,
float er, float eg, float eb,
float am, float tr, float sh )
{
outer.push_back( POINT_2D( x, y ) );
diffuse_red = dr;
diffuse_grn = dg;
diffuse_blu = db;
spec_red = sr;
spec_grn = sg;
spec_blu = sb;
emit_red = er;
emit_grn = eg;
emit_blu = eb;
ambient = am;
transp = tr;
shiny = sh;
}
void bag( LAYER_NUM layer, bool close = true );
};
// A vertical quad loop
struct VLoop
enum VRML_COLOR_INDEX
{
std::vector<POINT_2D> pts;
double z_top, z_bottom;
void add( double x, double y )
{
pts.push_back( POINT_2D( x, y ) );
}
void bag( TRIANGLEBAG& triangles, bool close = true );
VRML_COLOR_PCB = 0,
VRML_COLOR_TRACK,
VRML_COLOR_SILK,
VRML_COLOR_TIN,
VRML_COLOR_LAST
};
// The bags for all the layers
static TRIANGLEBAG layer_triangles[NB_LAYERS];
static TRIANGLEBAG via_triangles[4];
static double layer_z[NB_LAYERS];
static void bag_flat_triangle( LAYER_NUM layer, //{{{
double x1, double y1,
double x2, double y2,
double x3, double y3 )
class MODEL_VRML
{
double z = layer_z[layer];
private:
layer_triangles[layer].push_back( TRIANGLE( x1, y1, z, x2, y2, z, x3, y3, z ) );
}
double layer_z[NB_LAYERS];
VRML_COLOR colors[VRML_COLOR_LAST];
public:
void FLAT_FAN::bag( LAYER_NUM layer, bool close ) //{{{
{
unsigned i;
VRML_LAYER holes;
VRML_LAYER board;
VRML_LAYER top_copper;
VRML_LAYER bot_copper;
VRML_LAYER top_silk;
VRML_LAYER bot_silk;
VRML_LAYER top_tin;
VRML_LAYER bot_tin;
for( i = 0; i < pts.size() - 1; i++ )
bag_flat_triangle( layer, c.x, c.y, pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y );
double scale; // board internal units to output scaling
if( close )
bag_flat_triangle( layer, c.x, c.y, pts[i].x, pts[i].y, pts[0].x, pts[0].y );
}
double tx; // global translation along X
double ty; // global translation along Y
double board_thickness; // depth of the PCB
static void bag_flat_quad( LAYER_NUM layer, //{{{
double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4 )
{
bag_flat_triangle( layer, x1, y1, x3, y3, x2, y2 );
bag_flat_triangle( layer, x2, y2, x3, y3, x4, y4 );
}
LAYER_NUM s_text_layer;
int s_text_width;
MODEL_VRML()
{
for( int i = 0; i < NB_LAYERS; ++i )
layer_z[i] = 0;
// this default only makes sense if the output is in mm
board_thickness = 1.6;
// pcb green
colors[ VRML_COLOR_PCB ] = VRML_COLOR( .07, .3, .12, .07, .3, .12,
0, 0, 0, 1, 0, 0.2 );
// track green
colors[ VRML_COLOR_TRACK ] = VRML_COLOR( .08, .5, .1, .08, .5, .1,
0, 0, 0, 1, 0, 0.2 );
// silkscreen white
colors[ VRML_COLOR_SILK ] = VRML_COLOR( .9, .9, .9, .9, .9, .9,
0, 0, 0, 1, 0, 0.2 );
// pad silver
colors[ VRML_COLOR_TIN ] = VRML_COLOR( .749, .756, .761, .749, .756, .761,
0, 0, 0, 0.8, 0, 0.8 );
}
void FLAT_RING::bag( LAYER_NUM layer, bool close ) //{{{
{
unsigned i;
for( i = 0; i < inner.size() - 1; i++ )
bag_flat_quad( layer,
inner[i].x, inner[i].y,
outer[i].x, outer[i].y,
inner[i + 1].x, inner[i + 1].y,
outer[i + 1].x, outer[i + 1].y );
if( close )
bag_flat_quad( layer,
inner[i].x, inner[i].y,
outer[i].x, outer[i].y,
inner[0].x, inner[0].y,
outer[0].x, outer[0].y );
}
VRML_COLOR& GetColor( VRML_COLOR_INDEX aIndex )
{
return colors[aIndex];
}
void SetOffset( double aXoff, double aYoff )
{
tx = aXoff;
ty = aYoff;
}
double GetLayerZ( LAYER_NUM aLayer )
{
if( aLayer >= NB_LAYERS )
return 0;
return layer_z[ aLayer ];
}
void SetLayerZ( LAYER_NUM aLayer, double aValue )
{
layer_z[aLayer] = aValue;
}
static void bag_vquad( TRIANGLEBAG& triangles, //{{{
double x1, double y1, double x2, double y2,
double z1, double z2 )
void SetMaxDev( double dev )
{
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 );
}
};
// static var. for dealing with text
namespace VRMLEXPORT
{
triangles.push_back( TRIANGLE( x1, y1, z1,
x2, y2, z1,
x2, y2, z2 ) );
triangles.push_back( TRIANGLE( x1, y1, z1,
x2, y2, z2,
x1, y1, z2 ) );
static MODEL_VRML* model_vrml;
bool GetLayer( MODEL_VRML& aModel, LAYER_NUM layer, VRML_LAYER** vlayer );
}
void VLoop::bag( TRIANGLEBAG& triangles, bool close ) //{{{
// 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 )
{
unsigned i;
switch( layer )
{
case FIRST_COPPER_LAYER:
*vlayer = &aModel.bot_copper;
break;
case LAST_COPPER_LAYER:
*vlayer = &aModel.top_copper;
break;
for( i = 0; i < pts.size() - 1; i++ )
bag_vquad( triangles, pts[i].x, pts[i].y,
pts[i + 1].x, pts[i + 1].y,
z_top, z_bottom );
case SILKSCREEN_N_BACK:
*vlayer = &aModel.bot_silk;
break;
case SILKSCREEN_N_FRONT:
*vlayer = &aModel.top_silk;
break;
default:
return false;
}
if( close )
bag_vquad( triangles, pts[i].x, pts[i].y,
pts[0].x, pts[0].y,
z_top, z_bottom );
return true;
}
static void write_triangle_bag( FILE* output_file, int color_index, //{{{
const TRIANGLEBAG& triangles,
double boardIU2WRML )
static void write_triangle_bag( FILE* output_file, VRML_COLOR& color,
VRML_LAYER* layer, bool plane, bool top,
double top_z, double bottom_z )
{
/* A lot of nodes are not required, but blender sometimes chokes
* without them */
......@@ -233,21 +300,18 @@ static void write_triangle_bag( FILE* output_file, int color_index, //{{{
" Shape {\n",
" appearance Appearance {\n",
" material Material {\n",
0, // Material marker
" ambientIntensity 0.8\n",
" transparency 0.2\n",
" shininess 0.2\n",
0, // Material marker
" }\n",
" }\n",
" geometry IndexedFaceSet {\n",
" solid TRUE\n",
" coord Coordinate {\n",
" point [\n",
0, // Coordinates marker
0, // Coordinates marker
" ]\n",
" }\n",
" coordIndex [\n",
0, // Index marker
0, // Index marker
" ]\n",
" }\n",
" }\n",
......@@ -255,7 +319,7 @@ static void write_triangle_bag( FILE* output_file, int color_index, //{{{
" }\n",
" ]\n",
"}\n",
0 // End marker
0 // End marker
};
int marker_found = 0, lineno = 0;
......@@ -270,59 +334,49 @@ static void write_triangle_bag( FILE* output_file, int color_index, //{{{
switch( marker_found )
{
case 1: // Material marker
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,
" diffuseColor %g %g %g\n",
(double) g_ColorRefs[color_index].m_Red / 255.0,
(double) g_ColorRefs[color_index].m_Green / 255.0,
(double) g_ColorRefs[color_index].m_Blue / 255.0 );
" emissiveColor %g %g %g\n",
color.emit_red,
color.emit_grn,
color.emit_blu );
fprintf( output_file,
" specularColor %g %g %g\n",
(double) g_ColorRefs[color_index].m_Red / 255.0,
(double) g_ColorRefs[color_index].m_Green / 255.0,
(double) g_ColorRefs[color_index].m_Blue / 255.0 );
" ambientIntensity %g\n", color.ambient );
fprintf( output_file,
" emissiveColor %g %g %g\n",
(double) g_ColorRefs[color_index].m_Red / 255.0,
(double) g_ColorRefs[color_index].m_Green / 255.0,
(double) g_ColorRefs[color_index].m_Blue / 255.0 );
" transparency %g\n", color.transp );
fprintf( output_file,
" shininess %g\n", color.shiny );
break;
case 2:
{
// Coordinates marker
for( TRIANGLEBAG::const_iterator i = triangles.begin();
i != triangles.end();
i++ )
{
fprintf( output_file, "%.8g %.8g %.8g\n",
i->p1.x * boardIU2WRML, -i->p1.y * boardIU2WRML,
i->p1.z * boardIU2WRML );
fprintf( output_file, "%.8g %.8g %.8g\n",
i->p2.x * boardIU2WRML, -i->p2.y * boardIU2WRML,
i->p2.z * boardIU2WRML );
fprintf( output_file, "%.8g %.8g %.8g\n",
i->p3.x * boardIU2WRML, -i->p3.y * boardIU2WRML,
i->p3.z * boardIU2WRML );
}
}
break;
if( plane )
layer->WriteVertices( top_z, output_file );
else
layer->Write3DVertices( top_z, bottom_z, output_file );
fprintf( output_file, "\n" );
break;
case 3:
{
// Index marker
// OK, that's sick ...
int j = 0;
for( TRIANGLEBAG::const_iterator i = triangles.begin();
i != triangles.end();
i++ )
{
fprintf( output_file, "%d %d %d -1\n", j, j + 1, j + 2 );
j += 3;
}
}
break;
if( plane )
layer->WriteIndices( top, output_file );
else
layer->Write3DIndices( output_file );
fprintf( output_file, "\n" );
break;
default:
break;
......@@ -334,344 +388,211 @@ static void write_triangle_bag( FILE* output_file, int color_index, //{{{
}
static void compute_layer_Zs( BOARD* pcb ) //{{{
static void write_layers( MODEL_VRML& aModel, FILE* output_file, BOARD* aPcb )
{
// VRML_LAYER board;
aModel.board.Tesselate( &aModel.holes );
double brdz = aModel.board_thickness / 2.0 - 40000 * aModel.scale;
write_triangle_bag( output_file, aModel.GetColor( VRML_COLOR_PCB ),
&aModel.board, false, false, brdz, -brdz );
// 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 );
// 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 );
// 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 );
// 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 );
// 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 );
// 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 );
}
static void compute_layer_Zs( MODEL_VRML& aModel, BOARD* pcb )
{
int copper_layers = pcb->GetCopperLayerCount( );
int copper_layers = pcb->GetCopperLayerCount();
// We call it 'layer' thickness, but it's the whole board thickness!
double board_thickness = pcb->GetDesignSettings().GetBoardThickness();
double half_thickness = board_thickness / 2;
aModel.board_thickness = pcb->GetDesignSettings().GetBoardThickness() * aModel.scale;
double half_thickness = aModel.board_thickness / 2;
// Compute each layer's Z value, more or less like the 3d view
for( LAYER_NUM i = FIRST_LAYER; i <= LAYER_N_FRONT; ++i )
{
if( i < copper_layers )
layer_z[i] = board_thickness * i / (copper_layers - 1) - half_thickness;
aModel.SetLayerZ( i, aModel.board_thickness * i / (copper_layers - 1) - half_thickness );
else
layer_z[i] = half_thickness; // The component layer...
aModel.SetLayerZ( i, half_thickness ); // component layer
}
/* To avoid rounding interference, we apply an epsilon to each
* successive layer */
const double epsilon_z = 0.02 * IU_PER_MM; // That's 1/50 mm
layer_z[SOLDERPASTE_N_BACK] = -half_thickness - epsilon_z * 4;
layer_z[ADHESIVE_N_BACK] = -half_thickness - epsilon_z * 3;
layer_z[SILKSCREEN_N_BACK] = -half_thickness - epsilon_z * 2;
layer_z[SOLDERMASK_N_BACK] = -half_thickness - epsilon_z;
layer_z[SOLDERMASK_N_FRONT] = half_thickness + epsilon_z;
layer_z[SILKSCREEN_N_FRONT] = half_thickness + epsilon_z * 2;
layer_z[ADHESIVE_N_FRONT] = half_thickness + epsilon_z * 3;
layer_z[SOLDERPASTE_N_FRONT] = half_thickness + epsilon_z * 4;
layer_z[DRAW_N] = half_thickness + epsilon_z * 5;
layer_z[COMMENT_N] = half_thickness + epsilon_z * 6;
layer_z[ECO1_N] = half_thickness + epsilon_z * 7;
layer_z[ECO2_N] = half_thickness + epsilon_z * 8;
layer_z[EDGE_N] = 0;
}
static void export_vrml_line( LAYER_NUM layer, double startx, double starty, //{{{
double endx, double endy, double width, int divisions )
{
double r = width / 2;
double angle = atan2( endy - starty, endx - startx );
double alpha;
FLAT_FAN fan;
// Output the 'bone' as a triangle fan, this is the fan centre
fan.c.x = (startx + endx) / 2;
fan.c.y = (starty + endy) / 2;
// The 'end' side cap
for( alpha = angle - PI2; alpha < angle + PI2; alpha += PI2 / divisions )
fan.add( endx + r * cos( alpha ), endy + r * sin( alpha ) );
alpha = angle + PI2;
fan.add( endx + r * cos( alpha ), endy + r * sin( alpha ) );
// The 'start' side cap
for( alpha = angle + PI2; alpha < angle + 3 * PI2; alpha += PI2 / divisions )
fan.add( startx + r * cos( alpha ), starty + r * sin( alpha ) );
alpha = angle + 3 * PI2;
fan.add( startx + r * cos( alpha ), starty + r * sin( alpha ) );
// Export the fan
fan.bag( layer );
}
static void export_vrml_circle( LAYER_NUM layer, double startx, double starty, //{{{
double endx, double endy, double width )
{
double hole, radius;
FLAT_RING ring;
radius = Distance( startx, starty, endx, endy ) + ( width / 2);
hole = radius - width;
for( double alpha = 0; alpha < M_PI * 2; alpha += INC_ANGLE )
{
ring.add_inner( startx + hole * cos( alpha ), starty + hole * sin( alpha ) );
ring.add_outer( startx + radius * cos( alpha ), starty + radius * sin( alpha ) );
}
ring.bag( layer );
}
static void export_vrml_slot( TRIANGLEBAG& triangles, //{{{
LAYER_NUM top_layer, LAYER_NUM bottom_layer, double xc, double yc,
double dx, double dy, double orient )
{
double capx, capy; // Cap center
VLoop loop;
int divisions = SEGM_COUNT_PER_360 / 2;
loop.z_top = layer_z[top_layer];
loop.z_bottom = layer_z[bottom_layer];
double angle = DECIDEG2RAD( orient );
if( dy > dx )
{
EXCHG( dx, dy );
angle += PI2;
}
// The exchange above means that cutter radius is alvays dy/2
double r = dy / 2;
double alpha;
// The first side cap
capx = xc + cos( angle ) * dx / 2;
capy = yc + sin( angle ) * dx / 2;
for( alpha = angle - PI2; alpha < angle + PI2; alpha += PI2 / divisions )
loop.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
alpha = angle + PI2;
loop.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
// The other side cap
capx = xc - cos( angle ) * dx / 2;
capy = yc - sin( angle ) * dx / 2;
for( alpha = angle + PI2; alpha < angle + 3 * PI2; alpha += PI2 / divisions )
loop.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
alpha = angle + 3 * PI2;
loop.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
loop.bag( triangles );
double epsilon_z = Millimeter2iu( 0.02 ) * 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 );
aModel.SetLayerZ( SOLDERMASK_N_BACK, -half_thickness - epsilon_z );
aModel.SetLayerZ( SOLDERMASK_N_FRONT, half_thickness + epsilon_z );
aModel.SetLayerZ( SILKSCREEN_N_FRONT, half_thickness + epsilon_z * 2 );
aModel.SetLayerZ( ADHESIVE_N_FRONT, half_thickness + epsilon_z * 3 );
aModel.SetLayerZ( SOLDERPASTE_N_FRONT, half_thickness + epsilon_z * 4 );
aModel.SetLayerZ( DRAW_N, half_thickness + epsilon_z * 5 );
aModel.SetLayerZ( COMMENT_N, half_thickness + epsilon_z * 6 );
aModel.SetLayerZ( ECO1_N, half_thickness + epsilon_z * 7 );
aModel.SetLayerZ( ECO2_N, half_thickness + epsilon_z * 8 );
aModel.SetLayerZ( EDGE_N, 0 );
}
static void export_vrml_hole( TRIANGLEBAG& triangles,
int top_layer, int bottom_layer,
double xc, double yc, double hole )
static void export_vrml_line( MODEL_VRML& aModel, LAYER_NUM layer,
double startx, double starty,
double endx, double endy, double width )
{
VLoop loop;
VRML_LAYER* vlayer;
loop.z_top = layer_z[top_layer];
loop.z_bottom = layer_z[bottom_layer];
if( !VRMLEXPORT::GetLayer( aModel, layer, &vlayer ) )
return;
for( double alpha = 0; alpha < M_PI * 2; alpha += INC_ANGLE )
loop.add( xc + cos( alpha ) * hole, yc + sin( alpha ) * hole );
starty = -starty;
endy = -endy;
loop.bag( triangles );
}
static void export_vrml_oval_pad( LAYER_NUM layer, double xc, double yc,
double dx, double dy, double orient )
{
double capx, capy; // Cap center
FLAT_FAN fan;
fan.c.x = xc;
fan.c.y = yc;
double angle = DECIDEG2RAD( orient );
int divisions = SEGM_COUNT_PER_360 / 2;
if( dy > dx )
{
EXCHG( dx, dy );
angle += PI2;
}
// The exchange above means that cutter radius is alvays dy/2
double r = dy / 2;
double alpha;
// The first side cap
capx = xc + cos( angle ) * dx / 2;
capy = yc + sin( angle ) * dx / 2;
double angle = atan2( endy - starty, endx - startx );
double length = Distance( startx, starty, endx, endy ) + width;
double cx = ( startx + endx ) / 2.0;
double cy = ( starty + endy ) / 2.0;
for( alpha = angle - PI2; alpha < angle + PI2; alpha += PI2 / divisions )
fan.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
alpha = angle + PI2;
fan.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
// The other side cap
capx = xc - cos( angle ) * dx / 2;
capy = yc - sin( angle ) * dx / 2;
for( alpha = angle + PI2; alpha < angle + 3 * PI2; alpha += PI2 / divisions )
fan.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
alpha = angle + 3 * PI2;
fan.add( capx + r * cos( alpha ), capy + r * sin( alpha ) );
fan.bag( layer );
vlayer->AddSlot( cx, cy, length, width, angle, 1, false );
}
static void export_vrml_arc( LAYER_NUM layer, double centerx, double centery,
double arc_startx, double arc_starty,
double width, double arc_angle )
static void export_vrml_circle( MODEL_VRML& aModel, LAYER_NUM layer,
double startx, double starty,
double endx, double endy, double width )
{
FLAT_RING ring;
double start_angle = atan2( arc_starty - centery, arc_startx - centerx );
VRML_LAYER* vlayer;
int count = KiROUND( arc_angle / 360.0 * SEGM_COUNT_PER_360 );
if( !VRMLEXPORT::GetLayer( aModel, layer, &vlayer ) )
return;
if( count < 0 )
count = -count;
starty = -starty;
endy = -endy;
if( count == 0 )
count = 1;
double hole, radius;
double divisions = arc_angle*M_PI/180.0 / count;
radius = Distance( startx, starty, endx, endy ) + ( width / 2);
hole = radius - width;
double outer_radius = Distance( arc_startx, arc_starty, centerx, centery )
+ ( width / 2);
double inner_radius = outer_radius - width;
vlayer->AddCircle( startx, starty, radius, 1, false );
double alpha = 0;
for( int ii = 0; ii <= count; alpha += divisions, ii++ )
if( hole > 0.0001 )
{
double angle_rot = start_angle + alpha;
ring.add_inner( centerx + cos( angle_rot ) * inner_radius,
centery + sin( angle_rot ) * inner_radius );
ring.add_outer( centerx + cos( angle_rot ) * outer_radius,
centery + sin( angle_rot ) * outer_radius );
vlayer->AddCircle( startx, starty, hole, 1, true );
}
ring.bag( layer, false );
}
static void export_vrml_varc( TRIANGLEBAG& triangles,
LAYER_NUM top_layer, LAYER_NUM bottom_layer,
double centerx, double centery,
double arc_startx, double arc_starty,
double arc_angle )
static void export_vrml_arc( MODEL_VRML& aModel, LAYER_NUM layer,
double centerx, double centery,
double arc_startx, double arc_starty,
double width, double arc_angle )
{
VLoop loop;
loop.z_top = layer_z[top_layer];
loop.z_bottom = layer_z[bottom_layer];
double start_angle = atan2( arc_starty - centery, arc_startx - centerx );
double radius = Distance( arc_startx, arc_starty, centerx, centery );
int count = KiROUND( arc_angle / 360.0 * SEGM_COUNT_PER_360 );
if( count < 0 )
count = -count;
VRML_LAYER* vlayer;
if( count == 0 )
count = 1;
if( !VRMLEXPORT::GetLayer( aModel, layer, &vlayer ) )
return;
double divisions = arc_angle*M_PI/180.0 / count;
centery = -centery;
arc_starty = -arc_starty;
double alpha = 0;
arc_angle *= -M_PI / 180;
for( int ii = 0; ii <= count; alpha += divisions, ii++ )
{
double angle_rot = start_angle + alpha;
loop.add( centerx + cos( angle_rot ) * radius, centery + sin( angle_rot ) * radius );
}
loop.bag( triangles );
vlayer->AddArc( centerx, centery, arc_startx, arc_starty,
width, arc_angle, 1, false );
}
static void export_vrml_drawsegment( DRAWSEGMENT* drawseg ) //{{{
static void export_vrml_drawsegment( MODEL_VRML& aModel, DRAWSEGMENT* drawseg )
{
LAYER_NUM layer = drawseg->GetLayer();
double w = drawseg->GetWidth();
double x = drawseg->GetStart().x;
double y = drawseg->GetStart().y;
double xf = drawseg->GetEnd().x;
double yf = drawseg->GetEnd().y;
double w = drawseg->GetWidth() * aModel.scale;
double x = drawseg->GetStart().x * aModel.scale + aModel.tx;
double y = drawseg->GetStart().y * aModel.scale + aModel.ty;
double xf = drawseg->GetEnd().x * aModel.scale + aModel.tx;
double yf = drawseg->GetEnd().y * aModel.scale + aModel.ty;
// Items on the edge layer are high, not thick
// Items on the edge layer are handled elsewhere; just return
if( layer == EDGE_N )
{
switch( drawseg->GetShape() )
{
// There is a special 'varc' primitive for this
case S_ARC:
export_vrml_varc( layer_triangles[layer],
FIRST_COPPER_LAYER, LAST_COPPER_LAYER,
x, y, xf, yf, drawseg->GetAngle()/10 );
break;
// Circles on edge are usually important holes
case S_CIRCLE:
export_vrml_hole( layer_triangles[layer],
FIRST_COPPER_LAYER, LAST_COPPER_LAYER, x, y,
Distance( xf, yf, x, y ) / 2 );
break;
return;
default:
{
// Simply a quad
double z_top = layer_z[FIRST_COPPER_LAYER];
double z_bottom = layer_z[LAST_COPPER_LAYER];
bag_vquad( layer_triangles[layer], x, y, xf, yf, z_top, z_bottom );
break;
}
}
}
else
switch( drawseg->GetShape() )
{
switch( drawseg->GetShape() )
{
case S_ARC:
export_vrml_arc( layer,
(double) drawseg->GetCenter().x,
(double) drawseg->GetCenter().y,
(double) drawseg->GetArcStart().x,
(double) drawseg->GetArcStart().y,
w, drawseg->GetAngle()/10 );
break;
case S_ARC:
export_vrml_arc( aModel, layer,
(double) drawseg->GetCenter().x,
(double) drawseg->GetCenter().y,
(double) drawseg->GetArcStart().x,
(double) drawseg->GetArcStart().y,
w, drawseg->GetAngle() / 10 );
break;
case S_CIRCLE:
export_vrml_circle( layer, x, y, xf, yf, w );
break;
case S_CIRCLE:
export_vrml_circle( aModel, layer, x, y, xf, yf, w );
break;
default:
export_vrml_line( layer, x, y, xf, yf, w, 1 );
break;
}
default:
export_vrml_line( aModel, layer, x, y, xf, yf, w );
break;
}
}
/* C++ doesn't have closures and neither continuation forms... this is
* for coupling the vrml_text_callback with the common parameters */
static LAYER_NUM s_text_layer;
static int s_text_width;
static void vrml_text_callback( int x0, int y0, int xf, int yf )
{
export_vrml_line( s_text_layer, x0, y0, xf, yf, s_text_width, 1 );
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;
export_vrml_line( *VRMLEXPORT::model_vrml, s_text_layer,
x0 * scale + tx, y0 * scale + ty,
xf * scale + tx, yf * scale + ty,
s_text_width * scale );
}
static void export_vrml_pcbtext( TEXTE_PCB* text )
static void export_vrml_pcbtext( MODEL_VRML& aModel, TEXTE_PCB* text )
{
// Coupling by globals! Ewwww...
s_text_layer = text->GetLayer();
s_text_width = text->GetThickness();
VRMLEXPORT::model_vrml->s_text_layer = text->GetLayer();
VRMLEXPORT::model_vrml->s_text_width = text->GetThickness();
wxSize size = text->GetSize();
......@@ -680,9 +601,9 @@ static void export_vrml_pcbtext( TEXTE_PCB* text )
if( text->IsMultilineAllowed() )
{
wxPoint pos = text->GetTextPosition();
wxPoint pos = text->GetTextPosition();
wxArrayString* list = wxStringSplit( text->GetText(), '\n' );
wxPoint offset;
wxPoint offset;
offset.y = text->GetInterline();
......@@ -692,11 +613,11 @@ static void export_vrml_pcbtext( TEXTE_PCB* text )
{
wxString txt = list->Item( i );
DrawGraphicText( NULL, NULL, pos, BLACK,
txt, text->GetOrientation(), size,
text->GetHorizJustify(), text->GetVertJustify(),
text->GetThickness(), text->IsItalic(),
true,
vrml_text_callback );
txt, text->GetOrientation(), size,
text->GetHorizJustify(), text->GetVertJustify(),
text->GetThickness(), text->IsItalic(),
true,
vrml_text_callback );
pos += offset;
}
......@@ -705,28 +626,34 @@ static void export_vrml_pcbtext( TEXTE_PCB* text )
else
{
DrawGraphicText( NULL, NULL, text->GetTextPosition(), BLACK,
text->GetText(), text->GetOrientation(), size,
text->GetHorizJustify(), text->GetVertJustify(),
text->GetThickness(), text->IsItalic(),
true,
vrml_text_callback );
text->GetText(), text->GetOrientation(), size,
text->GetHorizJustify(), text->GetVertJustify(),
text->GetThickness(), text->IsItalic(),
true,
vrml_text_callback );
}
}
static void export_vrml_drawings( BOARD* pcb ) //{{{
static void export_vrml_drawings( MODEL_VRML& aModel, BOARD* pcb )
{
// draw graphic items
for( EDA_ITEM* drawing = pcb->m_Drawings; drawing != 0; drawing = drawing->Next() )
for( EDA_ITEM* drawing = pcb->m_Drawings; drawing != 0; drawing = drawing->Next() )
{
LAYER_NUM layer = ( (DRAWSEGMENT*) drawing )->GetLayer();
if( layer != FIRST_COPPER_LAYER && layer != LAST_COPPER_LAYER
&& layer != SILKSCREEN_N_BACK && layer != SILKSCREEN_N_FRONT )
continue;
switch( drawing->Type() )
{
case PCB_LINE_T:
export_vrml_drawsegment( (DRAWSEGMENT*) drawing );
export_vrml_drawsegment( aModel, (DRAWSEGMENT*) drawing );
break;
case PCB_TEXT_T:
export_vrml_pcbtext( (TEXTE_PCB*) drawing );
export_vrml_pcbtext( aModel, (TEXTE_PCB*) drawing );
break;
default:
......@@ -736,167 +663,382 @@ static void export_vrml_drawings( BOARD* pcb ) //{{{
}
static void export_round_padstack( BOARD* pcb, double x, double y, double r, //{{{
LAYER_NUM bottom_layer, LAYER_NUM top_layer )
// board edges and cutouts
static void export_vrml_board( MODEL_VRML& aModel, BOARD* pcb )
{
CPOLYGONS_LIST bufferPcbOutlines; // stores the board main outlines
CPOLYGONS_LIST allLayerHoles; // Contains through holes, calculated only once
allLayerHoles.reserve( 20000 );
// Build a polygon from edge cut items
wxString msg;
if( !pcb->GetBoardPolygonOutlines( bufferPcbOutlines,
allLayerHoles, &msg ) )
{
msg << wxT( "\n\n" ) <<
_( "Unable to calculate the board outlines;\n"
"fall back to using the board boundary box." );
wxMessageBox( msg );
}
double scale = aModel.scale;
double dx = aModel.tx;
double dy = aModel.ty;
int i = 0;
int seg;
// deal with the solid outlines
int nvert = bufferPcbOutlines.GetCornersCount();
while( i < nvert )
{
seg = aModel.board.NewContour();
if( seg < 0 )
{
msg << wxT( "\n\n" ) <<
_( "VRML Export Failed:\nCould not add outline to contours." );
wxMessageBox( msg );
return;
}
while( i < nvert )
{
aModel.board.AddVertex( seg, bufferPcbOutlines[i].x * scale + dx,
-(bufferPcbOutlines[i].y * scale + dy) );
if( bufferPcbOutlines[i].end_contour )
break;
++i;
}
aModel.board.EnsureWinding( seg, false );
++i;
}
// deal with the holes
nvert = allLayerHoles.GetCornersCount();
i = 0;
while( i < nvert )
{
seg = aModel.holes.NewContour();
if( seg < 0 )
{
msg << wxT( "\n\n" ) <<
_( "VRML Export Failed:\nCould not add holes to contours." );
wxMessageBox( msg );
return;
}
while( i < nvert )
{
aModel.holes.AddVertex( seg, allLayerHoles[i].x * scale + dx,
-(allLayerHoles[i].y * scale + dy) );
if( allLayerHoles[i].end_contour )
break;
++i;
}
aModel.holes.EnsureWinding( seg, true );
++i;
}
}
static void export_round_padstack( MODEL_VRML& aModel, BOARD* pcb,
double x, double y, double r,
LAYER_NUM bottom_layer, LAYER_NUM top_layer,
double hole )
{
int copper_layers = pcb->GetCopperLayerCount( );
LAYER_NUM layer = top_layer;
bool thru = true;
for( LAYER_NUM layer = bottom_layer; layer < copper_layers; ++layer )
// if not a thru hole do not put a hole in the board
if( top_layer != LAST_COPPER_LAYER || bottom_layer != FIRST_COPPER_LAYER )
thru = false;
while( 1 )
{
// The last layer is always the component one, unless it's single face
if( (layer > FIRST_COPPER_LAYER) && (layer == copper_layers - 1) )
layer = LAST_COPPER_LAYER;
if( layer == FIRST_COPPER_LAYER )
{
aModel.bot_copper.AddCircle( x, -y, r, 1 );
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 );
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 <= top_layer )
export_vrml_circle( layer, x, y, x + r / 2, y, r );
if( layer == bottom_layer )
break;
layer = bottom_layer;
}
}
static void export_vrml_via( BOARD* pcb, SEGVIA* via ) //{{{
static void export_vrml_via( MODEL_VRML& aModel, BOARD* pcb, SEGVIA* via )
{
double x, y, r, hole;
LAYER_NUM top_layer, bottom_layer;
r = via->GetWidth() / 2;
hole = via->GetDrillValue() / 2;
x = via->GetStart().x;
y = via->GetStart().y;
hole = via->GetDrillValue() * aModel.scale / 2.0;
r = via->GetWidth() * aModel.scale / 2.0;
x = via->GetStart().x * aModel.scale + aModel.tx;
y = via->GetStart().y * aModel.scale + aModel.ty;
via->ReturnLayerPair( &top_layer, &bottom_layer );
// Export the via padstack
export_round_padstack( pcb, x, y, r, bottom_layer, top_layer );
// do not render a buried via
if( top_layer != LAST_COPPER_LAYER && bottom_layer != FIRST_COPPER_LAYER )
return;
// Drill a hole
export_vrml_hole( via_triangles[via->GetShape()], top_layer, bottom_layer, x, y, hole );
// Export the via padstack
export_round_padstack( aModel, pcb, x, y, r, bottom_layer, top_layer, hole );
}
static void export_vrml_tracks( BOARD* pcb ) //{{{
static void export_vrml_tracks( MODEL_VRML& aModel, BOARD* pcb )
{
for( TRACK* track = pcb->m_Track; track != NULL; track = track->Next() )
{
if( track->Type() == PCB_VIA_T )
export_vrml_via( pcb, (SEGVIA*) track );
else
export_vrml_line( track->GetLayer(), track->GetStart().x, track->GetStart().y,
track->GetEnd().x, track->GetEnd().y, track->GetWidth(), 4 );
{
export_vrml_via( aModel, pcb, (SEGVIA*) track );
}
else if( track->GetLayer() == FIRST_COPPER_LAYER
|| track->GetLayer() == LAST_COPPER_LAYER )
export_vrml_line( aModel, track->GetLayer(),
track->GetStart().x * aModel.scale + aModel.tx,
track->GetStart().y * aModel.scale + aModel.ty,
track->GetEnd().x * aModel.scale + aModel.tx,
track->GetEnd().y * aModel.scale + aModel.ty,
track->GetWidth() * aModel.scale );
}
}
/* not used? @todo complete
static void export_vrml_zones( BOARD* pcb )
static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb )
{
// Export fill segments
for( SEGZONE* segzone = pcb->m_Zone;
segzone != 0;
segzone = segzone->Next() )
{
// Fill tracks are exported with low subdivisions
if( segzone->Type() == PCB_ZONE_T )
export_vrml_line( segzone->GetLayer(), segzone->m_Start.x, segzone->m_Start.y,
segzone->m_End.x, segzone->m_End.y, segzone->m_Width, 1 );
}
// Export zone outlines
for( int i = 0; i < pcb->GetAreaCount(); i++ )
double scale = aModel.scale;
double dx = aModel.tx;
double dy = aModel.ty;
double x, y;
for( int ii = 0; ii < aPcb->GetAreaCount(); ii++ )
{
ZONE_CONTAINER* zone = pcb->GetArea( i );
ZONE_CONTAINER* zone = aPcb->GetArea( ii );
VRML_LAYER* vl;
if( ( zone->m_FilledPolysList.size() == 0 )
||( zone->GetMinThickness() <= 1 ) )
if( !VRMLEXPORT::GetLayer( aModel, zone->GetLayer(), &vl ) )
continue;
int width = zone->GetMinThickness();
if( !zone->IsFilled() )
{
zone->SetFillMode( 0 ); // use filled polygons
zone->BuildFilledSolidAreasPolygons( aPcb );
}
const CPOLYGONS_LIST& poly = zone->GetFilledPolysList();
int nvert = poly.GetCornersCount();
int i = 0;
if( width > 0 )
while( i < nvert )
{
int imax = zone->m_FilledPolysList.size() - 1;
LAYER_NUM layer = zone->GetLayer();
CPolyPt* firstcorner = &zone->m_FilledPolysList[0];
CPolyPt* begincorner = firstcorner;
int seg = vl->NewContour();
bool first = true;
// I'm not really positive about what he's doing here...
for( int ic = 1; ic <= imax; ic++ )
{
CPolyPt* endcorner = &zone->m_FilledPolysList[ic];
if( seg < 0 )
break;
export_vrml_line( layer, begincorner->x, begincorner->y,
endcorner->x, endcorner->y, width, 1 );
while( i < nvert )
{
x = poly.GetX(i) * scale + dx;
y = -(poly.GetY(i) * scale + dy);
vl->AddVertex( seg, x, y );
if( (endcorner->end_contour) || (ic == imax) ) // the last corner of a filled area is found: draw it
{
export_vrml_line( layer, endcorner->x, endcorner->y,
firstcorner->x, firstcorner->y, width, 1 );
ic++;
if( poly.IsEndContour(i) )
break;
// A new contour?
if( ic < imax - 1 )
begincorner = firstcorner = &zone->m_FilledPolysList[ic];
}
else
begincorner = endcorner;
++i;
}
// KiCad ensures that the first polygon is the outline
// and all others are holes
vl->EnsureWinding( seg, first ? false : true );
if( first )
first = false;
++i;
}
}
}
*/
static void export_vrml_text_module( TEXTE_MODULE* module ) //{{{
static void export_vrml_text_module( TEXTE_MODULE* module )
{
if( module->IsVisible() )
{
wxSize size = module->GetSize();
if( module->IsMirrored() )
NEGATE( size.x ); // Text is mirrored
NEGATE( size.x ); // Text is mirrored
VRMLEXPORT::model_vrml->s_text_layer = module->GetLayer();
VRMLEXPORT::model_vrml->s_text_width = module->GetThickness();
s_text_layer = module->GetLayer();
s_text_width = module->GetThickness();
DrawGraphicText( NULL, NULL, module->GetTextPosition(), BLACK,
module->GetText(), module->GetDrawRotation(), size,
module->GetHorizJustify(), module->GetVertJustify(),
module->GetThickness(), module->IsItalic(),
true,
vrml_text_callback );
module->GetText(), module->GetDrawRotation(), size,
module->GetHorizJustify(), module->GetVertJustify(),
module->GetThickness(), module->IsItalic(),
true,
vrml_text_callback );
}
}
static void export_vrml_edge_module( EDGE_MODULE* aOutline ) //{{{
static void export_vrml_edge_module( MODEL_VRML& aModel, EDGE_MODULE* aOutline )
{
LAYER_NUM layer = aOutline->GetLayer();
double x = aOutline->GetStart().x;
double y = aOutline->GetStart().y;
double xf = aOutline->GetEnd().x;
double yf = aOutline->GetEnd().y;
double w = aOutline->GetWidth();
double x = aOutline->GetStart().x * aModel.scale + aModel.tx;
double y = aOutline->GetStart().y * aModel.scale + aModel.ty;
double xf = aOutline->GetEnd().x * aModel.scale + aModel.tx;
double yf = aOutline->GetEnd().y * aModel.scale + aModel.ty;
double w = aOutline->GetWidth() * aModel.scale;
switch( aOutline->GetShape() )
{
case S_ARC:
export_vrml_arc( layer, x, y, xf, yf, w, aOutline->GetAngle()/10 );
export_vrml_arc( aModel, layer, x, y, xf, yf, w, aOutline->GetAngle() / 10 );
break;
case S_CIRCLE:
export_vrml_circle( layer, x, y, xf, yf, w );
export_vrml_circle( aModel, layer, x, y, xf, yf, w );
break;
default:
export_vrml_line( layer, x, y, xf, yf, w, 1 );
export_vrml_line( aModel, layer, x, y, xf, yf, w );
break;
}
}
static void export_vrml_pad( BOARD* pcb, D_PAD* aPad ) //{{{
static void export_vrml_padshape( MODEL_VRML& aModel, VRML_LAYER* aLayer,
VRML_LAYER* aTinLayer, D_PAD* aPad )
{
double hole_drill_w = (double) aPad->GetDrillSize().x / 2;
double hole_drill_h = (double) aPad->GetDrillSize().y / 2;
double hole_drill = std::min( hole_drill_w, hole_drill_h );
double hole_x = aPad->GetPosition().x;
double hole_y = aPad->GetPosition().y;
// The (maybe offset) pad position
wxPoint pad_pos = aPad->ReturnShapePos();
double pad_x = pad_pos.x * aModel.scale + aModel.tx;
double pad_y = pad_pos.y * aModel.scale + aModel.ty;
wxSize pad_delta = aPad->GetDelta();
double pad_dx = pad_delta.x * aModel.scale / 2.0;
double pad_dy = pad_delta.y * aModel.scale / 2.0;
double pad_w = aPad->GetSize().x * aModel.scale / 2.0;
double pad_h = aPad->GetSize().y * aModel.scale / 2.0;
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 );
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 );
break;
case PAD_RECT:
// Just to be sure :D
pad_dx = 0;
pad_dy = 0;
case PAD_TRAPEZOID:
{
double coord[8] =
{
-pad_w + pad_dy, -pad_h - pad_dx,
-pad_w - pad_dy, pad_h + pad_dx,
+pad_w - pad_dy, -pad_h + pad_dx,
+pad_w + pad_dy, pad_h - pad_dx
};
for( int i = 0; i < 4; i++ )
{
RotatePoint( &coord[i * 2], &coord[i * 2 + 1], aPad->GetOrientation() );
coord[i * 2] += pad_x;
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 );
lines = aTinLayer->NewContour();
if( lines < 0 )
return;
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 );
}
break;
default:
;
}
}
static void export_vrml_pad( MODEL_VRML& aModel, BOARD* pcb, D_PAD* aPad )
{
double hole_drill_w = (double) aPad->GetDrillSize().x * aModel.scale / 2.0;
double hole_drill_h = (double) aPad->GetDrillSize().y * aModel.scale / 2.0;
double hole_drill = std::min( hole_drill_w, hole_drill_h );
double hole_x = aPad->GetPosition().x * aModel.scale + aModel.tx;
double hole_y = aPad->GetPosition().y * aModel.scale + aModel.ty;
// Export the hole on the edge layer
if( hole_drill > 0 )
......@@ -904,90 +1046,27 @@ static void export_vrml_pad( BOARD* pcb, D_PAD* aPad ) //{{{
if( aPad->GetDrillShape() == PAD_OVAL )
{
// Oblong hole (slot)
export_vrml_slot( layer_triangles[EDGE_N],
FIRST_COPPER_LAYER, LAST_COPPER_LAYER,
hole_x, hole_y, hole_drill_w, hole_drill_h,
aPad->GetOrientation() );
aModel.holes.AddSlot( hole_x, -hole_y, hole_drill_w * 2.0, hole_drill_h * 2.0,
DECIDEG2RAD( aPad->GetOrientation() ), 1, true );
}
else
{
// Drill a round hole
export_vrml_hole( layer_triangles[EDGE_N],
FIRST_COPPER_LAYER, LAST_COPPER_LAYER,
hole_x, hole_y, hole_drill );
aModel.holes.AddCircle( hole_x, -hole_y, hole_drill, 1, true );
}
}
// The pad proper, on the selected layers
LAYER_MSK layer_mask = aPad->GetLayerMask();
int copper_layers = pcb->GetCopperLayerCount( );
// The (maybe offseted) pad position
wxPoint pad_pos = aPad->ReturnShapePos();
double pad_x = pad_pos.x;
double pad_y = pad_pos.y;
wxSize pad_delta = aPad->GetDelta();
double pad_dx = pad_delta.x / 2;
double pad_dy = pad_delta.y / 2;
LAYER_MSK layer_mask = aPad->GetLayerMask();
double pad_w = aPad->GetSize().x / 2;
double pad_h = aPad->GetSize().y / 2;
for( LAYER_NUM layer = FIRST_COPPER_LAYER; layer < copper_layers; ++layer )
if( layer_mask & LAYER_BACK )
{
// The last layer is always the component one, unless it's single face
if( (layer > FIRST_COPPER_LAYER) && (layer == copper_layers - 1) )
layer = LAST_COPPER_LAYER;
if( layer_mask & GetLayerMask( layer ) )
{
// OK, the pad is on this layer, export it
switch( aPad->GetShape() )
{
case PAD_CIRCLE:
export_vrml_circle( layer, pad_x, pad_y,
pad_x + pad_w / 2, pad_y, pad_w );
break;
case PAD_OVAL:
export_vrml_oval_pad( layer, pad_x, pad_y,
pad_w * 2, pad_h * 2, aPad->GetOrientation() );
break;
case PAD_RECT:
// Just to be sure :D
pad_dx = 0;
pad_dy = 0;
case PAD_TRAPEZOID:
{
int coord[8] =
{
KiROUND( -pad_w - pad_dy ), KiROUND( +pad_h + pad_dx ),
KiROUND( -pad_w + pad_dy ), KiROUND( -pad_h - pad_dx ),
KiROUND( +pad_w - pad_dy ), KiROUND( +pad_h - pad_dx ),
KiROUND( +pad_w + pad_dy ), KiROUND( -pad_h + pad_dx ),
};
for( int i = 0; i < 4; i++ )
{
RotatePoint( &coord[i * 2], &coord[i * 2 + 1], aPad->GetOrientation() );
coord[i * 2] += KiROUND( pad_x );
coord[i * 2 + 1] += KiROUND( pad_y );
}
bag_flat_quad( layer, coord[0], coord[1],
coord[2], coord[3],
coord[4], coord[5],
coord[6], coord[7] );
}
break;
export_vrml_padshape( aModel, &aModel.bot_copper, &aModel.bot_tin, aPad );
}
default:
;
}
}
if( layer_mask & LAYER_FRONT )
{
export_vrml_padshape( aModel, &aModel.top_copper, &aModel.top_tin, aPad );
}
}
......@@ -1021,27 +1100,32 @@ static void compose_quat( double q1[4], double q2[4], double qr[4] )
{
double tmp[4];
tmp[0] = q2[3] *q1[0] + q2[0] *q1[3] + q2[1] *q1[2] - q2[2] *q1[1];
tmp[1] = q2[3] *q1[1] + q2[1] *q1[3] + q2[2] *q1[0] - q2[0] *q1[2];
tmp[2] = q2[3] *q1[2] + q2[2] *q1[3] + q2[0] *q1[1] - q2[1] *q1[0];
tmp[3] = q2[3] *q1[3] - q2[0] *q1[0] - q2[1] *q1[1] - q2[2] *q1[2];
qr[0] = tmp[0]; qr[1] = tmp[1];
qr[2] = tmp[2]; qr[3] = tmp[3];
tmp[0] = q2[3] * q1[0] + q2[0] * q1[3] + q2[1] * q1[2] - q2[2] * q1[1];
tmp[1] = q2[3] * q1[1] + q2[1] * q1[3] + q2[2] * q1[0] - q2[0] * q1[2];
tmp[2] = q2[3] * q1[2] + q2[2] * q1[3] + q2[0] * q1[1] - q2[1] * q1[0];
tmp[3] = q2[3] * q1[3] - q2[0] * q1[0] - q2[1] * q1[1] - q2[2] * q1[2];
qr[0] = tmp[0];
qr[1] = tmp[1];
qr[2] = tmp[2];
qr[3] = tmp[3];
}
static void export_vrml_module( BOARD* aPcb, MODULE* aModule,
FILE* aOutputFile,
double aVRMLModelsToBiu,
bool aExport3DFiles, const wxString & a3D_Subdir,
double boardIU2WRML )
static void export_vrml_module( MODEL_VRML& aModel, BOARD* aPcb, MODULE* aModule,
FILE* aOutputFile,
double aVRMLModelsToBiu,
bool aExport3DFiles, const wxString& a3D_Subdir )
{
// Reference and value
export_vrml_text_module( &aModule->Reference() );
export_vrml_text_module( &aModule->Value() );
if( aModule->Reference().IsVisible() )
export_vrml_text_module( &aModule->Reference() );
if( aModule->Value().IsVisible() )
export_vrml_text_module( &aModule->Value() );
// Export module edges
for( EDA_ITEM* item = aModule->GraphicalItems(); item != NULL; item = item->Next() )
for( EDA_ITEM* item = aModule->GraphicalItems(); item != NULL; item = item->Next() )
{
switch( item->Type() )
{
......@@ -1050,7 +1134,7 @@ static void export_vrml_module( BOARD* aPcb, MODULE* aModule,
break;
case PCB_MODULE_EDGE_T:
export_vrml_edge_module( dynamic_cast<EDGE_MODULE*>( item ) );
export_vrml_edge_module( aModel, dynamic_cast<EDGE_MODULE*>( item ) );
break;
default:
......@@ -1059,8 +1143,8 @@ static void export_vrml_module( BOARD* aPcb, MODULE* aModule,
}
// Export pads
for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() )
export_vrml_pad( aPcb, pad );
for( D_PAD* pad = aModule->Pads(); pad; pad = pad->Next() )
export_vrml_pad( aModel, aPcb, pad );
bool isFlipped = aModule->GetLayer() == LAYER_N_BACK;
......@@ -1072,12 +1156,12 @@ static void export_vrml_module( BOARD* aPcb, MODULE* aModule,
if( fname.IsEmpty() )
continue;
if( ! wxFileName::FileExists( fname ) )
if( !wxFileName::FileExists( fname ) )
{
wxFileName fn = fname;
fname = wxGetApp().FindLibraryPath( fn );
if( fname.IsEmpty() ) // keep "short" name if full filemane not found
if( fname.IsEmpty() ) // keep "short" name if full filemane not found
fname = vrmlm->m_Shape3DName;
}
......@@ -1098,9 +1182,9 @@ static void export_vrml_module( BOARD* aPcb, MODULE* aModule,
* for footprints that are flipped
* When flipped, axis rotation is the horizontal axis (X axis)
*/
double rotx = - vrmlm->m_MatRotation.x;
double roty = - vrmlm->m_MatRotation.y;
double rotz = - vrmlm->m_MatRotation.z;
double rotx = -vrmlm->m_MatRotation.x;
double roty = -vrmlm->m_MatRotation.y;
double rotz = -vrmlm->m_MatRotation.z;
if( isFlipped )
{
......@@ -1138,21 +1222,21 @@ static void export_vrml_module( BOARD* aPcb, MODULE* aModule,
double offsetz = vrmlm->m_MatPosition.z * IU_PER_MILS * 1000.0;
if( isFlipped )
NEGATE(offsetz);
else // In normal mode, Y axis is reversed in Pcbnew.
NEGATE(offsety);
NEGATE( offsetz );
else // In normal mode, Y axis is reversed in Pcbnew.
NEGATE( offsety );
RotatePoint( &offsetx, &offsety, aModule->GetOrientation() );
fprintf( aOutputFile, " translation %g %g %g\n",
(offsetx + aModule->GetPosition().x) * boardIU2WRML,
- (offsety + aModule->GetPosition().y) * boardIU2WRML, // Y axis is reversed in Pcbnew
(offsetz + layer_z[aModule->GetLayer()]) * boardIU2WRML);
(offsetx + aModule->GetPosition().x) * aModel.scale + aModel.tx,
-(offsety + aModule->GetPosition().y) * aModel.scale - aModel.ty,
(offsetz * aModel.scale ) + aModel.GetLayerZ( aModule->GetLayer() ) );
fprintf( aOutputFile, " scale %g %g %g\n",
vrmlm->m_MatScale.x * aVRMLModelsToBiu,
vrmlm->m_MatScale.y * aVRMLModelsToBiu,
vrmlm->m_MatScale.z * aVRMLModelsToBiu );
vrmlm->m_MatScale.x * aVRMLModelsToBiu,
vrmlm->m_MatScale.y * aVRMLModelsToBiu,
vrmlm->m_MatScale.z * aVRMLModelsToBiu );
if( fname.EndsWith( wxT( "x3d" ) ) )
{
......@@ -1163,7 +1247,7 @@ static void export_vrml_module( BOARD* aPcb, MODULE* aModule,
// embed x3d model in vrml format
parser->Load( fname );
fprintf( aOutputFile,
" children [\n %s ]\n", TO_UTF8( parser->VRML_representation() ) );
" children [\n %s ]\n", TO_UTF8( parser->VRML_representation() ) );
fprintf( aOutputFile, " }\n" );
delete parser;
}
......@@ -1171,33 +1255,25 @@ static void export_vrml_module( BOARD* aPcb, MODULE* aModule,
else
{
fprintf( aOutputFile,
" children [\n Inline {\n url \"%s\"\n } ]\n",
TO_UTF8( fname ) );
" children [\n Inline {\n url \"%s\"\n } ]\n",
TO_UTF8( fname ) );
fprintf( aOutputFile, " }\n" );
}
}
}
static void write_and_empty_triangle_bag( FILE* output_file, TRIANGLEBAG& triangles,
int color, double boardIU2WRML )
bool PCB_EDIT_FRAME::ExportVRML_File( const wxString& aFullFileName,
double aMMtoWRMLunit, bool aExport3DFiles,
const wxString& a3D_Subdir )
{
if( !triangles.empty() )
{
write_triangle_bag( output_file, color, triangles, boardIU2WRML );
triangles.clear( );
}
}
wxString msg;
FILE* output_file;
BOARD* pcb = GetBoard();
MODEL_VRML model3d;
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();
VRMLEXPORT::model_vrml = &model3d;
output_file = wxFopen( aFullFileName, wxT( "wt" ) );
......@@ -1217,39 +1293,36 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString & aFullFileName,
" title \"%s - Generated by Pcbnew\"\n"
"}\n", TO_UTF8( name ) );
/* The would be in BIU and not in meters, as the standard wants.
* It is trivial to embed everything in a transform node to
* fix it. For example here we build the world in inches...
*/
// Global VRML scale to export to a different scale.
// (aMMtoWRMLScale = 1.0 to export in mm)
double boardIU2WRML = aMMtoWRMLunit / MM_PER_IU;
model3d.scale = aMMtoWRMLunit / MM_PER_IU;
// 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 );
fprintf( output_file, "Transform {\n" );
/* Define the translation to have the board centre to the 2D axis origin
* more easy for rotations...
*/
// 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();
double dx = boardIU2WRML * bbbox.Centre().x;
double dy = boardIU2WRML * bbbox.Centre().y;
model3d.SetOffset( -model3d.scale * bbbox.Centre().x, -model3d.scale * bbbox.Centre().y );
fprintf( output_file, " translation %g %g 0.0\n", -dx, dy );
fprintf( output_file, " children [\n" );
// Preliminary computation: the z value for each layer
compute_layer_Zs( pcb );
compute_layer_Zs( model3d, pcb );
// Drawing and text on the board, and edges which are special
export_vrml_drawings( pcb );
// board edges and cutouts
export_vrml_board( model3d, pcb );
// Drawing and text on the board
export_vrml_drawings( model3d, pcb );
// Export vias and trackage
export_vrml_tracks( pcb );
export_vrml_tracks( model3d, pcb );
// Export zone fills
/* TODO export_vrml_zones(pcb);
*/
export_vrml_zones( model3d, pcb);
/* scaling factor to convert 3D models to board units (decimils)
* Usually we use Wings3D to create thems.
......@@ -1261,25 +1334,12 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString & aFullFileName,
// Export footprints
for( MODULE* module = pcb->m_Modules; module != 0; module = module->Next() )
export_vrml_module( pcb, module, output_file,
wrml_3D_models_scaling_factor,
aExport3DFiles, a3D_Subdir,
boardIU2WRML );
/* Output the bagged triangles for each layer
* Each layer will be a separate shape */
for( LAYER_NUM layer = FIRST_LAYER; layer < NB_LAYERS; ++layer )
write_and_empty_triangle_bag( output_file,
layer_triangles[layer],
pcb->GetLayerColor(layer),
boardIU2WRML );
// Same thing for the via layers
for( int i = 0; i < 4; i++ )
write_and_empty_triangle_bag( output_file,
via_triangles[i],
pcb->GetVisibleElementColor( VIAS_VISIBLE + i ),
boardIU2WRML );
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 );
// Close the outer 'transform' node
fputs( "]\n}\n", output_file );
......@@ -1296,7 +1356,7 @@ bool PCB_EDIT_FRAME::ExportVRML_File( const wxString & aFullFileName,
* some characters cannot be used in filenames,
* this function change them to "_"
*/
static void ChangeIllegalCharacters( wxString & aFileName, bool aDirSepIsIllegal )
static void ChangeIllegalCharacters( wxString& aFileName, bool aDirSepIsIllegal )
{
if( aDirSepIsIllegal )
aFileName.Replace( wxT( "/" ), wxT( "_" ) );
......
/*
* 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 NULL;
}
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
struct 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
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