Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
K
kicad-source-mirror
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
Elphel
kicad-source-mirror
Commits
714d5b28
Commit
714d5b28
authored
Jan 06, 2014
by
jean-pierre charras
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Better vrml export, from Cirilo Bernardo.
parent
54bbba6c
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
2666 additions
and
725 deletions
+2666
-725
CMakeLists.txt
pcbnew/CMakeLists.txt
+1
-0
export_vrml.cpp
pcbnew/export_vrml.cpp
+785
-725
vrml_board.cpp
pcbnew/vrml_board.cpp
+1501
-0
vrml_board.h
pcbnew/vrml_board.h
+379
-0
No files found.
pcbnew/CMakeLists.txt
View file @
714d5b28
...
...
@@ -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
...
...
pcbnew/export_vrml.cpp
View file @
714d5b28
...
...
@@ -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
,
" diffus
eColor %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
);
" emissiv
eColor %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
)
{
V
Loop
loop
;
V
RML_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
/
18
0
;
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
.
t
x
;
double
y
=
drawseg
->
GetStart
().
y
*
aModel
.
scale
+
aModel
.
t
y
;
double
xf
=
drawseg
->
GetEnd
().
x
*
aModel
.
scale
+
aModel
.
t
x
;
double
yf
=
drawseg
->
GetEnd
().
y
*
aModel
.
scale
+
aModel
.
t
y
;
// Items on the edge layer are h
igh, not thick
// Items on the edge layer are h
andled 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:
\n
Could 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:
\n
Could 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
.
t
x
;
y
=
via
->
GetStart
().
y
*
aModel
.
scale
+
aModel
.
t
y
;
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
.
t
x
;
double
y
=
aOutline
->
GetStart
().
y
*
aModel
.
scale
+
aModel
.
t
y
;
double
xf
=
aOutline
->
GetEnd
().
x
*
aModel
.
scale
+
aModel
.
t
x
;
double
yf
=
aOutline
->
GetEnd
().
y
*
aModel
.
scale
+
aModel
.
t
y
;
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
(
a
Model
,
a
Pcb
,
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
(
"_"
)
);
...
...
pcbnew/vrml_board.cpp
0 → 100644
View file @
714d5b28
/*
* 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
;
}
pcbnew/vrml_board.h
0 → 100644
View file @
714d5b28
/*
* 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
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment