our
$VERSION
=
'0.14'
;
our
$Math
=new CAD::OpenSCAD::Math;
class CAD::OpenSCAD::Loft{
field
$scad
:param;
method loftSolid{
my
(
$name
,
$face1
,
$face2
)=
@_
;
my
$pts
=[
@$face1
,
@$face2
];
my
$faces
=[[
reverse
(0..
$#$face1
)],@{
$self
->loftShell(
$face1
,
$face2
)},[
$#$face1
+1..
$#$face1
*2+1]];
if
(
$name
){
$scad
->polyhedron(
$name
,{
points
=>
$pts
,
faces
=>
$faces
});
return
$scad
}
return
{
points
=>
$pts
,
faces
=>
$faces
}
}
method helix{
my
$name
=
shift
;
my
(
$profile
,
$radius
,
$steps
,
$turns
,
$verticalShift
,
$radialShift
);
unless
(
ref
$_
[0] eq
"HASH"
){
(
$profile
,
$radius
,
$steps
,
$turns
,
$verticalShift
,
$radialShift
)=
@_
;
}
else
{
my
$params
=
$_
[0];
(
$profile
,
$radius
,
$steps
,
$turns
,
$verticalShift
,
$radialShift
)=
map
{
$params
->{
$_
}}(
qw/profile radius steps turns verticalShift radialShift/
);
}
my
$face1
=[];
push
@$face1
,[0,
$_
->[0],
$_
->[1]]
foreach
(
@$profile
);
$face1
=
$Math
->add(
$face1
,[0,
$radius
,0]);
my
$faces
=[[
reverse
(0..
$#$face1
)]];
my
$points
=[
@$face1
];
my
$index
=0;
for
(0..
$turns
*$steps
){
my
$face2
=
$Math
->rotz(
$face1
,
$Math
->deg2rad(-360/
$steps
));
$face2
=
$Math
->add(
$face2
,[
$radialShift
*sin
(
$_
*2
*$Math
->pi/
$steps
),
$radialShift
*cos
(
$_
*2
*$Math
->pi/
$steps
),
$verticalShift
]);
push
@$points
,
@$face2
;
push
@$faces
, @{
$self
->loftShell(
$face1
,
$face2
,
$index
)};
$face1
=[
@$face2
];
$index
+=
scalar
@$face1
;
}
push
@$faces
,[
$#$faces
..
$#$faces
+
scalar
@$face1
];
if
(
$name
){
$scad
->polyhedron(
$name
,{
points
=>
$points
,
faces
=>
$faces
});
}
return
{
points
=>
$points
,
faces
=>
$faces
}
}
method spheroid{
my
$name
=
shift
;
my
(
$sides
,
$radius
);
unless
(
ref
$_
[0]){
(
$sides
,
$radius
)=
@_
;
}
else
{
my
$params
=
$_
[0];
(
$sides
,
$radius
)=
map
{
$params
->{
$_
}}(
qw/sides radius/
);
}
my
$angle
=2
*$Math
->pi/
$sides
;
my
$pts
=[];
my
$layers
=[];
my
$faces
;
my
$index
=0;
my
$start
=
$sides
/4+1;
my
$end
=3
*$sides
/4 -(
$sides
%4?0:1);
for
my
$lat
(
$start
..
$end
){
my
$layer
=[];
for
my
$long
(0..
$sides
-1){
unshift
@$layer
,[
$radius
*cos
(
$lat
*$angle
)
*sin
(
$long
*$angle
),
$radius
*cos
(
$lat
*$angle
)
*cos
(
$long
*$angle
),
$radius
*sin
(
$lat
*$angle
)]
}
push
@$pts
,
@$layer
;
push
@$layers
,[
@$layer
];
if
( 1 ==
@$layers
){
push
@$faces
,[
reverse
(0..
$#$layer
)]; #top polygon
}
else
{
push
@$faces
,@{
$self
->loftShell(
$layers
->[-2],
$layers
->[-1],
$index
)};
$index
+=
scalar
@{
$layers
->[-2]};
}
}
push
@$faces
,[
$#$pts
-
$sides
+1..
$#$pts
];# bottom polygon;
if
(
$name
){
$scad
->polyhedron(
$name
,{
points
=>
$pts
,
faces
=>
$faces
});
return
$scad
}
return
{
points
=>
$pts
,
faces
=>
$faces
};
}
method regularPolygon{
my
$name
=
shift
;
my
(
$sides
,
$radius
);
unless
(
ref
$_
[0]){
(
$sides
,
$radius
)=
@_
;
}
else
{
my
$params
=
$_
[0];
(
$sides
,
$radius
)=
map
{
$params
->{
$_
}}(
qw/sides radius/
);
}
my
$angle
=2
*$Math
->pi/
$sides
;
my
$pts
=[];
for
(0..
$sides
-1){
push
@$pts
,[
$radius
*sin
(
$_
*$angle
),
$radius
*cos
(
$_
*$angle
)]
}
if
(
$name
) {
$scad
->polygon(
$name
,
$pts
);
return
$scad
;
}
return
$pts
;
}
method loftShell{
my
(
$face1
,
$face2
,
$index
)=
@_
;
$index
//=0;
my
$loftFaces
=[];
my
@indices
=(
$index
..
$#$face1
+
$index
,
@$face1
+
$index
..
@$face1
+
$#$face2
+
$index
);
my
$diff
=
abs
(
@$face2
-
@$face1
);
if
(
$diff
){
my
$steps
=
@$face2
>
@$face1
?
$#$face1
/
$diff
:
$#$face2
/
$diff
; # smaller array needs to be padded;
my
$start
=(
@$face2
>
@$face1
?
$#$face1
:
$#indices
)-
$steps
/2;
for
(1..
$diff
){
splice
(
@indices
,
$start
,0,
$indices
[
$start
]);
$start
-=
$steps
;
}
}
foreach
(0..
@indices
/2-2){
my
$face
=[
$indices
[
$_
],
$indices
[
$_
+1],
$indices
[(
@indices
/2)+1+
$_
],
$indices
[(
@indices
/2)+
$_
]];
push
@$loftFaces
,
$face
;
}
push
@$loftFaces
,[
$indices
[
@indices
/2-1],
$indices
[0],
$indices
[
@indices
/2],
$indices
[-1]];
return
$loftFaces
;
}
method profilePlane{
my
$profile
=
shift
;
my
$plane
=
shift
;
for
(
$plane
){
~/0|x/i &&
do
{
$profile
=
map
{[0,
$_
[0],
$_
[1]]}
@$profile
;
last
;
};
~/1|y/i &&
do
{
$profile
=
map
{[
$_
[0],0,
$_
[1]]}
@$profile
;
last
;
};
~/2|z/i &&
do
{
$profile
=
map
{[
$_
[0],
$_
[1],0]}
@$profile
;
last
;
};
die
"Unrecognised plane"
}
return
$profile
;
}
method conoid{
my
$name
=
shift
;
my
(
$apex
,
$sides
,
$radius
);
unless
(
ref
$_
[0] eq
"HASH"
){
(
$apex
,
$sides
,
$radius
)=
@_
;
}
else
{
my
$params
=
$_
[0];
(
$sides
,
$radius
)=
map
{
$params
->{
$_
}}(
qw/apex sides radius/
);
}
my
$profile
=
$self
->regularPolygon(
undef
,
$sides
,
$radius
);
my
$pts
=[
$apex
,
$profile
->[-1]];
my
$faces
=[];
foreach
(0..
$#$profile
){
push
@$faces
,[0,
$_
+1,
$_
+2];
push
@$pts
,
$profile
->[
$_
];
}
push
@$faces
,([0,
$#$profile
+2,1],[
reverse
(1..
$sides
+1)]);
$faces
=
$self
->reverseFaces(
$faces
)
if
$apex
->[2]<0;
$scad
->polyhedron(
$name
,{
points
=>
$pts
,
faces
=>
$faces
});
}
method reverseFaces{
my
$faces
=
shift
;
return
[
map
{[
reverse
@$_
]}
@$faces
]
}
method arc{
my
$name
=
shift
;
my
(
$profile
,
$radius
,
$steps
,
$angle
);
unless
(
ref
$_
[0] eq
"HASH"
){
(
$profile
,
$radius
,
$steps
,
$angle
)=
@_
;
}
else
{
my
$params
=
$_
[0];
(
$profile
,
$radius
,
$steps
,
$angle
)=
map
{
$params
->{
$_
}}(
qw/profile radius steps angle/
);
}
my
$face1
=[];
my
$faces
=[[
reverse
(0..
$#$face1
)]];
push
@$face1
,[0,
$_
->[0],
$_
->[1]]
foreach
(
@$profile
);
$face1
=
$Math
->add(
$face1
,[0,
$radius
,0]);
my
$stepAngle
=
$Math
->deg2rad(
$angle
/
$steps
);
my
$points
=[
@$face1
];
my
$index
=0;
for
(0..
$steps
-1){
my
$face2
=
$Math
->rotz(
$face1
,
$stepAngle
);
push
@$points
,
@$face2
;
push
@$faces
, @{
$self
->loftShell(
$face1
,
$face2
,
$index
)};
$face1
=[
@$face2
];
$index
+=
scalar
@$face1
;
}
push
@$faces
,[
$#$faces
..
$#$faces
+
scalar
@$face1
];
if
(
$name
){
$scad
->polyhedron(
$name
,{
points
=>
$points
,
faces
=>
$faces
});
}
return
{
points
=>
$points
,
faces
=>
$faces
}
}
}