2017-08-30 00:00:16 +00:00
//////////////////////////////////////////////////////////////////////
2021-09-20 03:08:38 +00:00
// LibFile: shapes3d.scad
2021-09-20 22:34:22 +00:00
// Some standard modules for making 3d shapes with attachment support, and function forms
// that produce a VNF. Also included are shortcuts cylinders in each orientation and extended versions of
// the standard modules that provide roundovers and chamfers. The sphereoid() module provides
// several different ways to make a sphere, and the text modules let you write text on a path
// so you can place it on a curved object.
2021-01-05 09:20:01 +00:00
// Includes:
2019-04-19 07:25:10 +00:00
// include <BOSL2/std.scad>
2017-08-30 00:00:16 +00:00
//////////////////////////////////////////////////////////////////////
2021-09-17 01:56:56 +00:00
// Section: Cuboids, Prismoids and Pyramids
2017-08-30 00:00:16 +00:00
2021-09-17 01:50:12 +00:00
// Function&Module: cube()
// Topics: Shapes (3D), Attachable, VNF Generators
// Usage: As Module
// cube(size, [center], ...);
// Usage: With Attachments
// cube(size, [center], ...) { attachments }
// Usage: As Function
// vnf = cube(size, [center], ...);
// See Also: cuboid(), prismoid()
// Description:
// Creates a 3D cubic object with support for anchoring and attachments.
// This can be used as a drop-in replacement for the built-in `cube()` module.
// When called as a function, returns a [VNF](vnf.scad) for a cube.
// Arguments:
// size = The size of the cube.
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=ALLNEG`.
// ---
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
// Example: Simple cube.
// cube(40);
// Example: Rectangular cube.
// cube([20,40,50]);
// Example: Anchoring.
// cube([20,40,50], anchor=BOTTOM+FRONT);
// Example: Spin.
// cube([20,40,50], anchor=BOTTOM+FRONT, spin=30);
// Example: Orientation.
// cube([20,40,50], anchor=BOTTOM+FRONT, spin=30, orient=FWD);
// Example: Standard Connectors.
// cube(40, center=true) show_anchors();
// Example: Called as Function
// vnf = cube([20,40,50]);
// vnf_polyhedron(vnf);
module cube ( size = 1 , center , anchor , spin = 0 , orient = UP )
{
anchor = get_anchor ( anchor , center , ALLNEG , ALLNEG ) ;
size = scalar_vec3 ( size ) ;
attachable ( anchor , spin , orient , size = size ) {
if ( size . z > 0 ) {
linear_extrude ( height = size . z , center = true , convexity = 2 ) {
square ( [ size . x , size . y ] , center = true ) ;
}
}
children ( ) ;
}
}
function cube ( size = 1 , center , anchor , spin = 0 , orient = UP ) =
let (
siz = scalar_vec3 ( size ) ,
anchor = get_anchor ( anchor , center , ALLNEG , ALLNEG ) ,
unscaled = [
[ - 1 , - 1 , - 1 ] , [ 1 , - 1 , - 1 ] , [ 1 , 1 , - 1 ] , [ - 1 , 1 , - 1 ] ,
[ - 1 , - 1 , 1 ] , [ 1 , - 1 , 1 ] , [ 1 , 1 , 1 ] , [ - 1 , 1 , 1 ] ,
] / 2 ,
verts = is_num ( size ) ? unscaled * size :
is_vector ( size , 3 ) ? [ for ( p = unscaled ) v_mul ( p , size ) ] :
assert ( is_num ( size ) || is_vector ( size , 3 ) ) ,
faces = [
[ 0 , 1 , 2 ] , [ 0 , 2 , 3 ] , //BOTTOM
[ 0 , 4 , 5 ] , [ 0 , 5 , 1 ] , //FRONT
[ 1 , 5 , 6 ] , [ 1 , 6 , 2 ] , //RIGHT
[ 2 , 6 , 7 ] , [ 2 , 7 , 3 ] , //BACK
[ 3 , 7 , 4 ] , [ 3 , 4 , 0 ] , //LEFT
[ 6 , 4 , 7 ] , [ 6 , 5 , 4 ] //TOP
]
) [ reorient ( anchor , spin , orient , size = siz , p = verts ) , faces ] ;
2019-03-23 04:13:18 +00:00
// Module: cuboid()
//
2021-01-17 09:38:10 +00:00
// Usage: Standard Cubes
2021-06-27 03:59:33 +00:00
// cuboid(size, [anchor=], [spin=], [orient=]);
2021-01-17 09:38:10 +00:00
// cuboid(size, p1=, ...);
// cuboid(p1=, p2=, ...);
// Usage: Chamfered Cubes
2021-06-27 03:59:33 +00:00
// cuboid(size, [chamfer=], [edges=], [except_edges=], [trimcorners=], ...);
2021-01-17 09:38:10 +00:00
// Usage: Rounded Cubes
2021-06-27 03:59:33 +00:00
// cuboid(size, [rounding=], [edges=], [except_edges=], [trimcorners=], ...);
2021-01-17 09:38:10 +00:00
// Usage: Attaching children
2021-06-27 03:59:33 +00:00
// cuboid(size, [anchor=], ...) [attachments];
2021-01-17 09:38:10 +00:00
//
2019-03-23 04:13:18 +00:00
// Description:
2019-04-25 02:42:38 +00:00
// Creates a cube or cuboid object, with optional chamfering or rounding.
2019-06-12 23:52:26 +00:00
// Negative chamfers and roundings can be applied to create external masks,
// but only apply to edges around the top or bottom faces.
2019-03-23 04:13:18 +00:00
//
// Arguments:
2019-02-27 11:56:34 +00:00
// size = The size of the cube.
2021-01-17 09:38:10 +00:00
// ---
2020-04-07 01:53:12 +00:00
// chamfer = Size of chamfer, inset from sides. Default: No chamfering.
2019-04-25 02:42:38 +00:00
// rounding = Radius of the edge rounding. Default: No rounding.
2019-11-12 07:38:27 +00:00
// edges = Edges to chamfer/round. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: All edges.
// except_edges = Edges to explicitly NOT chamfer/round. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: No edges.
2020-04-07 01:53:12 +00:00
// trimcorners = If true, rounds or chamfers corners where three chamfered/rounded edges meet. Default: `true`
2019-04-23 03:55:03 +00:00
// p1 = Align the cuboid's corner at `p1`, if given. Forces `anchor=ALLNEG`.
2019-03-23 04:13:18 +00:00
// p2 = If given with `p1`, defines the cornerpoints of the cuboid.
2019-05-26 19:47:50 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP`
2019-03-23 04:13:18 +00:00
//
// Example: Simple regular cube.
2019-02-27 11:56:34 +00:00
// cuboid(40);
2019-03-23 04:13:18 +00:00
// Example: Cube with minimum cornerpoint given.
// cuboid(20, p1=[10,0,0]);
// Example: Rectangular cube, with given X, Y, and Z sizes.
// cuboid([20,40,50]);
2019-06-12 23:52:26 +00:00
// Example: Cube by Opposing Corners.
2019-03-23 04:13:18 +00:00
// cuboid(p1=[0,10,0], p2=[20,30,30]);
2019-06-12 23:52:26 +00:00
// Example: Chamferred Edges and Corners.
2019-03-23 04:13:18 +00:00
// cuboid([30,40,50], chamfer=5);
2019-06-12 23:52:26 +00:00
// Example: Chamferred Edges, Untrimmed Corners.
2019-03-23 04:13:18 +00:00
// cuboid([30,40,50], chamfer=5, trimcorners=false);
2019-06-12 23:52:26 +00:00
// Example: Rounded Edges and Corners
2019-04-25 02:42:38 +00:00
// cuboid([30,40,50], rounding=10);
2019-06-12 23:52:26 +00:00
// Example: Rounded Edges, Untrimmed Corners
2019-04-25 02:42:38 +00:00
// cuboid([30,40,50], rounding=10, trimcorners=false);
2019-06-12 23:52:26 +00:00
// Example: Chamferring Selected Edges
2021-05-22 08:41:25 +00:00
// cuboid(
// [30,40,50], chamfer=5,
// edges=[TOP+FRONT,TOP+RIGHT,FRONT+RIGHT],
// $fn=24
// );
2019-06-12 23:52:26 +00:00
// Example: Rounding Selected Edges
2021-05-22 08:41:25 +00:00
// cuboid(
// [30,40,50], rounding=5,
// edges=[TOP+FRONT,TOP+RIGHT,FRONT+RIGHT],
// $fn=24
// );
2019-06-12 23:52:26 +00:00
// Example: Negative Chamferring
2021-05-22 08:41:25 +00:00
// cuboid(
// [30,40,50], chamfer=-5,
// edges=[TOP,BOT], except_edges=RIGHT,
// $fn=24
// );
2019-06-12 23:52:26 +00:00
// Example: Negative Chamferring, Untrimmed Corners
2021-05-22 08:41:25 +00:00
// cuboid(
// [30,40,50], chamfer=-5,
// edges=[TOP,BOT], except_edges=RIGHT,
// trimcorners=false, $fn=24
// );
2019-06-12 23:52:26 +00:00
// Example: Negative Rounding
2021-05-22 08:41:25 +00:00
// cuboid(
// [30,40,50], rounding=-5,
// edges=[TOP,BOT], except_edges=RIGHT,
// $fn=24
// );
2019-06-12 23:52:26 +00:00
// Example: Negative Rounding, Untrimmed Corners
2021-05-22 08:41:25 +00:00
// cuboid(
// [30,40,50], rounding=-5,
// edges=[TOP,BOT], except_edges=RIGHT,
// trimcorners=false, $fn=24
// );
2019-04-22 08:08:41 +00:00
// Example: Standard Connectors
2019-05-13 01:08:41 +00:00
// cuboid(40) show_anchors();
2019-02-27 11:56:34 +00:00
module cuboid (
2020-05-30 02:04:34 +00:00
size = [ 1 , 1 , 1 ] ,
2020-10-05 04:07:16 +00:00
p1 , p2 ,
chamfer ,
rounding ,
2020-05-30 02:04:34 +00:00
edges = EDGES_ALL ,
except_edges = [ ] ,
trimcorners = true ,
anchor = CENTER ,
spin = 0 ,
orient = UP
2019-02-27 11:56:34 +00:00
) {
2020-10-05 04:07:16 +00:00
module corner_shape ( corner ) {
2021-09-18 00:02:31 +00:00
e = _corner_edges ( edges , corner ) ;
2020-10-05 04:07:16 +00:00
cnt = sum ( e ) ;
r = first_defined ( [ chamfer , rounding , 0 ] ) ;
2020-10-09 17:31:56 +00:00
c = [ min ( r , size . x / 2 ) , min ( r , size . y / 2 ) , min ( r , size . z / 2 ) ] ;
2021-06-15 03:28:49 +00:00
c2 = v_mul ( corner , c / 2 ) ;
2020-10-05 04:07:16 +00:00
$fn = is_finite ( chamfer ) ? 4 : segs ( r ) ;
2021-06-15 03:28:49 +00:00
translate ( v_mul ( corner , size / 2 - c ) ) {
2020-11-30 03:21:35 +00:00
if ( cnt = = 0 || approx ( r , 0 ) ) {
2020-10-29 23:46:07 +00:00
translate ( c2 ) cube ( c , center = true ) ;
2020-10-05 04:07:16 +00:00
} else if ( cnt = = 1 ) {
2020-10-29 23:46:07 +00:00
if ( e . x ) right ( c2 . x ) xcyl ( l = c . x , r = r ) ;
if ( e . y ) back ( c2 . y ) ycyl ( l = c . y , r = r ) ;
if ( e . z ) up ( c2 . z ) zcyl ( l = c . z , r = r ) ;
2020-10-05 04:07:16 +00:00
} else if ( cnt = = 2 ) {
if ( ! e . x ) {
intersection ( ) {
2020-10-09 17:31:56 +00:00
ycyl ( l = c . y * 2 , r = r ) ;
zcyl ( l = c . z * 2 , r = r ) ;
2020-10-05 04:07:16 +00:00
}
} else if ( ! e . y ) {
intersection ( ) {
2020-10-09 17:31:56 +00:00
xcyl ( l = c . x * 2 , r = r ) ;
zcyl ( l = c . z * 2 , r = r ) ;
2020-10-05 04:07:16 +00:00
}
} else {
intersection ( ) {
2020-10-09 17:31:56 +00:00
xcyl ( l = c . x * 2 , r = r ) ;
ycyl ( l = c . y * 2 , r = r ) ;
2020-10-05 04:07:16 +00:00
}
}
} else {
if ( trimcorners ) {
spheroid ( r = r , style = "octa" ) ;
} else {
intersection ( ) {
2020-10-09 17:31:56 +00:00
xcyl ( l = c . x * 2 , r = r ) ;
ycyl ( l = c . y * 2 , r = r ) ;
zcyl ( l = c . z * 2 , r = r ) ;
2020-10-05 04:07:16 +00:00
}
}
}
}
}
2020-05-30 02:04:34 +00:00
size = scalar_vec3 ( size ) ;
edges = edges ( edges , except = except_edges ) ;
2020-11-30 03:21:35 +00:00
assert ( is_vector ( size , 3 ) ) ;
2021-05-27 03:07:05 +00:00
assert ( all_positive ( size ) ) ;
2020-11-30 03:21:35 +00:00
assert ( is_undef ( chamfer ) || is_finite ( chamfer ) ) ;
assert ( is_undef ( rounding ) || is_finite ( rounding ) ) ;
assert ( is_undef ( p1 ) || is_vector ( p1 ) ) ;
assert ( is_undef ( p2 ) || is_vector ( p2 ) ) ;
assert ( is_bool ( trimcorners ) ) ;
2020-05-30 02:04:34 +00:00
if ( ! is_undef ( p1 ) ) {
if ( ! is_undef ( p2 ) ) {
translate ( pointlist_bounds ( [ p1 , p2 ] ) [ 0 ] ) {
2021-06-15 03:28:49 +00:00
cuboid ( size = v_abs ( p2 - p1 ) , chamfer = chamfer , rounding = rounding , edges = edges , trimcorners = trimcorners , anchor = ALLNEG ) children ( ) ;
2020-05-30 02:04:34 +00:00
}
} else {
translate ( p1 ) {
cuboid ( size = size , chamfer = chamfer , rounding = rounding , edges = edges , trimcorners = trimcorners , anchor = ALLNEG ) children ( ) ;
}
}
} else {
2020-11-30 03:21:35 +00:00
if ( is_finite ( chamfer ) ) {
2020-05-30 02:04:34 +00:00
if ( any ( edges [ 0 ] ) ) assert ( chamfer < = size . y / 2 && chamfer < = size . z / 2 , "chamfer must be smaller than half the cube length or height." ) ;
if ( any ( edges [ 1 ] ) ) assert ( chamfer < = size . x / 2 && chamfer < = size . z / 2 , "chamfer must be smaller than half the cube width or height." ) ;
if ( any ( edges [ 2 ] ) ) assert ( chamfer < = size . x / 2 && chamfer < = size . y / 2 , "chamfer must be smaller than half the cube width or length." ) ;
}
2020-11-30 03:21:35 +00:00
if ( is_finite ( rounding ) ) {
2020-05-30 02:04:34 +00:00
if ( any ( edges [ 0 ] ) ) assert ( rounding < = size . y / 2 && rounding < = size . z / 2 , "rounding radius must be smaller than half the cube length or height." ) ;
if ( any ( edges [ 1 ] ) ) assert ( rounding < = size . x / 2 && rounding < = size . z / 2 , "rounding radius must be smaller than half the cube width or height." ) ;
if ( any ( edges [ 2 ] ) ) assert ( rounding < = size . x / 2 && rounding < = size . y / 2 , "rounding radius must be smaller than half the cube width or length." ) ;
}
majrots = [ [ 0 , 90 , 0 ] , [ 90 , 0 , 0 ] , [ 0 , 0 , 0 ] ] ;
attachable ( anchor , spin , orient , size = size ) {
2020-11-30 03:21:35 +00:00
if ( is_finite ( chamfer ) && ! approx ( chamfer , 0 ) ) {
2020-05-30 02:04:34 +00:00
if ( edges = = EDGES_ALL && trimcorners ) {
if ( chamfer < 0 ) {
cube ( size , center = true ) {
2020-08-17 06:09:59 +00:00
attach ( TOP , overlap = 0 ) prismoid ( [ size . x , size . y ] , [ size . x - 2 * chamfer , size . y - 2 * chamfer ] , h = - chamfer , anchor = TOP ) ;
attach ( BOT , overlap = 0 ) prismoid ( [ size . x , size . y ] , [ size . x - 2 * chamfer , size . y - 2 * chamfer ] , h = - chamfer , anchor = TOP ) ;
2020-05-30 02:04:34 +00:00
}
} else {
isize = [ for ( v = size ) max ( 0.001 , v - 2 * chamfer ) ] ;
hull ( ) {
2020-10-29 23:46:07 +00:00
cube ( [ size . x , isize . y , isize . z ] , center = true ) ;
cube ( [ isize . x , size . y , isize . z ] , center = true ) ;
cube ( [ isize . x , isize . y , size . z ] , center = true ) ;
2020-05-30 02:04:34 +00:00
}
}
} else if ( chamfer < 0 ) {
2021-05-07 23:53:44 +00:00
assert ( edges = = EDGES_ALL || edges [ 2 ] = = [ 0 , 0 , 0 , 0 ] , "Cannot use negative chamfer with Z aligned edges." ) ;
2020-05-30 02:04:34 +00:00
ach = abs ( chamfer ) ;
cube ( size , center = true ) ;
// External-Chamfer mask edges
difference ( ) {
union ( ) {
for ( i = [ 0 : 3 ] , axis = [ 0 : 1 ] ) {
if ( edges [ axis ] [ i ] > 0 ) {
vec = EDGE_OFFSETS [ axis ] [ i ] ;
2021-06-15 03:28:49 +00:00
translate ( v_mul ( vec / 2 , size + [ ach , ach , - ach ] ) ) {
2020-05-30 02:04:34 +00:00
rotate ( majrots [ axis ] ) {
cube ( [ ach , ach , size [ axis ] ] , center = true ) ;
}
}
}
}
// Add multi-edge corners.
if ( trimcorners ) {
for ( za = [ - 1 , 1 ] , ya = [ - 1 , 1 ] , xa = [ - 1 , 1 ] ) {
2021-09-18 00:02:31 +00:00
ce = _corner_edges ( edges , [ xa , ya , za ] ) ;
2021-01-02 00:31:15 +00:00
if ( ce . x + ce . y > 1 ) {
2021-06-15 03:28:49 +00:00
translate ( v_mul ( [ xa , ya , za ] / 2 , size + [ ach - 0.01 , ach - 0.01 , - ach ] ) ) {
2020-05-30 02:04:34 +00:00
cube ( [ ach + 0.01 , ach + 0.01 , ach ] , center = true ) ;
}
}
}
}
}
// Remove bevels from overhangs.
for ( i = [ 0 : 3 ] , axis = [ 0 : 1 ] ) {
if ( edges [ axis ] [ i ] > 0 ) {
vec = EDGE_OFFSETS [ axis ] [ i ] ;
2021-06-15 03:28:49 +00:00
translate ( v_mul ( vec / 2 , size + [ 2 * ach , 2 * ach , - 2 * ach ] ) ) {
2020-05-30 02:04:34 +00:00
rotate ( majrots [ axis ] ) {
zrot ( 45 ) cube ( [ ach * sqrt ( 2 ) , ach * sqrt ( 2 ) , size [ axis ] + 2.1 * ach ] , center = true ) ;
}
}
}
}
}
} else {
2020-10-05 04:07:16 +00:00
hull ( ) {
corner_shape ( [ - 1 , - 1 , - 1 ] ) ;
corner_shape ( [ 1 , - 1 , - 1 ] ) ;
corner_shape ( [ - 1 , 1 , - 1 ] ) ;
corner_shape ( [ 1 , 1 , - 1 ] ) ;
corner_shape ( [ - 1 , - 1 , 1 ] ) ;
corner_shape ( [ 1 , - 1 , 1 ] ) ;
corner_shape ( [ - 1 , 1 , 1 ] ) ;
corner_shape ( [ 1 , 1 , 1 ] ) ;
2020-05-30 02:04:34 +00:00
}
}
2020-11-30 03:21:35 +00:00
} else if ( is_finite ( rounding ) && ! approx ( rounding , 0 ) ) {
2020-05-30 02:04:34 +00:00
sides = quantup ( segs ( rounding ) , 4 ) ;
if ( edges = = EDGES_ALL ) {
if ( rounding < 0 ) {
cube ( size , center = true ) ;
zflip_copy ( ) {
up ( size . z / 2 ) {
difference ( ) {
down ( - rounding / 2 ) cube ( [ size . x - 2 * rounding , size . y - 2 * rounding , - rounding ] , center = true ) ;
down ( - rounding ) {
ycopies ( size . y - 2 * rounding ) xcyl ( l = size . x - 3 * rounding , r = - rounding ) ;
xcopies ( size . x - 2 * rounding ) ycyl ( l = size . y - 3 * rounding , r = - rounding ) ;
}
}
}
}
} else {
isize = [ for ( v = size ) max ( 0.001 , v - 2 * rounding ) ] ;
minkowski ( ) {
cube ( isize , center = true ) ;
if ( trimcorners ) {
2020-06-23 07:37:36 +00:00
spheroid ( r = rounding , style = "octa" , $fn = sides ) ;
2020-05-30 02:04:34 +00:00
} else {
intersection ( ) {
cyl ( r = rounding , h = rounding * 2 , $fn = sides ) ;
rotate ( [ 90 , 0 , 0 ] ) cyl ( r = rounding , h = rounding * 2 , $fn = sides ) ;
rotate ( [ 0 , 90 , 0 ] ) cyl ( r = rounding , h = rounding * 2 , $fn = sides ) ;
}
}
}
}
} else if ( rounding < 0 ) {
2021-05-07 23:53:44 +00:00
assert ( edges = = EDGES_ALL || edges [ 2 ] = = [ 0 , 0 , 0 , 0 ] , "Cannot use negative rounding with Z aligned edges." ) ;
2020-05-30 02:04:34 +00:00
ard = abs ( rounding ) ;
cube ( size , center = true ) ;
2020-09-22 07:24:39 +00:00
// External-Rounding mask edges
2020-05-30 02:04:34 +00:00
difference ( ) {
union ( ) {
for ( i = [ 0 : 3 ] , axis = [ 0 : 1 ] ) {
if ( edges [ axis ] [ i ] > 0 ) {
vec = EDGE_OFFSETS [ axis ] [ i ] ;
2021-06-15 03:28:49 +00:00
translate ( v_mul ( vec / 2 , size + [ ard , ard , - ard ] ) ) {
2020-05-30 02:04:34 +00:00
rotate ( majrots [ axis ] ) {
cube ( [ ard , ard , size [ axis ] ] , center = true ) ;
}
}
}
}
// Add multi-edge corners.
if ( trimcorners ) {
for ( za = [ - 1 , 1 ] , ya = [ - 1 , 1 ] , xa = [ - 1 , 1 ] ) {
2021-09-18 00:02:31 +00:00
ce = _corner_edges ( edges , [ xa , ya , za ] ) ;
2021-01-02 00:31:15 +00:00
if ( ce . x + ce . y > 1 ) {
2021-06-15 03:28:49 +00:00
translate ( v_mul ( [ xa , ya , za ] / 2 , size + [ ard - 0.01 , ard - 0.01 , - ard ] ) ) {
2020-05-30 02:04:34 +00:00
cube ( [ ard + 0.01 , ard + 0.01 , ard ] , center = true ) ;
}
}
}
}
}
// Remove roundings from overhangs.
for ( i = [ 0 : 3 ] , axis = [ 0 : 1 ] ) {
if ( edges [ axis ] [ i ] > 0 ) {
vec = EDGE_OFFSETS [ axis ] [ i ] ;
2021-06-15 03:28:49 +00:00
translate ( v_mul ( vec / 2 , size + [ 2 * ard , 2 * ard , - 2 * ard ] ) ) {
2020-05-30 02:04:34 +00:00
rotate ( majrots [ axis ] ) {
cyl ( l = size [ axis ] + 2.1 * ard , r = ard ) ;
}
}
}
}
}
} else {
2020-10-05 04:07:16 +00:00
hull ( ) {
corner_shape ( [ - 1 , - 1 , - 1 ] ) ;
corner_shape ( [ 1 , - 1 , - 1 ] ) ;
corner_shape ( [ - 1 , 1 , - 1 ] ) ;
corner_shape ( [ 1 , 1 , - 1 ] ) ;
corner_shape ( [ - 1 , - 1 , 1 ] ) ;
corner_shape ( [ 1 , - 1 , 1 ] ) ;
corner_shape ( [ - 1 , 1 , 1 ] ) ;
corner_shape ( [ 1 , 1 , 1 ] ) ;
2020-05-30 02:04:34 +00:00
}
}
} else {
cube ( size = size , center = true ) ;
}
children ( ) ;
}
}
2019-01-29 12:29:42 +00:00
}
2021-05-07 23:53:44 +00:00
2021-01-17 09:38:10 +00:00
function cuboid (
size = [ 1 , 1 , 1 ] ,
p1 , p2 ,
chamfer ,
rounding ,
edges = EDGES_ALL ,
except_edges = [ ] ,
trimcorners = true ,
anchor = CENTER ,
spin = 0 ,
orient = UP
) = no_function ( "cuboid" ) ;
2019-01-29 12:29:42 +00:00
2019-02-27 11:56:34 +00:00
2020-05-22 07:14:41 +00:00
// Function&Module: prismoid()
2019-03-23 04:13:18 +00:00
//
2021-01-17 09:38:10 +00:00
// Usage: Typical Prismoids
2021-06-27 03:59:33 +00:00
// prismoid(size1, size2, h|l, [shift], ...);
2021-01-17 09:38:10 +00:00
// Usage: Attaching Children
2021-06-27 03:59:33 +00:00
// prismoid(size1, size2, h|l, [shift], ...) [attachments];
2021-01-17 09:38:10 +00:00
// Usage: Chamfered Prismoids
2021-06-27 03:59:33 +00:00
// prismoid(size1, size2, h|l, [chamfer=], ...);
// prismoid(size1, size2, h|l, [chamfer1=], [chamfer2=], ...);
2021-01-17 09:38:10 +00:00
// Usage: Rounded Prismoids
2021-06-27 03:59:33 +00:00
// prismoid(size1, size2, h|l, [rounding=], ...);
// prismoid(size1, size2, h|l, [rounding1=], [rounding2=], ...);
2020-05-22 07:14:41 +00:00
// Usage: As Function
2021-06-27 03:59:33 +00:00
// vnf = prismoid(size1, size2, h|l, [shift], [rounding], [chamfer]);
// vnf = prismoid(size1, size2, h|l, [shift], [rounding1], [rounding2], [chamfer1], [chamfer2]);
2019-03-23 04:13:18 +00:00
//
2020-05-22 07:14:41 +00:00
// Description:
// Creates a rectangular prismoid shape with optional roundovers and chamfering.
// You can only round or chamfer the vertical(ish) edges. For those edges, you can
// specify rounding and/or chamferring per-edge, and for top and bottom separately.
2019-03-23 04:13:18 +00:00
//
// Arguments:
2021-01-17 09:38:10 +00:00
// size1 = [width, length] of the bottom end of the prism.
// size2 = [width, length] of the top end of the prism.
2020-05-22 07:14:41 +00:00
// h|l = Height of the prism.
2021-01-17 09:38:10 +00:00
// shift = [X,Y] amount to shift the center of the top end with respect to the center of the bottom end.
// ---
2021-05-24 00:51:14 +00:00
// rounding = The roundover radius for the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no rounding)
// rounding1 = The roundover radius for the bottom of the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-].
// rounding2 = The roundover radius for the top of the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-].
// chamfer = The chamfer size for the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no chamfer)
// chamfer1 = The chamfer size for the bottom of the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-].
// chamfer2 = The chamfer size for the top of the vertical-ish edges of the prismoid. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-].
2019-05-26 19:47:50 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
2019-03-23 04:13:18 +00:00
//
2021-06-23 04:09:47 +00:00
// See Also: rounded_prism()
//
2019-03-23 04:13:18 +00:00
// Example: Rectangular Pyramid
2020-05-22 07:14:41 +00:00
// prismoid([40,40], [0,0], h=20);
2019-03-23 04:13:18 +00:00
// Example: Prism
// prismoid(size1=[40,40], size2=[0,40], h=20);
// Example: Truncated Pyramid
// prismoid(size1=[35,50], size2=[20,30], h=20);
// Example: Wedge
// prismoid(size1=[60,35], size2=[30,0], h=30);
// Example: Truncated Tetrahedron
// prismoid(size1=[10,40], size2=[40,10], h=40);
// Example: Inverted Truncated Pyramid
// prismoid(size1=[15,5], size2=[30,20], h=20);
// Example: Right Prism
// prismoid(size1=[30,60], size2=[0,60], shift=[-15,0], h=30);
2021-02-20 03:56:43 +00:00
// Example(FlatSpin,VPD=160,VPT=[0,0,10]): Shifting/Skewing
2019-03-23 04:13:18 +00:00
// prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]);
2020-05-22 07:14:41 +00:00
// Example: Rounding
2020-05-22 07:27:52 +00:00
// prismoid(100, 80, rounding=10, h=30);
2020-05-22 07:14:41 +00:00
// Example: Outer Chamfer Only
2020-05-22 07:27:52 +00:00
// prismoid(100, 80, chamfer=5, h=30);
2020-05-22 07:14:41 +00:00
// Example: Gradiant Rounding
// prismoid(100, 80, rounding1=10, rounding2=0, h=30);
// Example: Per Corner Rounding
// prismoid(100, 80, rounding=[0,5,10,15], h=30);
// Example: Per Corner Chamfer
// prismoid(100, 80, chamfer=[0,5,10,15], h=30);
// Example: Mixing Chamfer and Rounding
2021-05-22 08:41:25 +00:00
// prismoid(
// 100, 80, h=30,
// chamfer=[0,5,0,10],
// rounding=[5,0,10,0]
// );
2020-05-22 07:14:41 +00:00
// Example: Really Mixing It Up
// prismoid(
// size1=[100,80], size2=[80,60], h=20,
// chamfer1=[0,5,0,10], chamfer2=[5,0,10,0],
// rounding1=[5,0,10,0], rounding2=[0,5,0,10]
// );
2021-02-20 03:56:43 +00:00
// Example(Spin,VPD=160,VPT=[0,0,10]): Standard Connectors
2020-05-22 07:14:41 +00:00
// prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5])
// show_anchors();
2019-03-23 04:13:18 +00:00
module prismoid (
2020-05-30 02:04:34 +00:00
size1 , size2 , h , shift = [ 0 , 0 ] ,
rounding = 0 , rounding1 , rounding2 ,
chamfer = 0 , chamfer1 , chamfer2 ,
l , center ,
anchor , spin = 0 , orient = UP
2019-03-23 04:13:18 +00:00
) {
2020-05-30 02:04:34 +00:00
assert ( is_num ( size1 ) || is_vector ( size1 , 2 ) ) ;
assert ( is_num ( size2 ) || is_vector ( size2 , 2 ) ) ;
assert ( is_num ( h ) || is_num ( l ) ) ;
assert ( is_vector ( shift , 2 ) ) ;
assert ( is_num ( rounding ) || is_vector ( rounding , 4 ) , "Bad rounding argument." ) ;
assert ( is_undef ( rounding1 ) || is_num ( rounding1 ) || is_vector ( rounding1 , 4 ) , "Bad rounding1 argument." ) ;
assert ( is_undef ( rounding2 ) || is_num ( rounding2 ) || is_vector ( rounding2 , 4 ) , "Bad rounding2 argument." ) ;
assert ( is_num ( chamfer ) || is_vector ( chamfer , 4 ) , "Bad chamfer argument." ) ;
assert ( is_undef ( chamfer1 ) || is_num ( chamfer1 ) || is_vector ( chamfer1 , 4 ) , "Bad chamfer1 argument." ) ;
assert ( is_undef ( chamfer2 ) || is_num ( chamfer2 ) || is_vector ( chamfer2 , 4 ) , "Bad chamfer2 argument." ) ;
eps = pow ( 2 , - 14 ) ;
size1 = is_num ( size1 ) ? [ size1 , size1 ] : size1 ;
size2 = is_num ( size2 ) ? [ size2 , size2 ] : size2 ;
2021-05-27 03:07:05 +00:00
assert ( all_nonnegative ( size1 ) ) ;
assert ( all_nonnegative ( size2 ) ) ;
assert ( size1 . x + size2 . x > 0 ) ;
assert ( size1 . y + size2 . y > 0 ) ;
2020-05-30 02:04:34 +00:00
s1 = [ max ( size1 . x , eps ) , max ( size1 . y , eps ) ] ;
s2 = [ max ( size2 . x , eps ) , max ( size2 . y , eps ) ] ;
rounding1 = default ( rounding1 , rounding ) ;
rounding2 = default ( rounding2 , rounding ) ;
chamfer1 = default ( chamfer1 , chamfer ) ;
chamfer2 = default ( chamfer2 , chamfer ) ;
anchor = get_anchor ( anchor , center , BOT , BOT ) ;
vnf = prismoid (
size1 = size1 , size2 = size2 , h = h , shift = shift ,
2021-06-23 04:09:47 +00:00
rounding1 = rounding1 , rounding2 = rounding2 ,
chamfer1 = chamfer1 , chamfer2 = chamfer2 ,
2020-05-30 02:04:34 +00:00
l = l , center = CENTER
) ;
attachable ( anchor , spin , orient , size = [ s1 . x , s1 . y , h ] , size2 = s2 , shift = shift ) {
vnf_polyhedron ( vnf , convexity = 4 ) ;
children ( ) ;
}
2019-03-23 04:13:18 +00:00
}
2020-05-22 07:14:41 +00:00
function prismoid (
2020-05-30 02:04:34 +00:00
size1 , size2 , h , shift = [ 0 , 0 ] ,
rounding = 0 , rounding1 , rounding2 ,
chamfer = 0 , chamfer1 , chamfer2 ,
l , center ,
anchor = DOWN , spin = 0 , orient = UP
2020-05-22 07:14:41 +00:00
) =
2020-05-30 02:04:34 +00:00
assert ( is_vector ( size1 , 2 ) )
assert ( is_vector ( size2 , 2 ) )
assert ( is_num ( h ) || is_num ( l ) )
assert ( is_vector ( shift , 2 ) )
2021-06-23 04:09:47 +00:00
assert (
( is_num ( rounding ) && rounding >= 0 ) ||
( is_vector ( rounding , 4 ) && all_nonnegative ( rounding ) ) ,
"Bad rounding argument."
)
assert (
is_undef ( rounding1 ) || ( is_num ( rounding1 ) && rounding1 >= 0 ) ||
( is_vector ( rounding1 , 4 ) && all_nonnegative ( rounding1 ) ) ,
"Bad rounding1 argument."
)
assert (
is_undef ( rounding2 ) || ( is_num ( rounding2 ) && rounding2 >= 0 ) ||
( is_vector ( rounding2 , 4 ) && all_nonnegative ( rounding2 ) ) ,
"Bad rounding2 argument."
)
assert (
( is_num ( chamfer ) && chamfer >= 0 ) ||
( is_vector ( chamfer , 4 ) && all_nonnegative ( chamfer ) ) ,
"Bad chamfer argument."
)
assert (
is_undef ( chamfer1 ) || ( is_num ( chamfer1 ) && chamfer1 >= 0 ) ||
( is_vector ( chamfer1 , 4 ) && all_nonnegative ( chamfer1 ) ) ,
"Bad chamfer1 argument."
)
assert (
is_undef ( chamfer2 ) || ( is_num ( chamfer2 ) && chamfer2 >= 0 ) ||
( is_vector ( chamfer2 , 4 ) && all_nonnegative ( chamfer2 ) ) ,
"Bad chamfer2 argument."
)
2020-05-30 02:04:34 +00:00
let (
eps = pow ( 2 , - 14 ) ,
h = first_defined ( [ h , l , 1 ] ) ,
shiftby = point3d ( point2d ( shift ) ) ,
s1 = [ max ( size1 . x , eps ) , max ( size1 . y , eps ) ] ,
s2 = [ max ( size2 . x , eps ) , max ( size2 . y , eps ) ] ,
rounding1 = default ( rounding1 , rounding ) ,
rounding2 = default ( rounding2 , rounding ) ,
chamfer1 = default ( chamfer1 , chamfer ) ,
chamfer2 = default ( chamfer2 , chamfer ) ,
anchor = get_anchor ( anchor , center , BOT , BOT ) ,
vnf = ( rounding1 = = 0 && rounding2 = = 0 && chamfer1 = = 0 && chamfer2 = = 0 ) ? (
let (
corners = [ [ 1 , 1 ] , [ 1 , - 1 ] , [ - 1 , - 1 ] , [ - 1 , 1 ] ] * 0.5 ,
points = [
2021-06-15 03:28:49 +00:00
for ( p = corners ) point3d ( v_mul ( s2 , p ) , + h / 2 ) + shiftby ,
for ( p = corners ) point3d ( v_mul ( s1 , p ) , - h / 2 )
2020-05-30 02:04:34 +00:00
] ,
faces = [
[ 0 , 1 , 2 ] , [ 0 , 2 , 3 ] , [ 0 , 4 , 5 ] , [ 0 , 5 , 1 ] ,
[ 1 , 5 , 6 ] , [ 1 , 6 , 2 ] , [ 2 , 6 , 7 ] , [ 2 , 7 , 3 ] ,
[ 3 , 7 , 4 ] , [ 3 , 4 , 0 ] , [ 4 , 7 , 6 ] , [ 4 , 6 , 5 ] ,
]
) [ points , faces ]
) : (
let (
path1 = rect ( size1 , rounding = rounding1 , chamfer = chamfer1 , anchor = CTR ) ,
path2 = rect ( size2 , rounding = rounding2 , chamfer = chamfer2 , anchor = CTR ) ,
points = [
each path3d ( path1 , - h / 2 ) ,
each path3d ( move ( shiftby , p = path2 ) , + h / 2 ) ,
] ,
faces = hull ( points )
) [ points , faces ]
)
) reorient ( anchor , spin , orient , size = [ s1 . x , s1 . y , h ] , size2 = s2 , shift = shift , p = vnf ) ;
2019-03-23 04:13:18 +00:00
2021-01-05 22:16:58 +00:00
// Module: rect_tube()
2021-01-17 09:38:10 +00:00
// Usage: Typical Rectangular Tubes
2021-06-27 03:59:33 +00:00
// rect_tube(h, size, isize, [center], [shift]);
// rect_tube(h, size, wall=, [center=]);
// rect_tube(h, isize=, wall=, [center=]);
2021-01-17 09:38:10 +00:00
// Usage: Tapering Rectangular Tubes
// rect_tube(h, size1=, size2=, wall=, ...);
// rect_tube(h, isize1=, isize2=, wall=, ...);
// rect_tube(h, size1=, size2=, isize1=, isize2=, ...);
// Usage: Chamfered
// rect_tube(h, size, isize, chamfer=, ...);
// rect_tube(h, size, isize, chamfer1=, chamfer2= ...);
// rect_tube(h, size, isize, ichamfer=, ...);
// rect_tube(h, size, isize, ichamfer1=, ichamfer2= ...);
// rect_tube(h, size, isize, chamfer=, ichamfer=, ...);
// Usage: Rounded
// rect_tube(h, size, isize, rounding=, ...);
// rect_tube(h, size, isize, rounding1=, rounding2= ...);
// rect_tube(h, size, isize, irounding=, ...);
// rect_tube(h, size, isize, irounding1=, irounding2= ...);
// rect_tube(h, size, isize, rounding=, irounding=, ...);
// Usage: Attaching Children
2021-06-27 03:59:33 +00:00
// rect_tube(h, size, isize, ...) [attachments];
2021-01-17 09:38:10 +00:00
//
2021-01-05 22:16:58 +00:00
// Description:
// Creates a rectangular or prismoid tube with optional roundovers and/or chamfers.
// You can only round or chamfer the vertical(ish) edges. For those edges, you can
// specify rounding and/or chamferring per-edge, and for top and bottom, inside and
// outside separately.
// Arguments:
2021-01-17 09:38:10 +00:00
// h|l = The height or length of the rectangular tube. Default: 1
2021-01-05 22:16:58 +00:00
// size = The outer [X,Y] size of the rectangular tube.
// isize = The inner [X,Y] size of the rectangular tube.
2021-01-17 09:38:10 +00:00
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=UP`.
// shift = [X,Y] amount to shift the center of the top end with respect to the center of the bottom end.
// ---
2021-01-05 22:16:58 +00:00
// wall = The thickness of the rectangular tube wall.
2021-02-15 08:28:36 +00:00
// size1 = The [X,Y] size of the outside of the bottom of the rectangular tube.
// size2 = The [X,Y] size of the outside of the top of the rectangular tube.
// isize1 = The [X,Y] size of the inside of the bottom of the rectangular tube.
// isize2 = The [X,Y] size of the inside of the top of the rectangular tube.
2021-01-05 22:16:58 +00:00
// rounding = The roundover radius for the outside edges of the rectangular tube.
// rounding1 = The roundover radius for the outside bottom corner of the rectangular tube.
// rounding2 = The roundover radius for the outside top corner of the rectangular tube.
// chamfer = The chamfer size for the outside edges of the rectangular tube.
// chamfer1 = The chamfer size for the outside bottom corner of the rectangular tube.
// chamfer2 = The chamfer size for the outside top corner of the rectangular tube.
// irounding = The roundover radius for the inside edges of the rectangular tube. Default: Same as `rounding`
// irounding1 = The roundover radius for the inside bottom corner of the rectangular tube.
// irounding2 = The roundover radius for the inside top corner of the rectangular tube.
// ichamfer = The chamfer size for the inside edges of the rectangular tube. Default: Same as `chamfer`
// ichamfer1 = The chamfer size for the inside bottom corner of the rectangular tube.
// ichamfer2 = The chamfer size for the inside top corner of the rectangular tube.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `BOTTOM`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
// Examples:
// rect_tube(size=50, wall=5, h=30);
// rect_tube(size=[100,60], wall=5, h=30);
// rect_tube(isize=[60,80], wall=5, h=30);
// rect_tube(size=[100,60], isize=[90,50], h=30);
// rect_tube(size1=[100,60], size2=[70,40], wall=5, h=30);
2021-05-22 08:41:25 +00:00
// Example:
// rect_tube(
// size1=[100,60], size2=[70,40],
// isize1=[40,20], isize2=[65,35], h=15
// );
2021-01-05 22:16:58 +00:00
// Example: Outer Rounding Only
// rect_tube(size=100, wall=5, rounding=10, irounding=0, h=30);
// Example: Outer Chamfer Only
// rect_tube(size=100, wall=5, chamfer=5, ichamfer=0, h=30);
// Example: Outer Rounding, Inner Chamfer
// rect_tube(size=100, wall=5, rounding=10, ichamfer=8, h=30);
// Example: Inner Rounding, Outer Chamfer
// rect_tube(size=100, wall=5, chamfer=10, irounding=8, h=30);
// Example: Gradiant Rounding
2021-05-22 08:41:25 +00:00
// rect_tube(
// size1=100, size2=80, wall=5, h=30,
// rounding1=10, rounding2=0,
// irounding1=8, irounding2=0
// );
2021-01-05 22:16:58 +00:00
// Example: Per Corner Rounding
2021-05-22 08:41:25 +00:00
// rect_tube(
// size=100, wall=10, h=30,
// rounding=[0,5,10,15], irounding=0
// );
2021-01-05 22:16:58 +00:00
// Example: Per Corner Chamfer
2021-05-22 08:41:25 +00:00
// rect_tube(
// size=100, wall=10, h=30,
// chamfer=[0,5,10,15], ichamfer=0
// );
2021-01-05 22:16:58 +00:00
// Example: Mixing Chamfer and Rounding
2021-05-22 08:41:25 +00:00
// rect_tube(
// size=100, wall=10, h=30,
// chamfer=[0,5,0,10], ichamfer=0,
// rounding=[5,0,10,0], irounding=0
// );
2021-01-05 22:16:58 +00:00
// Example: Really Mixing It Up
// rect_tube(
// size1=[100,80], size2=[80,60],
// isize1=[50,30], isize2=[70,50], h=20,
// chamfer1=[0,5,0,10], ichamfer1=[0,3,0,8],
// chamfer2=[5,0,10,0], ichamfer2=[3,0,8,0],
// rounding1=[5,0,10,0], irounding1=[3,0,8,0],
// rounding2=[0,5,0,10], irounding2=[0,3,0,8]
// );
module rect_tube (
2021-01-17 09:38:10 +00:00
h , size , isize , center , shift = [ 0 , 0 ] ,
wall , size1 , size2 , isize1 , isize2 ,
2021-01-05 22:16:58 +00:00
rounding = 0 , rounding1 , rounding2 ,
irounding = 0 , irounding1 , irounding2 ,
chamfer = 0 , chamfer1 , chamfer2 ,
ichamfer = 0 , ichamfer1 , ichamfer2 ,
anchor , spin = 0 , orient = UP ,
2021-01-17 09:38:10 +00:00
l
2021-01-05 22:16:58 +00:00
) {
2021-02-15 08:28:36 +00:00
h = one_defined ( [ h , l ] , "h,l" ) ;
2021-01-05 22:16:58 +00:00
assert ( is_num ( h ) , "l or h argument required." ) ;
assert ( is_vector ( shift , 2 ) ) ;
s1 = is_num ( size1 ) ? [ size1 , size1 ] :
is_vector ( size1 , 2 ) ? size1 :
is_num ( size ) ? [ size , size ] :
is_vector ( size , 2 ) ? size :
undef ;
s2 = is_num ( size2 ) ? [ size2 , size2 ] :
is_vector ( size2 , 2 ) ? size2 :
is_num ( size ) ? [ size , size ] :
is_vector ( size , 2 ) ? size :
undef ;
is1 = is_num ( isize1 ) ? [ isize1 , isize1 ] :
is_vector ( isize1 , 2 ) ? isize1 :
is_num ( isize ) ? [ isize , isize ] :
is_vector ( isize , 2 ) ? isize :
undef ;
is2 = is_num ( isize2 ) ? [ isize2 , isize2 ] :
is_vector ( isize2 , 2 ) ? isize2 :
is_num ( isize ) ? [ isize , isize ] :
is_vector ( isize , 2 ) ? isize :
undef ;
size1 = is_def ( s1 ) ? s1 :
( is_def ( wall ) && is_def ( is1 ) ) ? ( is1 + 2 * [ wall , wall ] ) :
undef ;
size2 = is_def ( s2 ) ? s2 :
( is_def ( wall ) && is_def ( is2 ) ) ? ( is2 + 2 * [ wall , wall ] ) :
undef ;
isize1 = is_def ( is1 ) ? is1 :
( is_def ( wall ) && is_def ( s1 ) ) ? ( s1 - 2 * [ wall , wall ] ) :
undef ;
isize2 = is_def ( is2 ) ? is2 :
( is_def ( wall ) && is_def ( s2 ) ) ? ( s2 - 2 * [ wall , wall ] ) :
undef ;
assert ( wall = = undef || is_num ( wall ) ) ;
assert ( size1 ! = undef , "Bad size/size1 argument." ) ;
assert ( size2 ! = undef , "Bad size/size2 argument." ) ;
assert ( isize1 ! = undef , "Bad isize/isize1 argument." ) ;
assert ( isize2 ! = undef , "Bad isize/isize2 argument." ) ;
assert ( isize1 . x < size1 . x , "Inner size is larger than outer size." ) ;
assert ( isize1 . y < size1 . y , "Inner size is larger than outer size." ) ;
assert ( isize2 . x < size2 . x , "Inner size is larger than outer size." ) ;
assert ( isize2 . y < size2 . y , "Inner size is larger than outer size." ) ;
anchor = get_anchor ( anchor , center , BOT , BOT ) ;
attachable ( anchor , spin , orient , size = [ each size1 , h ] , size2 = size2 , shift = shift ) {
diff ( "_H_o_L_e_" )
prismoid (
size1 , size2 , h = h , shift = shift ,
rounding = rounding , rounding1 = rounding1 , rounding2 = rounding2 ,
chamfer = chamfer , chamfer1 = chamfer1 , chamfer2 = chamfer2 ,
anchor = CTR
) {
children ( ) ;
tags ( "_H_o_L_e_" ) prismoid (
isize1 , isize2 , h = h + 0.05 , shift = shift ,
rounding = irounding , rounding1 = irounding1 , rounding2 = irounding2 ,
chamfer = ichamfer , chamfer1 = ichamfer1 , chamfer2 = ichamfer2 ,
anchor = CTR
) ;
}
children ( ) ;
}
}
2021-01-17 09:38:10 +00:00
function rect_tube (
h , size , isize , center , shift = [ 0 , 0 ] ,
wall , size1 , size2 , isize1 , isize2 ,
rounding = 0 , rounding1 , rounding2 ,
irounding = 0 , irounding1 , irounding2 ,
chamfer = 0 , chamfer1 , chamfer2 ,
ichamfer = 0 , ichamfer1 , ichamfer2 ,
anchor , spin = 0 , orient = UP ,
l
) = no_function ( "rect_tube" ) ;
2021-01-05 22:16:58 +00:00
2019-03-23 04:13:18 +00:00
// Module: right_triangle()
//
// Usage:
2021-06-27 03:59:33 +00:00
// right_triangle(size, [center]);
2019-03-23 04:13:18 +00:00
//
2020-01-14 03:06:56 +00:00
// Description:
// Creates a 3D right triangular prism with the hypotenuse in the X+Y+ quadrant.
//
2019-03-23 04:13:18 +00:00
// Arguments:
// size = [width, thickness, height]
2021-01-17 09:38:10 +00:00
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=UP`.
// ---
2019-05-26 19:47:50 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `ALLNEG`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
2019-03-23 04:13:18 +00:00
//
// Example: Centered
2020-01-14 03:06:56 +00:00
// right_triangle([60, 40, 10], center=true);
2019-03-23 04:13:18 +00:00
// Example: *Non*-Centered
2020-01-14 03:06:56 +00:00
// right_triangle([60, 40, 10]);
2019-04-22 08:08:41 +00:00
// Example: Standard Connectors
2020-01-14 03:06:56 +00:00
// right_triangle([60, 40, 15]) show_anchors();
2020-02-29 21:16:15 +00:00
module right_triangle ( size = [ 1 , 1 , 1 ] , center , anchor , spin = 0 , orient = UP )
2019-03-23 04:13:18 +00:00
{
2020-05-30 02:04:34 +00:00
size = scalar_vec3 ( size ) ;
anchor = get_anchor ( anchor , center , ALLNEG , ALLNEG ) ;
attachable ( anchor , spin , orient , size = size ) {
2020-11-30 04:23:03 +00:00
if ( size . z > 0 ) {
linear_extrude ( height = size . z , convexity = 2 , center = true ) {
polygon ( [ [ - size . x / 2 , - size . y / 2 ] , [ - size . x / 2 , size . y / 2 ] , [ size . x / 2 , - size . y / 2 ] ] ) ;
}
2020-05-30 02:04:34 +00:00
}
children ( ) ;
}
2019-03-23 04:13:18 +00:00
}
2021-01-17 09:38:10 +00:00
function right_triangle ( size = [ 1 , 1 , 1 ] , center , anchor , spin = 0 , orient = UP ) =
no_function ( "right_triangle" ) ;
2019-03-23 04:13:18 +00:00
2021-09-17 01:56:56 +00:00
// Section: Cylinders
2021-09-17 01:50:12 +00:00
// Function&Module: cylinder()
// Topics: Shapes (3D), Attachable, VNF Generators
// Usage: As Module
// cylinder(h, r=/d=, [center=], ...);
// cylinder(h, r1/d1=, r2/d2=, [center=], ...);
// Usage: With Attachments
// cylinder(h, r=/d=, [center=]) {attachments}
// Usage: As Function
// vnf = cylinder(h, r=/d=, [center=], ...);
// vnf = cylinder(h, r1/d1=, r2/d2=, [center=], ...);
// See Also: cyl()
// Description:
// Creates a 3D cylinder or conic object with support for anchoring and attachments.
// This can be used as a drop-in replacement for the built-in `cylinder()` module.
// When called as a function, returns a [VNF](vnf.scad) for a cylinder.
// Arguments:
// l / h = The height of the cylinder.
// r1 = The bottom radius of the cylinder. (Before orientation.)
// r2 = The top radius of the cylinder. (Before orientation.)
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=BOTTOM`.
// ---
// d1 = The bottom diameter of the cylinder. (Before orientation.)
// d2 = The top diameter of the cylinder. (Before orientation.)
// r = The radius of the cylinder.
// d = The diameter of the cylinder.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
// Example: By Radius
// xdistribute(30) {
// cylinder(h=40, r=10);
// cylinder(h=40, r1=10, r2=5);
// }
// Example: By Diameter
// xdistribute(30) {
// cylinder(h=40, d=25);
// cylinder(h=40, d1=25, d2=10);
// }
// Example(Med): Anchoring
// cylinder(h=40, r1=10, r2=5, anchor=BOTTOM+FRONT);
// Example(Med): Spin
// cylinder(h=40, r1=10, r2=5, anchor=BOTTOM+FRONT, spin=45);
// Example(Med): Orient
// cylinder(h=40, r1=10, r2=5, anchor=BOTTOM+FRONT, spin=45, orient=FWD);
// Example(Big): Standard Connectors
// xdistribute(40) {
// cylinder(h=30, d=25) show_anchors();
// cylinder(h=30, d1=25, d2=10) show_anchors();
// }
module cylinder ( h , r1 , r2 , center , l , r , d , d1 , d2 , anchor , spin = 0 , orient = UP )
{
anchor = get_anchor ( anchor , center , BOTTOM , BOTTOM ) ;
r1 = get_radius ( r1 = r1 , r = r , d1 = d1 , d = d , dflt = 1 ) ;
r2 = get_radius ( r1 = r2 , r = r , d1 = d2 , d = d , dflt = 1 ) ;
l = first_defined ( [ h , l , 1 ] ) ;
sides = segs ( max ( r1 , r2 ) ) ;
attachable ( anchor , spin , orient , r1 = r1 , r2 = r2 , l = l ) {
if ( r1 > r2 ) {
if ( l > 0 ) {
linear_extrude ( height = l , center = true , convexity = 2 , scale = r2 / r1 ) {
circle ( r = r1 ) ;
}
}
} else {
zflip ( ) {
if ( l > 0 ) {
linear_extrude ( height = l , center = true , convexity = 2 , scale = r1 / r2 ) {
circle ( r = r2 ) ;
}
}
}
}
children ( ) ;
}
}
function cylinder ( h , r1 , r2 , center , l , r , d , d1 , d2 , anchor , spin = 0 , orient = UP ) =
let (
anchor = get_anchor ( anchor , center , BOTTOM , BOTTOM ) ,
r1 = get_radius ( r1 = r1 , r = r , d1 = d1 , d = d , dflt = 1 ) ,
r2 = get_radius ( r1 = r2 , r = r , d1 = d2 , d = d , dflt = 1 ) ,
l = first_defined ( [ h , l , 1 ] ) ,
sides = segs ( max ( r1 , r2 ) ) ,
verts = [
for ( i = [ 0 : 1 : sides - 1 ] ) let ( a = 360 * ( 1 - i / sides ) ) [ r1 * cos ( a ) , r1 * sin ( a ) , - l / 2 ] ,
for ( i = [ 0 : 1 : sides - 1 ] ) let ( a = 360 * ( 1 - i / sides ) ) [ r2 * cos ( a ) , r2 * sin ( a ) , l / 2 ] ,
] ,
faces = [
[ for ( i = [ 0 : 1 : sides - 1 ] ) sides - 1 - i ] ,
for ( i = [ 0 : 1 : sides - 1 ] ) [ i , ( ( i + 1 ) % sides ) + sides , i + sides ] ,
for ( i = [ 0 : 1 : sides - 1 ] ) [ i , ( i + 1 ) % sides , ( ( i + 1 ) % sides ) + sides ] ,
[ for ( i = [ 0 : 1 : sides - 1 ] ) sides + i ]
]
) [ reorient ( anchor , spin , orient , l = l , r1 = r1 , r2 = r2 , p = verts ) , faces ] ;
2019-03-23 04:13:18 +00:00
// Module: cyl()
//
// Description:
2021-01-17 09:38:10 +00:00
// Creates cylinders in various anchorings and orientations, with optional rounding and chamfers.
// You can use `h` and `l` interchangably, and all variants allow specifying size by either `r`|`d`,
// or `r1`|`d1` and `r2`|`d2`. Note: the chamfers and rounding cannot be cumulatively longer than
// the cylinder's length.
2019-03-23 04:13:18 +00:00
//
// Usage: Normal Cylinders
2021-06-27 03:59:33 +00:00
// cyl(l|h, r, [center], [circum=], [realign=]);
2021-01-17 09:38:10 +00:00
// cyl(l|h, d=, ...);
// cyl(l|h, r1=, r2=, ...);
// cyl(l|h, d1=, d2=, ...);
2019-03-23 04:13:18 +00:00
//
// Usage: Chamferred Cylinders
2021-06-27 03:59:33 +00:00
// cyl(l|h, r|d, chamfer=, [chamfang=], [from_end=], ...);
// cyl(l|h, r|d, chamfer1=, [chamfang1=], [from_end=], ...);
// cyl(l|h, r|d, chamfer2=, [chamfang2=], [from_end=], ...);
// cyl(l|h, r|d, chamfer1=, chamfer2=, [chamfang1=], [chamfang2=], [from_end=], ...);
2019-03-23 04:13:18 +00:00
//
2019-04-25 02:42:38 +00:00
// Usage: Rounded End Cylinders
2021-01-17 09:38:10 +00:00
// cyl(l|h, r|d, rounding=, ...);
// cyl(l|h, r|d, rounding1=, ...);
// cyl(l|h, r|d, rounding2=, ...);
// cyl(l|h, r|d, rounding1=, rounding2=, ...);
2019-03-23 04:13:18 +00:00
//
// Arguments:
2021-01-17 09:38:10 +00:00
// l / h = Length of cylinder along oriented axis. Default: 1
// r = Radius of cylinder. Default: 1
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=DOWN`.
// ---
2019-02-27 11:56:34 +00:00
// r1 = Radius of the negative (X-, Y-, Z-) end of cylinder.
// r2 = Radius of the positive (X+, Y+, Z+) end of cylinder.
// d = Diameter of cylinder.
// d1 = Diameter of the negative (X-, Y-, Z-) end of cylinder.
// d2 = Diameter of the positive (X+, Y+, Z+) end of cylinder.
2019-03-23 04:13:18 +00:00
// circum = If true, cylinder should circumscribe the circle of the given size. Otherwise inscribes. Default: `false`
2019-02-28 01:45:15 +00:00
// chamfer = The size of the chamfers on the ends of the cylinder. Default: none.
2021-01-17 09:38:10 +00:00
// chamfer1 = The size of the chamfer on the bottom end of the cylinder. Default: none.
// chamfer2 = The size of the chamfer on the top end of the cylinder. Default: none.
2019-02-28 01:45:15 +00:00
// chamfang = The angle in degrees of the chamfers on the ends of the cylinder.
2021-01-17 09:38:10 +00:00
// chamfang1 = The angle in degrees of the chamfer on the bottom end of the cylinder.
// chamfang2 = The angle in degrees of the chamfer on the top end of the cylinder.
2019-03-23 04:13:18 +00:00
// from_end = If true, chamfer is measured from the end of the cylinder, instead of inset from the edge. Default: `false`.
2019-04-25 02:42:38 +00:00
// rounding = The radius of the rounding on the ends of the cylinder. Default: none.
2021-01-17 09:38:10 +00:00
// rounding1 = The radius of the rounding on the bottom end of the cylinder.
// rounding2 = The radius of the rounding on the top end of the cylinder.
2019-02-27 11:56:34 +00:00
// realign = If true, rotate the cylinder by half the angle of one face.
2019-05-26 19:47:50 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
2019-03-23 04:13:18 +00:00
//
// Example: By Radius
// xdistribute(30) {
// cyl(l=40, r=10);
// cyl(l=40, r1=10, r2=5);
// }
//
// Example: By Diameter
// xdistribute(30) {
// cyl(l=40, d=25);
// cyl(l=40, d1=25, d2=10);
// }
//
// Example: Chamferring
// xdistribute(60) {
// // Shown Left to right.
// cyl(l=40, d=40, chamfer=7); // Default chamfang=45
// cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=false);
// cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=true);
// }
//
2019-04-25 02:42:38 +00:00
// Example: Rounding
// cyl(l=40, d=40, rounding=10);
2019-03-23 04:13:18 +00:00
//
2019-04-25 02:42:38 +00:00
// Example: Heterogenous Chamfers and Rounding
2019-03-23 04:13:18 +00:00
// ydistribute(80) {
// // Shown Front to Back.
2019-05-26 06:31:05 +00:00
// cyl(l=40, d=40, rounding1=15, orient=UP);
// cyl(l=40, d=40, chamfer2=5, orient=UP);
// cyl(l=40, d=40, chamfer1=12, rounding2=10, orient=UP);
2019-03-23 04:13:18 +00:00
// }
//
// Example: Putting it all together
2021-05-22 08:41:25 +00:00
// cyl(
// l=40, d1=25, d2=15,
// chamfer1=10, chamfang1=30,
// from_end=true, rounding2=5
// );
2019-04-22 08:08:41 +00:00
//
2019-06-12 09:27:42 +00:00
// Example: External Chamfers
// cyl(l=50, r=30, chamfer=-5, chamfang=30, $fa=1, $fs=1);
//
// Example: External Roundings
// cyl(l=50, r=30, rounding1=-5, rounding2=5, $fa=1, $fs=1);
//
2019-04-22 08:08:41 +00:00
// Example: Standard Connectors
// xdistribute(40) {
2019-04-23 03:55:03 +00:00
// cyl(l=30, d=25) show_anchors();
// cyl(l=30, d1=25, d2=10) show_anchors();
2019-04-22 08:08:41 +00:00
// }
//
2019-02-27 11:56:34 +00:00
module cyl (
2021-01-17 09:38:10 +00:00
h , r , center ,
l , r1 , r2 ,
d , d1 , d2 ,
chamfer , chamfer1 , chamfer2 ,
chamfang , chamfang1 , chamfang2 ,
rounding , rounding1 , rounding2 ,
2020-05-30 02:04:34 +00:00
circum = false , realign = false , from_end = false ,
2021-01-17 09:38:10 +00:00
anchor , spin = 0 , orient = UP
2019-02-27 11:56:34 +00:00
) {
2020-05-30 02:04:34 +00:00
l = first_defined ( [ l , h , 1 ] ) ;
2021-01-09 07:40:07 +00:00
_r1 = get_radius ( r1 = r1 , r = r , d1 = d1 , d = d , dflt = 1 ) ;
_r2 = get_radius ( r1 = r2 , r = r , d1 = d2 , d = d , dflt = 1 ) ;
sides = segs ( max ( _r1 , _r2 ) ) ;
2020-05-30 02:04:34 +00:00
sc = circum ? 1 / cos ( 180 / sides ) : 1 ;
2021-01-09 07:40:07 +00:00
r1 = _r1 * sc ;
r2 = _r2 * sc ;
2020-05-30 02:04:34 +00:00
phi = atan2 ( l , r2 - r1 ) ;
anchor = get_anchor ( anchor , center , BOT , CENTER ) ;
attachable ( anchor , spin , orient , r1 = r1 , r2 = r2 , l = l ) {
zrot ( realign ? 180 / sides : 0 ) {
if ( ! any_defined ( [ chamfer , chamfer1 , chamfer2 , rounding , rounding1 , rounding2 ] ) ) {
2021-01-09 07:40:07 +00:00
cylinder ( h = l , r1 = r1 , r2 = r2 , center = true , $fn = sides ) ;
2020-05-30 02:04:34 +00:00
} else {
vang = atan2 ( l , r1 - r2 ) / 2 ;
chang1 = 90 - first_defined ( [ chamfang1 , chamfang , vang ] ) ;
chang2 = 90 - first_defined ( [ chamfang2 , chamfang , 90 - vang ] ) ;
2021-01-07 03:08:54 +00:00
cham1 = u_mul ( first_defined ( [ chamfer1 , chamfer ] ) , ( from_end ? 1 : tan ( chang1 ) ) ) ;
cham2 = u_mul ( first_defined ( [ chamfer2 , chamfer ] ) , ( from_end ? 1 : tan ( chang2 ) ) ) ;
2020-05-30 02:04:34 +00:00
fil1 = first_defined ( [ rounding1 , rounding ] ) ;
fil2 = first_defined ( [ rounding2 , rounding ] ) ;
if ( chamfer ! = undef ) {
assert ( chamfer < = r1 , "chamfer is larger than the r1 radius of the cylinder." ) ;
assert ( chamfer < = r2 , "chamfer is larger than the r2 radius of the cylinder." ) ;
}
if ( cham1 ! = undef ) {
assert ( cham1 < = r1 , "chamfer1 is larger than the r1 radius of the cylinder." ) ;
}
if ( cham2 ! = undef ) {
assert ( cham2 < = r2 , "chamfer2 is larger than the r2 radius of the cylinder." ) ;
}
if ( rounding ! = undef ) {
assert ( rounding < = r1 , "rounding is larger than the r1 radius of the cylinder." ) ;
assert ( rounding < = r2 , "rounding is larger than the r2 radius of the cylinder." ) ;
}
if ( fil1 ! = undef ) {
assert ( fil1 < = r1 , "rounding1 is larger than the r1 radius of the cylinder." ) ;
}
if ( fil2 ! = undef ) {
assert ( fil2 < = r2 , "rounding2 is larger than the r1 radius of the cylinder." ) ;
}
dy1 = abs ( first_defined ( [ cham1 , fil1 , 0 ] ) ) ;
dy2 = abs ( first_defined ( [ cham2 , fil2 , 0 ] ) ) ;
assert ( dy1 + dy2 < = l , "Sum of fillets and chamfer sizes must be less than the length of the cylinder." ) ;
path = concat (
[ [ 0 , l / 2 ] ] ,
! is_undef ( cham2 ) ? (
let (
p1 = [ r2 - cham2 / tan ( chang2 ) , l / 2 ] ,
p2 = lerp ( [ r2 , l / 2 ] , [ r1 , - l / 2 ] , abs ( cham2 ) / l )
) [ p1 , p2 ]
) : ! is_undef ( fil2 ) ? (
let (
2020-09-25 07:01:45 +00:00
cn = circle_2tangents ( [ r2 - fil2 , l / 2 ] , [ r2 , l / 2 ] , [ r1 , - l / 2 ] , r = abs ( fil2 ) ) ,
2020-05-30 02:04:34 +00:00
ang = fil2 < 0 ? phi : phi - 180 ,
steps = ceil ( abs ( ang ) / 360 * segs ( abs ( fil2 ) ) ) ,
step = ang / steps ,
pts = [ for ( i = [ 0 : 1 : steps ] ) let ( a = 90 + i * step ) cn [ 0 ] + abs ( fil2 ) * [ cos ( a ) , sin ( a ) ] ]
) pts
) : [ [ r2 , l / 2 ] ] ,
! is_undef ( cham1 ) ? (
let (
p1 = lerp ( [ r1 , - l / 2 ] , [ r2 , l / 2 ] , abs ( cham1 ) / l ) ,
p2 = [ r1 - cham1 / tan ( chang1 ) , - l / 2 ]
) [ p1 , p2 ]
) : ! is_undef ( fil1 ) ? (
let (
2020-09-25 07:01:45 +00:00
cn = circle_2tangents ( [ r1 - fil1 , - l / 2 ] , [ r1 , - l / 2 ] , [ r2 , l / 2 ] , r = abs ( fil1 ) ) ,
2020-05-30 02:04:34 +00:00
ang = fil1 < 0 ? 180 - phi : - phi ,
steps = ceil ( abs ( ang ) / 360 * segs ( abs ( fil1 ) ) ) ,
step = ang / steps ,
pts = [ for ( i = [ 0 : 1 : steps ] ) let ( a = ( fil1 < 0 ? 180 : 0 ) + ( phi - 90 ) + i * step ) cn [ 0 ] + abs ( fil1 ) * [ cos ( a ) , sin ( a ) ] ]
) pts
) : [ [ r1 , - l / 2 ] ] ,
[ [ 0 , - l / 2 ] ]
) ;
rotate_extrude ( convexity = 2 ) {
polygon ( path ) ;
}
}
}
children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
2019-02-27 11:56:34 +00:00
2019-03-23 04:13:18 +00:00
// Module: xcyl()
//
// Description:
// Creates a cylinder oriented along the X axis.
//
2021-01-17 09:38:10 +00:00
// Usage: Typical
2021-06-27 03:59:33 +00:00
// xcyl(l|h, r, [anchor=]);
// xcyl(l|h, d=, [anchor=]);
// xcyl(l|h, r1=|d1=, r2=|d2=, [anchor=]);
2021-01-17 09:38:10 +00:00
// Usage: Attaching Children
2021-06-27 03:59:33 +00:00
// xcyl(l|h, r, [anchor=]) [attachments];
2019-03-23 04:13:18 +00:00
//
// Arguments:
2021-01-17 09:38:10 +00:00
// l / h = Length of cylinder along oriented axis. Default: 1
// r = Radius of cylinder. Default: 1
// ---
2019-03-23 04:13:18 +00:00
// r1 = Optional radius of left (X-) end of cylinder.
// r2 = Optional radius of right (X+) end of cylinder.
// d = Optional diameter of cylinder. (use instead of `r`)
// d1 = Optional diameter of left (X-) end of cylinder.
// d2 = Optional diameter of right (X+) end of cylinder.
2019-05-26 19:47:50 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
2019-03-23 04:13:18 +00:00
//
// Example: By Radius
// ydistribute(50) {
// xcyl(l=35, r=10);
// xcyl(l=35, r1=15, r2=5);
// }
//
// Example: By Diameter
// ydistribute(50) {
// xcyl(l=35, d=20);
// xcyl(l=35, d1=30, d2=10);
// }
2021-01-17 09:38:10 +00:00
module xcyl ( h , r , d , r1 , r2 , d1 , d2 , l , anchor = CENTER )
2019-02-10 11:12:46 +00:00
{
2020-09-22 07:24:39 +00:00
r1 = get_radius ( r1 = r1 , r = r , d1 = d1 , d = d , dflt = 1 ) ;
r2 = get_radius ( r1 = r2 , r = r , d1 = d2 , d = d , dflt = 1 ) ;
l = first_defined ( [ l , h , 1 ] ) ;
attachable ( anchor , 0 , UP , r1 = r1 , r2 = r2 , l = l , axis = RIGHT ) {
cyl ( l = l , r1 = r1 , r2 = r2 , orient = RIGHT , anchor = CENTER ) ;
children ( ) ;
}
2019-02-10 11:12:46 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: ycyl()
//
// Description:
// Creates a cylinder oriented along the Y axis.
//
2021-01-17 09:38:10 +00:00
// Usage: Typical
2021-06-27 03:59:33 +00:00
// ycyl(l|h, r, [anchor=]);
// ycyl(l|h, d=, [anchor=]);
// ycyl(l|h, r1=|d1=, r2=|d2=, [anchor=]);
2021-01-17 09:38:10 +00:00
// Usage: Attaching Children
2021-06-27 03:59:33 +00:00
// ycyl(l|h, r, [anchor=]) [attachments];
2019-03-23 04:13:18 +00:00
//
// Arguments:
// l / h = Length of cylinder along oriented axis. (Default: `1.0`)
// r = Radius of cylinder.
2021-01-17 09:38:10 +00:00
// ---
2019-03-23 04:13:18 +00:00
// r1 = Radius of front (Y-) end of cone.
// r2 = Radius of back (Y+) end of one.
// d = Diameter of cylinder.
// d1 = Diameter of front (Y-) end of one.
// d2 = Diameter of back (Y+) end of one.
2019-05-26 19:47:50 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
2019-03-23 04:13:18 +00:00
//
// Example: By Radius
// xdistribute(50) {
// ycyl(l=35, r=10);
// ycyl(l=35, r1=15, r2=5);
// }
//
// Example: By Diameter
// xdistribute(50) {
// ycyl(l=35, d=20);
// ycyl(l=35, d1=30, d2=10);
// }
2021-01-17 09:38:10 +00:00
module ycyl ( h , r , d , r1 , r2 , d1 , d2 , l , anchor = CENTER )
2019-02-10 11:12:46 +00:00
{
2020-09-22 07:24:39 +00:00
r1 = get_radius ( r1 = r1 , r = r , d1 = d1 , d = d , dflt = 1 ) ;
r2 = get_radius ( r1 = r2 , r = r , d1 = d2 , d = d , dflt = 1 ) ;
l = first_defined ( [ l , h , 1 ] ) ;
attachable ( anchor , 0 , UP , r1 = r1 , r2 = r2 , l = l , axis = BACK ) {
2020-12-13 04:28:33 +00:00
cyl ( l = l , h = h , r1 = r1 , r2 = r2 , orient = BACK , anchor = CENTER ) ;
2020-09-22 07:24:39 +00:00
children ( ) ;
}
2019-02-10 11:12:46 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: zcyl()
//
// Description:
// Creates a cylinder oriented along the Z axis.
//
2021-01-17 09:38:10 +00:00
// Usage: Typical
2021-06-27 03:59:33 +00:00
// zcyl(l|h, r, [anchor=]);
// zcyl(l|h, d=, [anchor=]);
// zcyl(l|h, r1=|d1=, r2=|d2=, [anchor=]);
2021-01-17 09:38:10 +00:00
// Usage: Attaching Children
2021-06-27 03:59:33 +00:00
// zcyl(l|h, r, [anchor=]) [attachments];
2019-03-23 04:13:18 +00:00
//
// Arguments:
// l / h = Length of cylinder along oriented axis. (Default: 1.0)
// r = Radius of cylinder.
2021-01-17 09:38:10 +00:00
// ---
2019-03-23 04:13:18 +00:00
// r1 = Radius of front (Y-) end of cone.
// r2 = Radius of back (Y+) end of one.
// d = Diameter of cylinder.
// d1 = Diameter of front (Y-) end of one.
// d2 = Diameter of back (Y+) end of one.
2019-05-26 19:47:50 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
2019-03-23 04:13:18 +00:00
//
// Example: By Radius
// xdistribute(50) {
// zcyl(l=35, r=10);
// zcyl(l=35, r1=15, r2=5);
// }
//
// Example: By Diameter
// xdistribute(50) {
// zcyl(l=35, d=20);
// zcyl(l=35, d1=30, d2=10);
// }
2021-01-17 09:38:10 +00:00
module zcyl ( h , r , d , r1 , r2 , d1 , d2 , l , anchor = CENTER )
2019-02-10 11:12:46 +00:00
{
2020-05-30 02:04:34 +00:00
cyl ( l = l , h = h , r = r , r1 = r1 , r2 = r2 , d = d , d1 = d1 , d2 = d2 , orient = UP , anchor = anchor ) children ( ) ;
2019-01-30 00:38:02 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: tube()
//
// Description:
// Makes a hollow tube with the given outer size and wall thickness.
//
2021-01-17 09:38:10 +00:00
// Usage: Typical
2021-06-27 03:59:33 +00:00
// tube(h|l, or, ir, [center], [realign=]);
2021-01-17 09:38:10 +00:00
// tube(h|l, or=|od=, ir=|id=, ...);
// tube(h|l, ir|id, wall, ...);
// tube(h|l, or|od, wall, ...);
// tube(h|l, ir1|id1, ir2|id2, wall, ...);
// tube(h|l, or1|od1, or2|od2, wall, ...);
2021-06-27 03:59:33 +00:00
// tube(h|l, ir1|id1, ir2|id2, or1|od1, or2|od2, [realign]);
2021-01-17 09:38:10 +00:00
// Usage: Attaching Children
2021-06-27 03:59:33 +00:00
// tube(h|l, or, ir, [center]) [attachments];
2019-03-23 04:13:18 +00:00
//
// Arguments:
2021-01-17 09:38:10 +00:00
// h / l = height of tube. Default: 1
// or = Outer radius of tube. Default: 1
// ir = Inner radius of tube.
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=DOWN`.
// ---
2019-03-23 04:13:18 +00:00
// od = Outer diameter of tube.
2021-01-17 09:38:10 +00:00
// id = Inner diameter of tube.
// wall = horizontal thickness of tube wall. Default 0.5
// or1 = Outer radius of bottom of tube. Default: value of r)
// or2 = Outer radius of top of tube. Default: value of r)
2019-03-23 04:13:18 +00:00
// od1 = Outer diameter of bottom of tube.
// od2 = Outer diameter of top of tube.
2019-02-24 12:35:40 +00:00
// ir1 = Inner radius of bottom of tube.
// ir2 = Inner radius of top of tube.
2019-02-24 07:26:15 +00:00
// id1 = Inner diameter of bottom of tube.
// id2 = Inner diameter of top of tube.
2019-03-23 04:13:18 +00:00
// realign = If true, rotate the tube by half the angle of one face.
2019-05-26 19:47:50 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
2019-03-23 04:13:18 +00:00
//
// Example: These all Produce the Same Tube
// tube(h=30, or=40, wall=5);
// tube(h=30, ir=35, wall=5);
// tube(h=30, or=40, ir=35);
// tube(h=30, od=80, id=70);
// Example: These all Produce the Same Conical Tube
// tube(h=30, or1=40, or2=25, wall=5);
// tube(h=30, ir1=35, or2=20, wall=5);
// tube(h=30, or1=40, or2=25, ir1=35, ir2=20);
// Example: Circular Wedge
// tube(h=30, or1=40, or2=30, ir1=20, ir2=30);
2019-04-22 08:08:41 +00:00
// Example: Standard Connectors
2019-04-23 03:55:03 +00:00
// tube(h=30, or=40, wall=5) show_anchors();
2019-02-27 11:56:34 +00:00
module tube (
2021-01-17 09:38:10 +00:00
h , or , ir , center ,
od , id , wall ,
or1 , or2 , od1 , od2 ,
ir1 , ir2 , id1 , id2 ,
realign = false , l ,
anchor , spin = 0 , orient = UP
2019-02-27 11:56:34 +00:00
) {
2020-05-30 02:04:34 +00:00
h = first_defined ( [ h , l , 1 ] ) ;
2021-01-17 09:38:10 +00:00
orr1 = get_radius ( r1 = or1 , r = or , d1 = od1 , d = od , dflt = undef ) ;
orr2 = get_radius ( r1 = or2 , r = or , d1 = od2 , d = od , dflt = undef ) ;
2020-11-07 22:23:09 +00:00
irr1 = get_radius ( r1 = ir1 , r = ir , d1 = id1 , d = id , dflt = undef ) ;
irr2 = get_radius ( r1 = ir2 , r = ir , d1 = id2 , d = id , dflt = undef ) ;
2021-01-17 09:38:10 +00:00
r1 = default ( orr1 , u_add ( irr1 , wall ) ) ;
r2 = default ( orr2 , u_add ( irr2 , wall ) ) ;
ir1 = default ( irr1 , u_sub ( orr1 , wall ) ) ;
ir2 = default ( irr2 , u_sub ( orr2 , wall ) ) ;
2020-05-30 02:04:34 +00:00
assert ( ir1 < = r1 , "Inner radius is larger than outer radius." ) ;
assert ( ir2 < = r2 , "Inner radius is larger than outer radius." ) ;
sides = segs ( max ( r1 , r2 ) ) ;
anchor = get_anchor ( anchor , center , BOT , BOT ) ;
attachable ( anchor , spin , orient , r1 = r1 , r2 = r2 , l = h ) {
zrot ( realign ? 180 / sides : 0 ) {
difference ( ) {
cyl ( h = h , r1 = r1 , r2 = r2 , $fn = sides ) children ( ) ;
cyl ( h = h + 0.05 , r1 = ir1 , r2 = ir2 ) ;
}
}
children ( ) ;
}
2020-05-19 05:57:50 +00:00
}
2017-08-30 00:00:16 +00:00
2021-09-20 22:34:22 +00:00
// Module: pie_slice()
//
// Description:
// Creates a pie slice shape.
//
// Usage: Typical
// pie_slice(l|h, r, ang, [center]);
// pie_slice(l|h, d=, ang=, ...);
// pie_slice(l|h, r1=|d1=, r2=|d2=, ang=, ...);
// Usage: Attaching Children
// pie_slice(l|h, r, ang, ...) [attachments];
//
// Arguments:
// h / l = height of pie slice.
// r = radius of pie slice.
// ang = pie slice angle in degrees.
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=UP`.
// ---
// r1 = bottom radius of pie slice.
// r2 = top radius of pie slice.
// d = diameter of pie slice.
// d1 = bottom diameter of pie slice.
// d2 = top diameter of pie slice.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
//
// Example: Cylindrical Pie Slice
// pie_slice(ang=45, l=20, r=30);
// Example: Conical Pie Slice
// pie_slice(ang=60, l=20, d1=50, d2=70);
module pie_slice (
h , r , ang = 30 , center ,
r1 , r2 , d , d1 , d2 , l ,
anchor , spin = 0 , orient = UP
) {
l = first_defined ( [ l , h , 1 ] ) ;
r1 = get_radius ( r1 = r1 , r = r , d1 = d1 , d = d , dflt = 10 ) ;
r2 = get_radius ( r1 = r2 , r = r , d1 = d2 , d = d , dflt = 10 ) ;
maxd = max ( r1 , r2 ) + 0.1 ;
anchor = get_anchor ( anchor , center , BOT , BOT ) ;
attachable ( anchor , spin , orient , r1 = r1 , r2 = r2 , l = l ) {
difference ( ) {
cyl ( r1 = r1 , r2 = r2 , h = l ) ;
if ( ang < 180 ) rotate ( ang ) back ( maxd / 2 ) cube ( [ 2 * maxd , maxd , l + 0.1 ] , center = true ) ;
difference ( ) {
fwd ( maxd / 2 ) cube ( [ 2 * maxd , maxd , l + 0.2 ] , center = true ) ;
if ( ang > 180 ) rotate ( ang - 180 ) back ( maxd / 2 ) cube ( [ 2 * maxd , maxd , l + 0.1 ] , center = true ) ;
}
}
children ( ) ;
}
}
2019-03-23 04:13:18 +00:00
2021-09-17 01:56:56 +00:00
// Section: Other Round Objects
2019-03-23 04:13:18 +00:00
2021-09-17 01:50:12 +00:00
// Function&Module: sphere()
// Topics: Shapes (3D), Attachable, VNF Generators
// Usage: As Module
// sphere(r|d=, [circum=], [style=], ...);
// Usage: With Attachments
// sphere(r|d=, ...) { attachments }
// Usage: As Function
// vnf = sphere(r|d=, [circum=], [style=], ...);
// See Also: spheroid()
// Description:
// Creates a sphere object, with support for anchoring and attachments.
// This is a drop-in replacement for the built-in `sphere()` module.
// When called as a function, returns a [VNF](vnf.scad) for a sphere.
// Arguments:
// r = Radius of the sphere.
// ---
// d = Diameter of the sphere.
// circum = If true, the sphere is made large enough to circumscribe the sphere of the ideal side. Otherwise inscribes. Default: false (inscribes)
// style = The style of the sphere's construction. One of "orig", "aligned", "stagger", "octa", or "icosa". Default: "orig"
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
// Example: By Radius
// sphere(r=50);
// Example: By Diameter
// sphere(d=100);
// Example: style="orig"
// sphere(d=100, style="orig", $fn=10);
// Example: style="aligned"
// sphere(d=100, style="aligned", $fn=10);
// Example: style="stagger"
// sphere(d=100, style="stagger", $fn=10);
// Example: style="icosa"
// sphere(d=100, style="icosa", $fn=10);
// // In "icosa" style, $fn is quantized
// // to the nearest multiple of 5.
// Example: Anchoring
// sphere(d=100, anchor=FRONT);
// Example: Spin
// sphere(d=100, anchor=FRONT, spin=45);
// Example: Orientation
// sphere(d=100, anchor=FRONT, spin=45, orient=FWD);
// Example: Standard Connectors
// sphere(d=50) show_anchors();
// Example: Called as Function
// vnf = sphere(d=100, style="icosa");
// vnf_polyhedron(vnf);
module sphere ( r , d , circum = false , style = "orig" , anchor = CENTER , spin = 0 , orient = UP )
spheroid ( r = r , d = d , circum = circum , style = style , anchor = anchor , spin = spin , orient = orient ) children ( ) ;
function sphere ( r , d , circum = false , style = "orig" , anchor = CENTER , spin = 0 , orient = UP ) =
spheroid ( r = r , d = d , circum = circum , style = style , anchor = anchor , spin = spin , orient = orient ) ;
2020-04-25 11:00:16 +00:00
// Function&Module: spheroid()
2021-01-17 09:38:10 +00:00
// Usage: Typical
2021-06-27 03:59:33 +00:00
// spheroid(r|d, [circum], [style]);
2021-01-17 09:38:10 +00:00
// Usage: Attaching Children
2021-06-27 03:59:33 +00:00
// spheroid(r|d, [circum], [style]) [attachments];
2020-04-25 11:00:16 +00:00
// Usage: As Function
2021-06-27 03:59:33 +00:00
// vnf = spheroid(r|d, [circum], [style]);
2019-04-17 02:16:50 +00:00
// Description:
2020-04-25 11:00:16 +00:00
// Creates a spheroid object, with support for anchoring and attachments.
// This is a drop-in replacement for the built-in `sphere()` module.
// When called as a function, returns a [VNF](vnf.scad) for a spheroid.
2020-12-28 05:54:31 +00:00
// The exact triangulation of this spheroid can be controlled via the `style=`
// argument, where the value can be one of `"orig"`, `"aligned"`, `"stagger"`,
// `"octa"`, or `"icosa"`:
// - `style="orig"` constructs a sphere the same way that the OpenSCAD `sphere()` built-in does.
// - `style="aligned"` constructs a sphere where, if `$fn` is a multiple of 4, it has vertices at all axis maxima and minima. ie: its bounding box is exactly the sphere diameter in length on all three axes. This is the default.
// - `style="stagger"` forms a sphere where all faces are triangular, but the top and bottom poles have thinner triangles.
// - `style="octa"` forms a sphere by subdividing an octahedron (8-sided platonic solid). This makes more uniform faces over the entirety of the sphere, and guarantees the bounding box is the sphere diameter in size on all axes. The effective `$fn` value is quantized to a multiple of 4, though. This is used in constructing rounded corners for various other shapes.
// - `style="icosa"` forms a sphere by subdividing an icosahedron (20-sided platonic solid). This makes even more uniform faces over the entirety of the sphere. The effective `$fn` value is quantized to a multiple of 5, though.
2019-04-17 02:16:50 +00:00
// Arguments:
2020-04-25 11:00:16 +00:00
// r = Radius of the spheroid.
2021-01-17 09:38:10 +00:00
// style = The style of the spheroid's construction. One of "orig", "aligned", "stagger", "octa", or "icosa". Default: "aligned"
// ---
2020-04-25 11:00:16 +00:00
// d = Diameter of the spheroid.
// circum = If true, the spheroid is made large enough to circumscribe the sphere of the ideal side. Otherwise inscribes. Default: false (inscribes)
2019-05-26 19:47:50 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
2019-04-22 08:08:41 +00:00
// Example: By Radius
2020-04-25 11:00:16 +00:00
// spheroid(r=50);
2019-04-22 08:08:41 +00:00
// Example: By Diameter
2020-04-25 11:00:16 +00:00
// spheroid(d=100);
// Example: style="orig"
// spheroid(d=100, style="orig", $fn=10);
// Example: style="aligned"
// spheroid(d=100, style="aligned", $fn=10);
// Example: style="stagger"
// spheroid(d=100, style="stagger", $fn=10);
2020-06-23 07:37:36 +00:00
// Example: style="octa", octahedral based tesselation.
// spheroid(d=100, style="octa", $fn=10);
// // In "octa" style, $fn is quantized
// // to the nearest multiple of 4.
// Example: style="icosa", icosahedral based tesselation.
2020-04-25 11:00:16 +00:00
// spheroid(d=100, style="icosa", $fn=10);
// // In "icosa" style, $fn is quantized
// // to the nearest multiple of 5.
// Example: Anchoring
// spheroid(d=100, anchor=FRONT);
// Example: Spin
// spheroid(d=100, anchor=FRONT, spin=45);
// Example: Orientation
// spheroid(d=100, anchor=FRONT, spin=45, orient=FWD);
2019-04-22 08:08:41 +00:00
// Example: Standard Connectors
2020-04-25 11:00:16 +00:00
// spheroid(d=50) show_anchors();
// Example: Called as Function
// vnf = spheroid(d=100, style="icosa");
// vnf_polyhedron(vnf);
2021-01-17 09:38:10 +00:00
module spheroid ( r , style = "aligned" , d , circum = false , anchor = CENTER , spin = 0 , orient = UP )
2019-04-17 02:16:50 +00:00
{
2020-05-30 02:04:34 +00:00
r = get_radius ( r = r , d = d , dflt = 1 ) ;
sides = segs ( r ) ;
2020-12-28 05:54:31 +00:00
vsides = ceil ( sides / 2 ) ;
2020-05-30 02:04:34 +00:00
attachable ( anchor , spin , orient , r = r ) {
if ( style = = "orig" ) {
2020-12-28 11:25:08 +00:00
merids = [ for ( i = [ 0 : 1 : vsides - 1 ] ) 90 - ( i + 0.5 ) * 180 / vsides ] ;
2020-12-28 05:54:31 +00:00
path = [
2020-12-28 11:31:51 +00:00
let ( a = merids [ 0 ] ) [ 0 , sin ( a ) ] ,
for ( a = merids ) [ cos ( a ) , sin ( a ) ] ,
2021-03-30 07:46:59 +00:00
let ( a = last ( merids ) ) [ 0 , sin ( a ) ]
2020-12-28 05:54:31 +00:00
] ;
2020-12-28 11:31:51 +00:00
scale ( r ) rotate ( 180 ) rotate_extrude ( convexity = 2 , $fn = sides ) polygon ( path ) ;
2020-05-30 02:04:34 +00:00
} else {
vnf = spheroid ( r = r , circum = circum , style = style ) ;
vnf_polyhedron ( vnf , convexity = 2 ) ;
}
children ( ) ;
}
2019-04-17 02:16:50 +00:00
}
2021-01-17 09:38:10 +00:00
function spheroid ( r , style = "aligned" , d , circum = false , anchor = CENTER , spin = 0 , orient = UP ) =
2020-05-30 02:04:34 +00:00
let (
r = get_radius ( r = r , d = d , dflt = 1 ) ,
hsides = segs ( r ) ,
vsides = max ( 2 , ceil ( hsides / 2 ) ) ,
2020-06-23 07:37:36 +00:00
octa_steps = round ( max ( 4 , hsides ) / 4 ) ,
2020-05-30 02:04:34 +00:00
icosa_steps = round ( max ( 5 , hsides ) / 5 ) ,
rr = circum ? ( r / cos ( 90 / vsides ) / cos ( 180 / hsides ) ) : r ,
stagger = style = = "stagger" ,
verts = style = = "orig" ? [
for ( i = [ 0 : 1 : vsides - 1 ] ) let ( phi = ( i + 0.5 ) * 180 / ( vsides ) )
for ( j = [ 0 : 1 : hsides - 1 ] ) let ( theta = j * 360 / hsides )
spherical_to_xyz ( rr , theta , phi ) ,
] : style = = "aligned" || style = = "stagger" ? [
spherical_to_xyz ( rr , 0 , 0 ) ,
for ( i = [ 1 : 1 : vsides - 1 ] ) let ( phi = i * 180 / vsides )
for ( j = [ 0 : 1 : hsides - 1 ] ) let ( theta = ( j + ( ( stagger && i % 2 ! = 0 ) ? 0.5 : 0 ) ) * 360 / hsides )
spherical_to_xyz ( rr , theta , phi ) ,
spherical_to_xyz ( rr , 0 , 180 )
2020-06-23 11:40:36 +00:00
] : style = = "octa" ? let (
meridians = [
1 ,
for ( i = [ 1 : 1 : octa_steps ] ) i * 4 ,
for ( i = [ octa_steps - 1 : - 1 : 1 ] ) i * 4 ,
1 ,
]
) [
for ( i = idx ( meridians ) , j = [ 0 : 1 : meridians [ i ] - 1 ] )
spherical_to_xyz ( rr , j * 360 / meridians [ i ] , i * 180 / ( len ( meridians ) - 1 ) )
2020-05-30 02:04:34 +00:00
] : style = = "icosa" ? [
for ( tb = [ 0 , 1 ] , j = [ 0 , 2 ] , i = [ 0 : 1 : 4 ] ) let (
theta0 = i * 360 / 5 ,
theta1 = ( i - 0.5 ) * 360 / 5 ,
theta2 = ( i + 0.5 ) * 360 / 5 ,
phi0 = 180 / 3 * j ,
phi1 = 180 / 3 ,
v0 = spherical_to_xyz ( 1 , theta0 , phi0 ) ,
v1 = spherical_to_xyz ( 1 , theta1 , phi1 ) ,
v2 = spherical_to_xyz ( 1 , theta2 , phi1 ) ,
ax0 = vector_axis ( v0 , v1 ) ,
ang0 = vector_angle ( v0 , v1 ) ,
ax1 = vector_axis ( v0 , v2 ) ,
ang1 = vector_angle ( v0 , v2 )
)
for ( k = [ 0 : 1 : icosa_steps ] ) let (
u = k / icosa_steps ,
vv0 = rot ( ang0 * u , ax0 , p = v0 ) ,
vv1 = rot ( ang1 * u , ax1 , p = v0 ) ,
ax2 = vector_axis ( vv0 , vv1 ) ,
ang2 = vector_angle ( vv0 , vv1 )
)
for ( l = [ 0 : 1 : k ] ) let (
v = k ? l / k : 0 ,
pt = rot ( ang2 * v , v = ax2 , p = vv0 ) * rr * ( tb ? - 1 : 1 )
) pt
2020-06-23 11:40:36 +00:00
] : assert ( in_list ( style , [ "orig" , "aligned" , "stagger" , "octa" , "icosa" ] ) ) ,
2020-05-30 02:04:34 +00:00
lv = len ( verts ) ,
faces = style = = "orig" ? [
[ for ( i = [ 0 : 1 : hsides - 1 ] ) hsides - i - 1 ] ,
[ for ( i = [ 0 : 1 : hsides - 1 ] ) lv - hsides + i ] ,
for ( i = [ 0 : 1 : vsides - 2 ] , j = [ 0 : 1 : hsides - 1 ] ) each [
[ ( i + 1 ) * hsides + j , i * hsides + j , i * hsides + ( j + 1 ) % hsides ] ,
[ ( i + 1 ) * hsides + j , i * hsides + ( j + 1 ) % hsides , ( i + 1 ) * hsides + ( j + 1 ) % hsides ] ,
]
] : style = = "aligned" || style = = "stagger" ? [
for ( i = [ 0 : 1 : hsides - 1 ] ) let (
b2 = lv - 2 - hsides
) each [
[ i + 1 , 0 , ( ( i + 1 ) % hsides ) + 1 ] ,
[ lv - 1 , b2 + i + 1 , b2 + ( ( i + 1 ) % hsides ) + 1 ] ,
] ,
for ( i = [ 0 : 1 : vsides - 3 ] , j = [ 0 : 1 : hsides - 1 ] ) let (
base = 1 + hsides * i
) each (
( stagger && i % 2 ! = 0 ) ? [
[ base + j , base + hsides + j % hsides , base + hsides + ( j + hsides - 1 ) % hsides ] ,
[ base + j , base + ( j + 1 ) % hsides , base + hsides + j ] ,
] : [
[ base + j , base + ( j + 1 ) % hsides , base + hsides + ( j + 1 ) % hsides ] ,
[ base + j , base + hsides + ( j + 1 ) % hsides , base + hsides + j ] ,
]
)
2020-06-23 07:37:36 +00:00
] : style = = "octa" ? let (
2020-06-23 11:40:36 +00:00
meridians = [
0 , 1 ,
for ( i = [ 1 : 1 : octa_steps ] ) i * 4 ,
for ( i = [ octa_steps - 1 : - 1 : 1 ] ) i * 4 ,
1 ,
] ,
offs = cumsum ( meridians ) ,
2021-03-30 07:46:59 +00:00
pc = last ( offs ) - 1 ,
2020-06-23 11:40:36 +00:00
os = octa_steps * 2
2020-06-23 07:37:36 +00:00
) [
2020-06-23 11:40:36 +00:00
for ( i = [ 0 : 1 : 3 ] ) [ 0 , 1 + ( i + 1 ) % 4 , 1 + i ] ,
for ( i = [ 0 : 1 : 3 ] ) [ pc - 0 , pc - ( 1 + ( i + 1 ) % 4 ) , pc - ( 1 + i ) ] ,
for ( i = [ 1 : 1 : octa_steps - 1 ] ) let (
m = meridians [ i + 2 ] / 4
2020-06-23 07:37:36 +00:00
)
2020-06-23 11:40:36 +00:00
for ( j = [ 0 : 1 : 3 ] , k = [ 0 : 1 : m - 1 ] ) let (
m1 = meridians [ i + 1 ] ,
m2 = meridians [ i + 2 ] ,
p1 = offs [ i + 0 ] + ( j * m1 / 4 + k + 0 ) % m1 ,
p2 = offs [ i + 0 ] + ( j * m1 / 4 + k + 1 ) % m1 ,
p3 = offs [ i + 1 ] + ( j * m2 / 4 + k + 0 ) % m2 ,
p4 = offs [ i + 1 ] + ( j * m2 / 4 + k + 1 ) % m2 ,
p5 = offs [ os - i + 0 ] + ( j * m1 / 4 + k + 0 ) % m1 ,
p6 = offs [ os - i + 0 ] + ( j * m1 / 4 + k + 1 ) % m1 ,
p7 = offs [ os - i - 1 ] + ( j * m2 / 4 + k + 0 ) % m2 ,
p8 = offs [ os - i - 1 ] + ( j * m2 / 4 + k + 1 ) % m2
) each [
[ p1 , p4 , p3 ] ,
if ( k < m - 1 ) [ p1 , p2 , p4 ] ,
[ p5 , p7 , p8 ] ,
if ( k < m - 1 ) [ p5 , p8 , p6 ] ,
] ,
2020-05-30 02:04:34 +00:00
] : style = = "icosa" ? let (
pyr = [ for ( x = [ 0 : 1 : icosa_steps + 1 ] ) x ] ,
tri = sum ( pyr ) ,
soff = cumsum ( pyr )
) [
for ( tb = [ 0 , 1 ] , j = [ 0 , 1 ] , i = [ 0 : 1 : 4 ] ) let (
base = ( ( ( ( tb * 2 ) + j ) * 5 ) + i ) * tri
)
for ( k = [ 0 : 1 : icosa_steps - 1 ] )
for ( l = [ 0 : 1 : k ] ) let (
v1 = base + soff [ k ] + l ,
v2 = base + soff [ k + 1 ] + l ,
v3 = base + soff [ k + 1 ] + ( l + 1 ) ,
faces = [
if ( l > 0 ) [ v1 - 1 , v1 , v2 ] ,
[ v1 , v3 , v2 ] ,
] ,
faces2 = ( tb + j ) % 2 ? [ for ( f = faces ) reverse ( f ) ] : faces
) each faces2
] : [ ]
) [ reorient ( anchor , spin , orient , r = r , p = verts ) , faces ] ;
2018-12-22 21:17:04 +00:00
2019-03-23 04:13:18 +00:00
2021-09-17 01:56:56 +00:00
// Module: torus()
//
// Usage: Typical
// torus(r_maj|d_maj, r_min|d_min, [center], ...);
// torus(or|od, ir|id, ...);
// torus(r_maj|d_maj, or|od, ...);
// torus(r_maj|d_maj, ir|id, ...);
// torus(r_min|d_min, or|od, ...);
// torus(r_min|d_min, ir|id, ...);
// Usage: Attaching Children
// torus(or|od, ir|id, ...) [attachments];
//
// Description:
// Creates a torus shape.
//
// Figure(2D,Med):
// module text3d(t,size=8) text(text=t,size=size,font="Helvetica", halign="center",valign="center");
// module dashcirc(r,start=0,angle=359.9,dashlen=5) let(step=360*dashlen/(2*r*PI)) for(a=[start:step:start+angle]) stroke(arc(r=r,start=a,angle=step/2));
// r = 75; r2 = 30;
// down(r2+0.1) #torus(r_maj=r, r_min=r2, $fn=72);
// color("blue") linear_extrude(height=0.01) {
// dashcirc(r=r,start=15,angle=45);
// dashcirc(r=r-r2, start=90+15, angle=60);
// dashcirc(r=r+r2, start=180+45, angle=30);
// dashcirc(r=r+r2, start=15, angle=30);
// }
// rot(240) color("blue") linear_extrude(height=0.01) {
// stroke([[0,0],[r+r2,0]], endcaps="arrow2",width=2);
// right(r) fwd(9) rot(-240) text3d("or",size=10);
// }
// rot(135) color("blue") linear_extrude(height=0.01) {
// stroke([[0,0],[r-r2,0]], endcaps="arrow2",width=2);
// right((r-r2)/2) back(8) rot(-135) text3d("ir",size=10);
// }
// rot(45) color("blue") linear_extrude(height=0.01) {
// stroke([[0,0],[r,0]], endcaps="arrow2",width=2);
// right(r/2) back(8) text3d("r_maj",size=9);
// }
// rot(30) color("blue") linear_extrude(height=0.01) {
// stroke([[r,0],[r+r2,0]], endcaps="arrow2",width=2);
// right(r+r2/2) fwd(8) text3d("r_min",size=7);
// }
//
// Arguments:
// r_maj = major radius of torus ring. (use with 'r_min', or 'd_min')
// r_min = minor radius of torus ring. (use with 'r_maj', or 'd_maj')
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=DOWN`.
// ---
// d_maj = major diameter of torus ring. (use with 'r_min', or 'd_min')
// d_min = minor diameter of torus ring. (use with 'r_maj', or 'd_maj')
// or = outer radius of the torus. (use with 'ir', or 'id')
// ir = inside radius of the torus. (use with 'or', or 'od')
// od = outer diameter of the torus. (use with 'ir' or 'id')
// id = inside diameter of the torus. (use with 'or' or 'od')
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
//
// Example:
// // These all produce the same torus.
// torus(r_maj=22.5, r_min=7.5);
// torus(d_maj=45, d_min=15);
// torus(or=30, ir=15);
// torus(od=60, id=30);
// torus(d_maj=45, id=30);
// torus(d_maj=45, od=60);
// torus(d_min=15, id=30);
// torus(d_min=15, od=60);
// Example: Standard Connectors
// torus(od=60, id=30) show_anchors();
module torus (
r_maj , r_min , center ,
d_maj , d_min ,
or , od , ir , id ,
anchor , spin = 0 , orient = UP
) {
_or = get_radius ( r = or , d = od , dflt = undef ) ;
_ir = get_radius ( r = ir , d = id , dflt = undef ) ;
_r_maj = get_radius ( r = r_maj , d = d_maj , dflt = undef ) ;
_r_min = get_radius ( r = r_min , d = d_min , dflt = undef ) ;
majrad = is_finite ( _r_maj ) ? _r_maj :
is_finite ( _ir ) && is_finite ( _or ) ? ( _or + _ir ) / 2 :
is_finite ( _ir ) && is_finite ( _r_min ) ? ( _ir + _r_min ) :
is_finite ( _or ) && is_finite ( _r_min ) ? ( _or - _r_min ) :
assert ( false , "Bad Parameters" ) ;
minrad = is_finite ( _r_min ) ? _r_min :
is_finite ( _ir ) ? ( majrad - _ir ) :
is_finite ( _or ) ? ( _or - majrad ) :
assert ( false , "Bad Parameters" ) ;
anchor = get_anchor ( anchor , center , BOT , CENTER ) ;
attachable ( anchor , spin , orient , r = ( majrad + minrad ) , l = minrad * 2 ) {
rotate_extrude ( convexity = 4 ) {
right ( majrad ) circle ( r = minrad ) ;
}
children ( ) ;
}
}
2019-03-23 04:13:18 +00:00
// Module: teardrop()
//
// Description:
// Makes a teardrop shape in the XZ plane. Useful for 3D printable holes.
//
2021-01-17 09:38:10 +00:00
// Usage: Typical
2021-06-27 03:59:33 +00:00
// teardrop(h|l, r, [ang], [cap_h], ...);
// teardrop(h|l, d=, [ang=], [cap_h=], ...);
2021-06-20 04:27:17 +00:00
// Usage: Psuedo-Conical
2021-06-27 03:59:33 +00:00
// teardrop(h|l, r1=, r2=, [ang=], [cap_h1=], [cap_h2=], ...);
// teardrop(h|l, d1=, d2=, [ang=], [cap_h1=], [cap_h2=], ...);
2021-01-17 09:38:10 +00:00
// Usage: Attaching Children
2021-06-27 03:59:33 +00:00
// teardrop(h|l, r, ...) [attachments];
2019-03-23 04:13:18 +00:00
//
// Arguments:
2021-01-17 09:38:10 +00:00
// h / l = Thickness of teardrop. Default: 1
// r = Radius of circular part of teardrop. Default: 1
// ang = Angle of hat walls from the Z axis. Default: 45 degrees
// cap_h = If given, height above center where the shape will be truncated. Default: `undef` (no truncation)
// ---
2021-06-20 04:27:17 +00:00
// r1 = Radius of circular portion of the front end of the teardrop shape.
// r2 = Radius of circular portion of the back end of the teardrop shape.
// d = Diameter of circular portion of the teardrop shape.
// d1 = Diameter of circular portion of the front end of the teardrop shape.
// d2 = Diameter of circular portion of the back end of the teardrop shape.
// cap_h1 = If given, height above center where the shape will be truncated, on the front side. Default: `undef` (no truncation)
// cap_h2 = If given, height above center where the shape will be truncated, on the back side. Default: `undef` (no truncation)
2019-05-26 19:47:50 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
2019-08-30 01:00:56 +00:00
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
2019-03-23 04:13:18 +00:00
//
2021-06-20 04:27:17 +00:00
// Extra Anchors:
// cap = The center of the top of the cap, oriented with the cap face normal.
// cap_fwd = The front edge of the cap.
// cap_back = The back edge of the cap.
//
2019-03-23 04:13:18 +00:00
// Example: Typical Shape
// teardrop(r=30, h=10, ang=30);
// Example: Crop Cap
// teardrop(r=30, h=10, ang=30, cap_h=40);
// Example: Close Crop
// teardrop(r=30, h=10, ang=30, cap_h=20);
2021-06-20 04:27:17 +00:00
// Example: Psuedo-Conical
// teardrop(r1=20, r2=30, h=40, cap_h1=25, cap_h2=35);
// Example: Standard Conical Connectors
// teardrop(d1=20, d2=30, h=20, cap_h1=11, cap_h2=16)
// show_anchors(custom=false);
// Example(Spin,VPD=275): Named Conical Connectors
// teardrop(d1=20, d2=30, h=20, cap_h1=11, cap_h2=16)
// show_anchors(std=false);
module teardrop ( h , r , ang = 45 , cap_h , r1 , r2 , d , d1 , d2 , cap_h1 , cap_h2 , l , anchor = CENTER , spin = 0 , orient = UP )
2019-03-23 04:13:18 +00:00
{
2021-06-20 04:27:17 +00:00
r1 = get_radius ( r = r , r1 = r1 , d = d , d1 = d1 , dflt = 1 ) ;
r2 = get_radius ( r = r , r1 = r2 , d = d , d1 = d2 , dflt = 1 ) ;
2020-05-30 02:04:34 +00:00
l = first_defined ( [ l , h , 1 ] ) ;
2021-06-20 04:27:17 +00:00
tip_y1 = adj_ang_to_hyp ( r1 , 90 - ang ) ;
tip_y2 = adj_ang_to_hyp ( r2 , 90 - ang ) ;
cap_h1 = min ( first_defined ( [ cap_h1 , cap_h , tip_y1 ] ) , tip_y1 ) ;
cap_h2 = min ( first_defined ( [ cap_h2 , cap_h , tip_y2 ] ) , tip_y2 ) ;
capvec = unit ( [ 0 , cap_h1 - cap_h2 , l ] ) ;
2021-01-17 09:38:10 +00:00
anchors = [
2021-09-16 22:32:12 +00:00
named_anchor ( "cap" , [ 0 , 0 , ( cap_h1 + cap_h2 ) / 2 ] , capvec ) ,
named_anchor ( "cap_fwd" , [ 0 , - l / 2 , cap_h1 ] , unit ( ( capvec + FWD ) / 2 ) ) ,
named_anchor ( "cap_back" , [ 0 , + l / 2 , cap_h2 ] , unit ( ( capvec + BACK ) / 2 ) , 180 ) ,
2021-01-17 09:38:10 +00:00
] ;
2021-06-20 04:27:17 +00:00
attachable ( anchor , spin , orient , r1 = r1 , r2 = r2 , l = l , axis = BACK , anchors = anchors ) {
2020-05-30 02:04:34 +00:00
rot ( from = UP , to = FWD ) {
2020-11-30 04:23:03 +00:00
if ( l > 0 ) {
2021-06-20 04:27:17 +00:00
if ( r1 = = r2 ) {
linear_extrude ( height = l , center = true , slices = 2 ) {
teardrop2d ( r = r1 , ang = ang , cap_h = cap_h ) ;
}
} else {
hull ( ) {
up ( l / 2 - 0.001 ) {
linear_extrude ( height = 0.001 , center = false ) {
teardrop2d ( r = r1 , ang = ang , cap_h = cap_h1 ) ;
}
}
down ( l / 2 ) {
linear_extrude ( height = 0.001 , center = false ) {
teardrop2d ( r = r2 , ang = ang , cap_h = cap_h2 ) ;
}
}
}
2020-11-30 04:23:03 +00:00
}
2020-05-30 02:04:34 +00:00
}
}
children ( ) ;
}
2019-03-23 04:13:18 +00:00
}
// Module: onion()
//
// Description:
// Creates a sphere with a conical hat, to make a 3D teardrop.
//
// Usage:
2021-06-27 03:59:33 +00:00
// onion(r|d, [ang], [cap_h]);
2021-01-17 09:38:10 +00:00
// Usage: Typical
2021-06-27 03:59:33 +00:00
// onion(r, [ang], [cap_h], ...);
// onion(d=, [ang=], [cap_h=], ...);
2021-01-17 09:38:10 +00:00
// Usage: Attaching Children
2021-06-27 03:59:33 +00:00
// onion(r, ...) [attachments];
2019-03-23 04:13:18 +00:00
//
// Arguments:
2021-01-17 09:38:10 +00:00
// r = radius of spherical portion of the bottom. Default: 1
// ang = Angle of cone on top from vertical. Default: 45 degrees
// cap_h = If given, height above sphere center to truncate teardrop shape. Default: `undef` (no truncation)
// ---
2019-03-23 04:13:18 +00:00
// d = diameter of spherical portion of bottom.
2019-05-26 19:47:50 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
2019-03-23 04:13:18 +00:00
//
// Example: Typical Shape
2021-01-17 09:38:10 +00:00
// onion(r=30, ang=30);
2019-03-23 04:13:18 +00:00
// Example: Crop Cap
2021-01-17 09:38:10 +00:00
// onion(r=30, ang=30, cap_h=40);
2019-03-23 04:13:18 +00:00
// Example: Close Crop
2021-01-17 09:38:10 +00:00
// onion(r=30, ang=30, cap_h=20);
2019-04-22 08:08:41 +00:00
// Example: Standard Connectors
2021-01-17 09:38:10 +00:00
// onion(r=30, ang=30, cap_h=40) show_anchors();
module onion ( r , ang = 45 , cap_h , d , anchor = CENTER , spin = 0 , orient = UP )
2019-03-23 04:13:18 +00:00
{
2020-05-30 02:04:34 +00:00
r = get_radius ( r = r , d = d , dflt = 1 ) ;
2021-04-12 07:40:09 +00:00
tip_y = adj_ang_to_hyp ( r , 90 - ang ) ;
cap_h = min ( default ( cap_h , tip_y ) , tip_y ) ;
2020-05-30 02:04:34 +00:00
anchors = [
2021-04-12 07:40:09 +00:00
[ "cap" , [ 0 , 0 , cap_h ] , UP , 0 ]
2020-05-30 02:04:34 +00:00
] ;
attachable ( anchor , spin , orient , r = r , anchors = anchors ) {
rotate_extrude ( convexity = 2 ) {
difference ( ) {
2021-01-17 09:38:10 +00:00
teardrop2d ( r = r , ang = ang , cap_h = cap_h ) ;
2021-04-12 09:30:50 +00:00
left ( r ) square ( size = [ 2 * r , 2 * max ( cap_h , r ) + 1 ] , center = true ) ;
2020-05-30 02:04:34 +00:00
}
}
children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
2021-09-17 01:50:12 +00:00
// Section: Text
// Module: atext()
// Topics: Attachments, Text
// Usage:
// atext(text, [h], [size], [font]);
// Description:
// Creates a 3D text block that can be attached to other attachable objects.
// NOTE: This cannot have children attached to it.
// Arguments:
// text = The text string to instantiate as an object.
// h = The height to which the text should be extruded. Default: 1
// size = The font size used to create the text block. Default: 10
// font = The name of the font used to create the text block. Default: "Courier"
// ---
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"baseline"`
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP`
// See Also: attachable()
// Extra Anchors:
// "baseline" = Anchors at the baseline of the text, at the start of the string.
// str("baseline",VECTOR) = Anchors at the baseline of the text, modified by the X and Z components of the appended vector.
// Examples:
// atext("Foobar", h=3, size=10);
// atext("Foobar", h=2, size=12, font="Helvetica");
// atext("Foobar", h=2, anchor=CENTER);
// atext("Foobar", h=2, anchor=str("baseline",CENTER));
// atext("Foobar", h=2, anchor=str("baseline",BOTTOM+RIGHT));
// Example: Using line_of() distributor
// txt = "This is the string.";
// line_of(spacing=[10,-5],n=len(txt))
// atext(txt[$idx], size=10, anchor=CENTER);
// Example: Using arc_of() distributor
// txt = "This is the string";
// arc_of(r=50, n=len(txt), sa=0, ea=180)
// atext(select(txt,-1-$idx), size=10, anchor=str("baseline",CENTER), spin=-90);
module atext ( text , h = 1 , size = 9 , font = "Courier" , anchor = "baseline" , spin = 0 , orient = UP ) {
no_children ( $children ) ;
dummy1 =
assert ( is_undef ( anchor ) || is_vector ( anchor ) || is_string ( anchor ) , str ( "Got: " , anchor ) )
assert ( is_undef ( spin ) || is_vector ( spin , 3 ) || is_num ( spin ) , str ( "Got: " , spin ) )
assert ( is_undef ( orient ) || is_vector ( orient , 3 ) , str ( "Got: " , orient ) ) ;
anchor = default ( anchor , CENTER ) ;
spin = default ( spin , 0 ) ;
orient = default ( orient , UP ) ;
geom = _attach_geom ( size = [ size , size , h ] ) ;
anch = ! any ( [ for ( c = anchor ) c = = "[" ] ) ? anchor :
let (
parts = str_split ( str_split ( str_split ( anchor , "]" ) [ 0 ] , "[" ) [ 1 ] , "," ) ,
vec = [ for ( p = parts ) str_float ( str_strip_leading ( p , " " ) ) ]
) vec ;
ha = anchor = = "baseline" ? "left" :
anchor = = anch && is_string ( anchor ) ? "center" :
anch . x < 0 ? "left" :
anch . x > 0 ? "right" :
"center" ;
va = starts_with ( anchor , "baseline" ) ? "baseline" :
anchor = = anch && is_string ( anchor ) ? "center" :
anch . y < 0 ? "bottom" :
anch . y > 0 ? "top" :
"center" ;
base = anchor = = "baseline" ? CENTER :
anchor = = anch && is_string ( anchor ) ? CENTER :
anch . z < 0 ? BOTTOM :
anch . z > 0 ? TOP :
CENTER ;
m = _attach_transform ( base , spin , orient , geom ) ;
multmatrix ( m ) {
$ parent_anchor = anchor ;
$ parent_spin = spin ;
$ parent_orient = orient ;
$ parent_geom = geom ;
$ parent_size = _attach_geom_size ( geom ) ;
$ attach_to = undef ;
do_show = _attachment_is_shown ( $t ags ) ;
if ( do_show ) {
if ( is_undef ( $ color ) ) {
linear_extrude ( height = h , center = true )
text ( text = text , size = size , halign = ha , valign = va , font = font ) ;
} else color ( $ color ) {
$ color = undef ;
linear_extrude ( height = h , center = true )
text ( text = text , size = size , halign = ha , valign = va , font = font ) ;
}
}
}
}
2021-09-18 23:11:08 +00:00
// This could be replaced with _cut_to_seg_u_form
2021-09-17 01:50:12 +00:00
function _cut_interp ( pathcut , path , data ) =
[ for ( entry = pathcut )
let (
a = path [ entry [ 1 ] - 1 ] ,
b = path [ entry [ 1 ] ] ,
c = entry [ 0 ] ,
i = max_index ( v_abs ( b - a ) ) ,
factor = ( c [ i ] - a [ i ] ) / ( b [ i ] - a [ i ] )
)
( 1 - factor ) * data [ entry [ 1 ] - 1 ] + factor * data [ entry [ 1 ] ]
] ;
2021-09-18 23:11:08 +00:00
2021-09-17 01:50:12 +00:00
// Module: path_text()
// Usage:
// path_text(path, text, [size], [thickness], [font], [lettersize], [offset], [reverse], [normal], [top], [textmetrics])
// Description:
// Place the text letter by letter onto the specified path using textmetrics (if available and requested)
// or user specified letter spacing. The path can be 2D or 3D. In 2D the text appears along the path with letters upright
// as determined by the path direction. In 3D by default letters are positioned on the tangent line to the path with the path normal
// pointing toward the reader. The path normal points away from the center of curvature (the opposite of the normal produced
// by path_normals()). Note that this means that if the center of curvature switches sides the text will flip upside down.
// If you want text on such a path you must supply your own normal or top vector.
// .
// Text appears starting at the beginning of the path, so if the 3D path moves right to left
// then a left-to-right reading language will display in the wrong order. (For a 2D path text will appear upside down.)
// The text for a 3D path appears positioned to be read from "outside" of the curve (from a point on the other side of the
// curve from the center of curvature). If you need the text to read properly from the inside, you can set reverse to
// true to flip the text, or supply your own normal.
// .
// If you do not have the experimental textmetrics feature enabled then you must specify the space for the letters
// using lettersize, which can be a scalar or array. You will have the easiest time getting good results by using
// a monospace font such as Courier. Note that even with text metrics, spacing may be different because path_text()
// doesn't do kerning to adjust positions of individual glyphs. Also if your font has ligatures they won't be used.
// .
// By default letters appear centered on the path. The offset can be specified to shift letters toward the reader (in
// the direction of the normal).
// .
// You can specify your own normal by setting `normal` to a direction or a list of directions. Your normal vector should
// point toward the reader. You can also specify
// top, which directs the top of the letters in a desired direction. If you specify your own directions and they
// are not perpendicular to the path then the direction you specify will take priority and the
// letters will not rest on the tangent line of the path. Note that the normal or top directions that you
// specify must not be parallel to the path.
// Arguments:
// path = path to place the text on
// text = text to create
// size = font size
// thickness = thickness of letters (not allowed for 2D path)
// font = font to use
// ---
// lettersize = scalar or array giving size of letters
// offset = distance to shift letters "up" (towards the reader). Not allowed for 2D path. Default: 0
// normal = direction or list of directions pointing towards the reader of the text. Not allowed for 2D path.
// top = direction or list of directions pointing toward the top of the text
// reverse = reverse the letters if true. Not allowed for 2D path. Default: false
// textmetrics = if set to true and lettersize is not given then use the experimental textmetrics feature. You must be running a dev snapshot that includes this feature and have the feature turned on in your preferences. Default: false
// Example: The examples use Courier, a monospaced font. The width is 1/1.2 times the specified size for this font. This text could wrap around a cylinder.
// path = path3d(arc(100, r=25, angle=[245, 370]));
// color("red")stroke(path, width=.3);
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2);
// Example: By setting the normal to UP we can get text that lies flat, for writing around the edge of a disk:
// path = path3d(arc(100, r=25, angle=[245, 370]));
// color("red")stroke(path, width=.3);
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, normal=UP);
// Example: If we want text that reads from the other side we can use reverse. Note we have to reverse the direction of the path and also set the reverse option.
// path = reverse(path3d(arc(100, r=25, angle=[65, 190])));
// color("red")stroke(path, width=.3);
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, reverse=true);
// Example: text debossed onto a cylinder in a spiral. The text is 1 unit deep because it is half in, half out.
// text = ("A long text example to wrap around a cylinder, possibly for a few times.");
// L = 5*len(text);
// maxang = 360*L/(PI*50);
// spiral = [for(a=[0:1:maxang]) [25*cos(a), 25*sin(a), 10-30/maxang*a]];
// difference(){
// cyl(d=50, l=50, $fn=120);
// path_text(spiral, text, size=5, lettersize=5/1.2, font="Courier", thickness=2);
// }
// Example: Same example but text embossed. Make sure you have enough depth for the letters to fully overlap the object.
// text = ("A long text example to wrap around a cylinder, possibly for a few times.");
// L = 5*len(text);
// maxang = 360*L/(PI*50);
// spiral = [for(a=[0:1:maxang]) [25*cos(a), 25*sin(a), 10-30/maxang*a]];
// cyl(d=50, l=50, $fn=120);
// path_text(spiral, text, size=5, lettersize=5/1.2, font="Courier", thickness=2);
// Example: Here the text baseline sits on the path. (Note the default orientation makes text readable from below, so we specify the normal.)
// path = arc(100, points = [[-20, 0, 20], [0,0,5], [20,0,20]]);
// color("red")stroke(path,width=.2);
// path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Courier", normal=FRONT);
// Example: If we use top to orient the text upward, the text baseline is no longer aligned with the path.
// path = arc(100, points = [[-20, 0, 20], [0,0,5], [20,0,20]]);
// color("red")stroke(path,width=.2);
// path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Courier", top=UP);
// Example: This sine wave wrapped around the cylinder has a twisting normal that produces wild letter layout. We fix it with a custom normal which is different at every path point.
// path = [for(theta = [0:360]) [25*cos(theta), 25*sin(theta), 4*cos(theta*4)]];
// normal = [for(theta = [0:360]) [cos(theta), sin(theta),0]];
// zrot(-120)
// difference(){
// cyl(r=25, h=20, $fn=120);
// path_text(path, "A sine wave wiggles", font="Courier", lettersize=5/1.2, size=5, normal=normal);
// }
// Example: The path center of curvature changes, and the text flips.
// path = zrot(-120,p=path3d( concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180])))));
// color("red")stroke(path,width=.2);
// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier", thickness=2);
// Example: We can fix it with top:
// path = zrot(-120,p=path3d( concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180])))));
// color("red")stroke(path,width=.2);
// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier", thickness=2, top=UP);
// Example(2D): With a 2D path instead of 3D there's no ambiguity about direction and it works by default:
// path = zrot(-120,p=concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180]))));
// color("red")stroke(path,width=.2);
// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier");
module path_text ( path , text , font , size , thickness , lettersize , offset = 0 , reverse = false , normal , top , textmetrics = false )
{
dummy2 = assert ( is_path ( path , [ 2 , 3 ] ) , "Must supply a 2d or 3d path" )
assert ( num_defined ( [ normal , top ] ) < = 1 , "Cannot define both \"normal\" and \"top\"" ) ;
dim = len ( path [ 0 ] ) ;
normalok = is_undef ( normal ) || is_vector ( normal , 3 ) || ( is_path ( normal , 3 ) && len ( normal ) = = len ( path ) ) ;
topok = is_undef ( top ) || is_vector ( top , dim ) || ( dim = = 2 && is_vector ( top , 3 ) && top [ 2 ] = = 0 )
|| ( is_path ( top , dim ) && len ( top ) = = len ( path ) ) ;
dummy4 = assert ( dim = = 3 || is_undef ( thickness ) , "Cannot give a thickness with 2d path" )
assert ( dim = = 3 || ! reverse , "Reverse not allowed with 2d path" )
assert ( dim = = 3 || offset = = 0 , "Cannot give offset with 2d path" )
assert ( dim = = 3 || is_undef ( normal ) , "Cannot define \"normal\" for a 2d path, only \"top\"" )
assert ( normalok , "\"normal\" must be a vector or path compatible with the given path" )
assert ( topok , "\"top\" must be a vector or path compatible with the given path" ) ;
thickness = first_defined ( [ thickness , 1 ] ) ;
normal = is_vector ( normal ) ? repeat ( normal , len ( path ) )
: is_def ( normal ) ? normal
: undef ;
top = is_vector ( top ) ? repeat ( dim = = 2 ? point2d ( top ) : top , len ( path ) )
: is_def ( top ) ? top
: undef ;
lsize = is_def ( lettersize ) ? force_list ( lettersize , len ( text ) )
: textmetrics ? [ for ( letter = text ) let ( t = textmetrics ( letter , font = font , size = size ) ) t . advance [ 0 ] ]
: assert ( false , "textmetrics disabled: Must specify letter size" ) ;
dummy1 = assert ( sum ( lsize ) < = path_length ( path ) , "Path is too short for the text" ) ;
2021-09-19 02:27:55 +00:00
pts = _path_cut_points ( path , add_scalar ( [ 0 , each cumsum ( lsize ) ] , lsize [ 0 ] / 2 ) , direction = true ) ;
2021-09-17 01:50:12 +00:00
usernorm = is_def ( normal ) ;
usetop = is_def ( top ) ;
normpts = is_undef ( normal ) ? ( reverse ? 1 : - 1 ) * subindex ( pts , 3 ) : _cut_interp ( pts , path , normal ) ;
toppts = is_undef ( top ) ? undef : _cut_interp ( pts , path , top ) ;
for ( i = idx ( text ) )
let ( tangent = pts [ i ] [ 2 ] )
assert ( ! usetop || ! approx ( tangent * toppts [ i ] , norm ( top [ i ] ) * norm ( tangent ) ) ,
str ( "Specified top direction parallel to path at character " , i ) )
assert ( usetop || ! approx ( tangent * normpts [ i ] , norm ( normpts [ i ] ) * norm ( tangent ) ) ,
str ( "Specified normal direction parallel to path at character " , i ) )
let (
adjustment = usetop ? ( tangent * toppts [ i ] ) * toppts [ i ] / ( toppts [ i ] * toppts [ i ] )
: usernorm ? ( tangent * normpts [ i ] ) * normpts [ i ] / ( normpts [ i ] * normpts [ i ] )
: [ 0 , 0 , 0 ]
)
move ( pts [ i ] [ 0 ] )
if ( dim = = 3 ) {
frame_map ( x = tangent - adjustment ,
z = usetop ? undef : normpts [ i ] ,
y = usetop ? toppts [ i ] : undef )
up ( offset - thickness / 2 )
linear_extrude ( height = thickness )
left ( lsize [ 0 ] / 2 ) text ( text [ i ] , font = font , size = size ) ;
} else {
frame_map ( x = point3d ( tangent - adjustment ) , y = point3d ( usetop ? toppts [ i ] : - normpts [ i ] ) )
left ( lsize [ 0 ] / 2 ) text ( text [ i ] , font = font , size = size ) ;
}
}
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Section: Miscellaneous
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: nil()
//
// Description:
// Useful when you MUST pass a child to a module, but you want it to be nothing.
2019-04-22 08:08:41 +00:00
module nil ( ) union ( ) { }
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: noop()
//
// Description:
2021-01-17 09:38:10 +00:00
// Passes through the children passed to it, with no action at all. Useful while debugging when
// you want to replace a command. This is an attachable non-object.
2019-05-26 06:31:05 +00:00
//
// Arguments:
2019-05-26 19:47:50 +00:00
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
2020-02-29 21:16:15 +00:00
module noop ( spin = 0 , orient = UP ) attachable ( CENTER , spin , orient , d = 0.01 ) { nil ( ) ; children ( ) ; }
2019-03-23 04:13:18 +00:00
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: interior_fillet()
//
// Description:
// Creates a shape that can be unioned into a concave joint between two faces, to fillet them.
2020-04-07 01:53:12 +00:00
// Center this part along the concave edge to be chamfered and union it in.
2019-03-23 04:13:18 +00:00
//
2021-01-17 09:38:10 +00:00
// Usage: Typical
2021-06-27 03:59:33 +00:00
// interior_fillet(l, r, [ang], [overlap], ...);
// interior_fillet(l, d=, [ang=], [overlap=], ...);
2021-01-17 09:38:10 +00:00
// Usage: Attaching Children
2021-06-27 03:59:33 +00:00
// interior_fillet(l, r, [ang], [overlap], ...) [attachments];
2019-03-23 04:13:18 +00:00
//
// Arguments:
2020-08-27 03:39:45 +00:00
// l = Length of edge to fillet.
// r = Radius of fillet.
// ang = Angle between faces to fillet.
// overlap = Overlap size for unioning with faces.
2021-01-17 09:38:10 +00:00
// ---
// d = Diameter of fillet.
2020-01-14 03:06:56 +00:00
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `FRONT+LEFT`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
2019-03-23 04:13:18 +00:00
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// union() {
2021-05-22 08:41:25 +00:00
// translate([0,2,-4])
// cube([20, 4, 24], anchor=BOTTOM);
// translate([0,-10,-4])
// cube([20, 20, 4], anchor=BOTTOM);
// color("green")
// interior_fillet(
// l=20, r=10,
// spin=180, orient=RIGHT
// );
2019-03-23 04:13:18 +00:00
// }
//
// Example:
2020-01-14 07:54:12 +00:00
// interior_fillet(l=40, r=10, spin=-90);
2020-01-14 03:06:56 +00:00
//
// Example: Using with Attachments
// cube(50,center=true) {
// position(FRONT+LEFT)
// interior_fillet(l=50, r=10, spin=-90);
// position(BOT+FRONT)
// interior_fillet(l=50, r=10, spin=180, orient=RIGHT);
// }
2020-08-27 03:39:45 +00:00
module interior_fillet ( l = 1.0 , r , ang = 90 , overlap = 0.01 , d , anchor = FRONT + LEFT , spin = 0 , orient = UP ) {
r = get_radius ( r = r , d = d , dflt = 1 ) ;
2020-05-30 02:04:34 +00:00
dy = r / tan ( ang / 2 ) ;
steps = ceil ( segs ( r ) * ang / 360 ) ;
step = ang / steps ;
attachable ( anchor , spin , orient , size = [ r , r , l ] ) {
2020-11-30 04:23:03 +00:00
if ( l > 0 ) {
linear_extrude ( height = l , convexity = 4 , center = true ) {
path = concat (
[ [ 0 , 0 ] ] ,
[ for ( i = [ 0 : 1 : steps ] ) let ( a = 270 - i * step ) r * [ cos ( a ) , sin ( a ) ] + [ dy , r ] ]
) ;
translate ( - [ r , r ] / 2 ) polygon ( path ) ;
}
2020-05-30 02:04:34 +00:00
}
children ( ) ;
}
2019-03-23 04:13:18 +00:00
}
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
2021-01-01 08:59:37 +00:00
// Function&Module: heightfield()
// Usage: As Module
2021-06-27 03:59:33 +00:00
// heightfield(data, [size], [bottom], [maxz], [xrange], [yrange], [style], [convexity], ...);
2021-01-17 09:38:10 +00:00
// Usage: Attaching Children
2021-06-27 03:59:33 +00:00
// heightfield(data, [size], ...) [attachments];
2021-01-01 08:59:37 +00:00
// Usage: As Function
2021-06-27 03:59:33 +00:00
// vnf = heightfield(data, [size], [bottom], [maxz], [xrange], [yrange], [style], ...);
2019-07-14 22:10:13 +00:00
// Description:
2021-01-01 08:59:37 +00:00
// Given a regular rectangular 2D grid of scalar values, or a function literal, generates a 3D
// surface where the height at any given point is the scalar value for that position.
2019-07-14 22:10:13 +00:00
// Arguments:
2021-01-01 08:59:37 +00:00
// data = This is either the 2D rectangular array of heights, or a function literal that takes X and Y arguments.
// size = The [X,Y] size of the surface to create. If given as a scalar, use it for both X and Y sizes. Default: `[100,100]`
// bottom = The Z coordinate for the bottom of the heightfield object to create. Any heights lower than this will be truncated to very slightly above this height. Default: -20
// maxz = The maximum height to model. Truncates anything taller to this height. Default: 99
// xrange = A range of values to iterate X over when calculating a surface from a function literal. Default: [-1 : 0.01 : 1]
// yrange = A range of values to iterate Y over when calculating a surface from a function literal. Default: [-1 : 0.01 : 1]
// style = The style of subdividing the quads into faces. Valid options are "default", "alt", and "quincunx". Default: "default"
2021-01-17 09:38:10 +00:00
// ---
2021-01-01 08:59:37 +00:00
// convexity = Max number of times a line could intersect a wall of the surface being formed. Module only. Default: 10
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP`
2019-07-14 22:10:13 +00:00
// Example:
2021-01-01 08:59:37 +00:00
// heightfield(size=[100,100], bottom=-20, data=[
2021-05-22 08:41:25 +00:00
// for (y=[-180:4:180]) [
// for(x=[-180:4:180])
// 10*cos(3*norm([x,y]))
// ]
2019-07-14 22:10:13 +00:00
// ]);
// Example:
// intersection() {
2021-01-01 08:59:37 +00:00
// heightfield(size=[100,100], data=[
2021-05-22 08:41:25 +00:00
// for (y=[-180:5:180]) [
// for(x=[-180:5:180])
// 10+5*cos(3*x)*sin(3*y)
// ]
2019-07-14 22:10:13 +00:00
// ]);
// cylinder(h=50,d=100);
// }
2021-05-22 08:41:25 +00:00
// Example: Heightfield by Function
2021-01-01 08:59:37 +00:00
// fn = function (x,y) 10*sin(x*360)*cos(y*360);
// heightfield(size=[100,100], data=fn);
2021-05-22 08:41:25 +00:00
// Example: Heightfield by Function, with Specific Ranges
2021-01-01 08:59:37 +00:00
// fn = function (x,y) 2*cos(5*norm([x,y]));
2021-05-22 08:41:25 +00:00
// heightfield(
// size=[100,100], bottom=-20, data=fn,
// xrange=[-180:2:180], yrange=[-180:2:180]
// );
2021-01-17 09:38:10 +00:00
module heightfield ( data , size = [ 100 , 100 ] , bottom = - 20 , maxz = 100 , xrange = [ - 1 : 0.04 : 1 ] , yrange = [ - 1 : 0.04 : 1 ] , style = "default" , convexity = 10 , anchor = CENTER , spin = 0 , orient = UP )
2019-07-14 22:10:13 +00:00
{
2020-05-30 02:04:34 +00:00
size = is_num ( size ) ? [ size , size ] : point2d ( size ) ;
2021-01-01 08:59:37 +00:00
vnf = heightfield ( data = data , size = size , xrange = xrange , yrange = yrange , bottom = bottom , maxz = maxz , style = style ) ;
attachable ( anchor , spin , orient , vnf = vnf ) {
vnf_polyhedron ( vnf , convexity = convexity ) ;
children ( ) ;
}
2019-07-14 22:10:13 +00:00
}
2021-01-17 09:38:10 +00:00
function heightfield ( data , size = [ 100 , 100 ] , bottom = - 20 , maxz = 100 , xrange = [ - 1 : 0.04 : 1 ] , yrange = [ - 1 : 0.04 : 1 ] , style = "default" , anchor = CENTER , spin = 0 , orient = UP ) =
2021-01-01 08:59:37 +00:00
assert ( is_list ( data ) || is_function ( data ) )
let (
size = is_num ( size ) ? [ size , size ] : point2d ( size ) ,
xvals = is_list ( data )
? [ for ( i = idx ( data [ 0 ] ) ) i ]
: assert ( is_list ( xrange ) || is_range ( xrange ) ) [ for ( x = xrange ) x ] ,
yvals = is_list ( data )
? [ for ( i = idx ( data ) ) i ]
: assert ( is_list ( yrange ) || is_range ( yrange ) ) [ for ( y = yrange ) y ] ,
xcnt = len ( xvals ) ,
minx = min ( xvals ) ,
maxx = max ( xvals ) ,
2021-01-01 09:21:17 +00:00
ycnt = len ( yvals ) ,
2021-01-01 08:59:37 +00:00
miny = min ( yvals ) ,
maxy = max ( yvals ) ,
verts = is_list ( data ) ? [
2021-01-01 09:21:17 +00:00
for ( y = [ 0 : 1 : ycnt - 1 ] ) [
for ( x = [ 0 : 1 : xcnt - 1 ] ) [
size . x * ( x / ( xcnt - 1 ) - 0.5 ) ,
size . y * ( y / ( ycnt - 1 ) - 0.5 ) ,
2021-01-01 08:59:37 +00:00
data [ y ] [ x ]
]
]
] : [
2021-01-01 09:21:17 +00:00
for ( y = yrange ) [
for ( x = xrange ) let (
z = data ( x , y )
) [
2021-01-01 08:59:37 +00:00
size . x * ( ( x - minx ) / ( maxx - minx ) - 0.5 ) ,
size . y * ( ( y - miny ) / ( maxy - miny ) - 0.5 ) ,
min ( maxz , max ( bottom + 0.1 , default ( z , 0 ) ) )
]
]
] ,
vnf = vnf_merge ( [
2021-01-01 09:21:17 +00:00
vnf_vertex_array ( verts , style = style , reverse = true ) ,
2021-01-01 08:59:37 +00:00
vnf_vertex_array ( [
2021-01-01 09:21:17 +00:00
verts [ 0 ] ,
2021-01-01 08:59:37 +00:00
[ for ( v = verts [ 0 ] ) [ v . x , v . y , bottom ] ] ,
] ) ,
vnf_vertex_array ( [
2021-01-01 09:21:17 +00:00
[ for ( v = verts [ ycnt - 1 ] ) [ v . x , v . y , bottom ] ] ,
2021-01-01 08:59:37 +00:00
verts [ ycnt - 1 ] ,
] ) ,
vnf_vertex_array ( [
[ for ( r = verts ) let ( v = r [ 0 ] ) [ v . x , v . y , bottom ] ] ,
2021-01-01 09:21:17 +00:00
[ for ( r = verts ) let ( v = r [ 0 ] ) v ] ,
2021-01-01 08:59:37 +00:00
] ) ,
vnf_vertex_array ( [
[ for ( r = verts ) let ( v = r [ xcnt - 1 ] ) v ] ,
2021-01-01 09:21:17 +00:00
[ for ( r = verts ) let ( v = r [ xcnt - 1 ] ) [ v . x , v . y , bottom ] ] ,
2021-01-01 08:59:37 +00:00
] ) ,
vnf_vertex_array ( [
[
for ( v = verts [ 0 ] ) [ v . x , v . y , bottom ] ,
for ( r = verts ) let ( v = r [ xcnt - 1 ] ) [ v . x , v . y , bottom ] ,
2021-01-01 09:21:17 +00:00
] , [
for ( r = verts ) let ( v = r [ 0 ] ) [ v . x , v . y , bottom ] ,
for ( v = verts [ ycnt - 1 ] ) [ v . x , v . y , bottom ] ,
2021-01-01 08:59:37 +00:00
]
] )
] )
) reorient ( anchor , spin , orient , vnf = vnf , p = vnf ) ;
2017-08-30 00:00:16 +00:00
2021-10-01 04:30:28 +00:00
// Module: ruler()
// Usage:
// ruler(length, width, [thickness=], [depth=], [labels=], [pipscale=], [maxscale=], [colors=], [alpha=], [unit=], [inch=]);
// Description:
// Creates a ruler for checking dimensions of the model
// Arguments:
// length = length of the ruler. Default 100
// width = width of the ruler. Default: size of the largest unit division
// ---
// thickness = thickness of the ruler. Default: 1
// depth = the depth of mark subdivisions. Default: 3
// labels = draw numeric labels for depths where labels are larger than 1. Default: false
// pipscale = width scale of the pips relative to the next size up. Default: 1/3
// maxscale = log10 of the maximum width divisions to display. Default: based on input length
// colors = colors to use for the ruler, a list of two values. Default: `["black","white"]`
// alpha = transparency value. Default: 1.0
// unit = unit to mark. Scales the ruler marks to a different length. Default: 1
// inch = set to true for a ruler scaled to inches (assuming base dimension is mm). Default: false
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `LEFT+BACK+TOP`
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP`
// Examples(2D,Big):
// ruler(100,depth=3);
// ruler(100,depth=3,labels=true);
// ruler(27);
// ruler(27,maxscale=0);
// ruler(100,pipscale=3/4,depth=2);
// ruler(100,width=2,depth=2);
// Example(2D,Big): Metric vs Imperial
// ruler(12,width=50,inch=true,labels=true,maxscale=0);
// fwd(50)ruler(300,width=50,labels=true);
module ruler ( length = 100 , width , thickness = 1 , depth = 3 , labels = false , pipscale = 1 / 3 , maxscale ,
colors = [ "black" , "white" ] , alpha = 1.0 , unit = 1 , inch = false , anchor = LEFT + BACK + TOP , spin = 0 , orient = UP )
{
inchfactor = 25.4 ;
assert ( depth < = 5 , "Cannot render scales smaller than depth=5" ) ;
assert ( len ( colors ) = = 2 , "colors must contain a list of exactly two colors." ) ;
length = inch ? inchfactor * length : length ;
unit = inch ? inchfactor * unit : unit ;
maxscale = is_def ( maxscale ) ? maxscale : floor ( log ( length / unit - EPSILON ) ) ;
scales = unit * [ for ( logsize = [ maxscale : - 1 : maxscale - depth + 1 ] ) pow ( 10 , logsize ) ] ;
widthfactor = ( 1 - pipscale ) / ( 1 - pow ( pipscale , depth ) ) ;
width = default ( width , scales [ 0 ] ) ;
widths = width * widthfactor * [ for ( logsize = [ 0 : - 1 : - depth + 1 ] ) pow ( pipscale , - logsize ) ] ;
offsets = concat ( [ 0 ] , cumsum ( widths ) ) ;
attachable ( anchor , spin , orient , size = [ length , width , thickness ] ) {
translate ( [ - length / 2 , - width / 2 , 0 ] )
for ( i = [ 0 : 1 : len ( scales ) - 1 ] ) {
count = ceil ( length / scales [ i ] ) ;
fontsize = 0.5 * min ( widths [ i ] , scales [ i ] / ceil ( log ( count * scales [ i ] / unit ) ) ) ;
back ( offsets [ i ] ) {
xcopies ( scales [ i ] , n = count , sp = [ 0 , 0 , 0 ] ) union ( ) {
actlen = ( $ idx < count - 1 ) || approx ( length % scales [ i ] , 0 ) ? scales [ i ] : length % scales [ i ] ;
color ( colors [ $ idx % 2 ] , alpha = alpha ) {
w = i > 0 ? quantup ( widths [ i ] , 1 / 1024 ) : widths [ i ] ; // What is the i>0 test supposed to do here?
cube ( [ quantup ( actlen , 1 / 1024 ) , quantup ( w , 1 / 1024 ) , thickness ] , anchor = FRONT + LEFT ) ;
}
mark =
i = = 0 && $ idx % 10 = = 0 && $ idx ! = 0 ? 0 :
i = = 0 && $ idx % 10 = = 9 && $ idx ! = count - 1 ? 1 :
$ idx % 10 = = 4 ? 1 :
$ idx % 10 = = 5 ? 0 : - 1 ;
flip = 1 - mark * 2 ;
if ( mark >= 0 ) {
marklength = min ( widths [ i ] / 2 , scales [ i ] * 2 ) ;
markwidth = marklength * 0.4 ;
translate ( [ mark * scales [ i ] , widths [ i ] , 0 ] ) {
color ( colors [ 1 - $ idx % 2 ] , alpha = alpha ) {
linear_extrude ( height = thickness + scales [ i ] / 100 , convexity = 2 , center = true ) {
polygon ( scale ( [ flip * markwidth , marklength ] , p = [ [ 0 , 0 ] , [ 1 , - 1 ] , [ 0 , - 0.9 ] ] ) ) ;
}
}
}
}
if ( labels && scales [ i ] / unit + EPSILON >= 1 ) {
color ( colors [ ( $ idx + 1 ) % 2 ] , alpha = alpha ) {
linear_extrude ( height = thickness + scales [ i ] / 100 , convexity = 2 , center = true ) {
back ( scales [ i ] * . 02 ) {
text ( text = str ( $ idx * scales [ i ] / unit ) , size = fontsize , halign = "left" , valign = "baseline" ) ;
}
}
}
}
}
}
}
children ( ) ;
}
}
2020-05-30 02:04:34 +00:00
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap