2017-08-30 00:00:16 +00:00
//////////////////////////////////////////////////////////////////////
2019-03-23 04:13:18 +00:00
// LibFile: transforms.scad
// This is the file that the most commonly used transformations, distributors, and mutator are in.
// To use, add the following lines to the beginning of your file:
// ```
// include <BOSL/constants.scad>
// use <BOSL/transforms.scad>
// ```
2017-08-30 00:00:16 +00:00
//////////////////////////////////////////////////////////////////////
/*
BSD 2 - Clause License
Copyright ( c ) 2017 , Revar Desmera
All rights reserved .
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions are met :
* Redistributions of source code must retain the above copyright notice , this
list of conditions and the following disclaimer .
* Redistributions in binary form must reproduce the above copyright notice ,
this list of conditions and the following disclaimer in the documentation
and / or other materials provided with the distribution .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* /
2019-03-23 04:13:18 +00:00
use < math.scad >
include < compat.scad >
2019-02-27 11:52:59 +00:00
include < constants.scad >
2017-08-30 00:00:16 +00:00
//////////////////////////////////////////////////////////////////////
2019-03-23 04:13:18 +00:00
// Section: Translations
2017-08-30 00:00:16 +00:00
//////////////////////////////////////////////////////////////////////
2019-03-23 04:13:18 +00:00
// Module: move()
//
// Description:
// Moves/translates children.
//
// Usage:
// move([x], [y], [z]) ...
// move([x,y,z]) ...
//
// Arguments:
2017-08-30 00:00:16 +00:00
// x = X axis translation.
// y = Y axis translation.
// z = Z axis translation.
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
// #sphere(d=10);
// move([0,20,30]) sphere(d=10);
//
// Example:
// #sphere(d=10);
// move(y=20) sphere(d=10);
//
// Example:
// #sphere(d=10);
// move(x=-10, y=-5) sphere(d=10);
module move ( a = [ 0 , 0 , 0 ] , x = 0 , y = 0 , z = 0 )
{
translate ( a + [ x , y , z ] ) children ( ) ;
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: xmove()
//
// Description:
// Moves/translates children the given amount along the X axis.
//
// Usage:
// xmove(x) ...
//
// Arguments:
// x = Amount to move right along the X axis. Negative values move left.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// #sphere(d=10);
// xmove(20) sphere(d=10);
module xmove ( x = 0 ) translate ( [ x , 0 , 0 ] ) children ( ) ;
// Module: ymove()
//
// Description:
// Moves/translates children the given amount along the Y axis.
//
// Usage:
// ymove(y) ...
//
// Arguments:
// y = Amount to move back along the Y axis. Negative values move forward.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// #sphere(d=10);
// ymove(20) sphere(d=10);
module ymove ( y = 0 ) translate ( [ 0 , y , 0 ] ) children ( ) ;
// Module: zmove()
//
// Description:
// Moves/translates children the given amount along the Z axis.
//
// Usage:
// zmove(z) ...
//
// Arguments:
// z = Amount to move up along the Z axis. Negative values move down.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// #sphere(d=10);
// zmove(20) sphere(d=10);
module zmove ( z = 0 ) translate ( [ 0 , 0 , z ] ) children ( ) ;
// Module: left()
//
// Description:
// Moves children left (in the X- direction) by the given amount.
//
// Usage:
// left(x) ...
//
// Arguments:
// x = Scalar amount to move left.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// #sphere(d=10);
// left(20) sphere(d=10);
module left ( x = 0 ) translate ( [ - x , 0 , 0 ] ) children ( ) ;
// Module: right()
//
// Description:
// Moves children right (in the X+ direction) by the given amount.
//
// Usage:
// right(x) ...
//
// Arguments:
// x = Scalar amount to move right.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// #sphere(d=10);
// right(20) sphere(d=10);
module right ( x = 0 ) translate ( [ x , 0 , 0 ] ) children ( ) ;
// Module: fwd() / forward()
//
// Description:
// Moves children forward (in the Y- direction) by the given amount.
//
// Usage:
// fwd(y) ...
// forward(y) ...
//
// Arguments:
// y = Scalar amount to move forward.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// #sphere(d=10);
// fwd(20) sphere(d=10);
module forward ( y = 0 ) translate ( [ 0 , - y , 0 ] ) children ( ) ;
module fwd ( y = 0 ) translate ( [ 0 , - y , 0 ] ) children ( ) ;
// Module: back()
//
// Description:
// Moves children back (in the Y+ direction) by the given amount.
//
// Usage:
// back(y) ...
//
// Arguments:
// y = Scalar amount to move back.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// #sphere(d=10);
// back(20) sphere(d=10);
module back ( y = 0 ) translate ( [ 0 , y , 0 ] ) children ( ) ;
// Module: down()
//
// Description:
// Moves children down (in the Z- direction) by the given amount.
//
// Usage:
// down(z) ...
//
// Arguments:
// z = Scalar amount to move down.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// #sphere(d=10);
// down(20) sphere(d=10);
module down ( z = 0 ) translate ( [ 0 , 0 , - z ] ) children ( ) ;
// Module: up()
//
// Description:
// Moves children up (in the Z+ direction) by the given amount.
//
// Usage:
// up(z) ...
//
// Arguments:
// z = Scalar amount to move up.
//
// Example:
// #sphere(d=10);
// up(20) sphere(d=10);
module up ( z = 0 ) translate ( [ 0 , 0 , z ] ) children ( ) ;
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
//////////////////////////////////////////////////////////////////////
// Section: Rotations
//////////////////////////////////////////////////////////////////////
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: rot()
//
// Description:
// Rotates children around an arbitrary axis by the given number of degrees.
// Can be used as a drop-in replacement for `rotate()`, with extra features.
//
// Usage:
// rot(a, [cp], [reverse]) ...
// rot([X,Y,Z], [cp], [reverse]) ...
// rot(a, v, [cp], [reverse]) ...
// rot(from, to, [a], [reverse]) ...
//
// Arguments:
// a = Scalar angle or vector of XYZ rotation angles to rotate by, in degrees.
// v = vector for the axis of rotation. Default: [0,0,1] or V_UP
2019-01-25 21:47:14 +00:00
// cp = centerpoint to rotate around. Default: [0,0,0]
2019-03-23 04:13:18 +00:00
// from = Starting vector for vector-based rotations.
// to = Target vector for vector-based rotations.
// reverse = If true, exactly reverses the rotation, including axis rotation ordering. Default: false
//
2019-01-25 21:47:14 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// #cube([2,4,9]);
2019-01-25 21:47:14 +00:00
// rot([30,60,0], cp=[0,0,9]) cube([2,4,9]);
2019-03-23 04:13:18 +00:00
//
// Example:
// #cube([2,4,9]);
2019-01-25 21:47:14 +00:00
// rot(30, v=[1,1,0], cp=[0,0,9]) cube([2,4,9]);
2019-03-23 04:13:18 +00:00
//
2019-02-27 11:52:59 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// #cube([2,4,9]);
// rot(from=V_UP, to=V_LEFT+V_BACK) cube([2,4,9]);
module rot ( a = 0 , v = undef , cp = undef , from = undef , to = undef , reverse = false )
{
if ( is_def ( cp ) ) {
translate ( cp ) rot ( a = a , v = v , from = from , to = to , reverse = reverse ) translate ( - cp ) children ( ) ;
} else if ( is_def ( from ) ) {
assertion ( is_def ( to ) , "`from` and `to` should be used together." ) ;
2019-04-01 01:43:54 +00:00
axis = vector_axis ( from , to ) ;
ang = vector_angle ( from , to ) ;
if ( ang < 0.0001 && a = = 0 ) {
2019-03-23 04:13:18 +00:00
children ( ) ; // May be slightly faster?
2019-04-01 01:43:54 +00:00
} else if ( reverse ) {
rotate ( a = - ang , v = axis ) rotate ( a = - a , v = from ) children ( ) ;
2019-03-23 04:13:18 +00:00
} else {
2019-04-01 01:43:54 +00:00
rotate ( a = ang , v = axis ) rotate ( a = a , v = from ) children ( ) ;
2019-03-23 04:13:18 +00:00
}
} else if ( a = = 0 ) {
2019-02-27 11:52:59 +00:00
children ( ) ; // May be slightly faster?
2019-03-23 04:13:18 +00:00
} else if ( reverse ) {
if ( is_def ( v ) ) {
rotate ( a = - a , v = v ) children ( ) ;
} else if ( is_scalar ( a ) ) {
rotate ( - a ) children ( ) ;
} else {
rotate ( [ - a [ 0 ] , 0 , 0 ] ) rotate ( [ 0 , - a [ 1 ] , 0 ] ) rotate ( [ 0 , 0 , - a [ 2 ] ] ) children ( ) ;
2019-02-27 11:52:59 +00:00
}
2019-03-23 04:13:18 +00:00
} else {
rotate ( a = a , v = v ) children ( ) ;
2019-02-27 11:52:59 +00:00
}
}
2019-03-23 04:13:18 +00:00
// Module: xrot()
//
// Description:
// Rotates children around the X axis by the given number of degrees.
//
// Usage:
// xrot(a, [cp]) ...
//
// Arguments:
2018-12-10 20:11:34 +00:00
// a = angle to rotate by in degrees.
// cp = centerpoint to rotate around. Default: [0,0,0]
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
// #cylinder(h=50, r=10, center=true);
// xrot(90) cylinder(h=50, r=10, center=true);
module xrot ( a = 0 , cp = undef )
{
2019-02-27 11:52:59 +00:00
if ( a = = 0 ) {
children ( ) ; // May be slightly faster?
2019-03-23 04:13:18 +00:00
} else if ( is_def ( cp ) ) {
2018-12-10 20:11:34 +00:00
translate ( cp ) rotate ( [ a , 0 , 0 ] ) translate ( - cp ) children ( ) ;
2019-03-23 04:13:18 +00:00
} else {
rotate ( [ a , 0 , 0 ] ) children ( ) ;
2018-12-10 20:11:34 +00:00
}
}
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: yrot()
//
// Description:
// Rotates children around the Y axis by the given number of degrees.
//
// Usage:
// yrot(a, [cp]) ...
//
// Arguments:
2018-12-10 20:11:34 +00:00
// a = angle to rotate by in degrees.
// cp = centerpoint to rotate around. Default: [0,0,0]
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
// #cylinder(h=50, r=10, center=true);
// yrot(90) cylinder(h=50, r=10, center=true);
module yrot ( a = 0 , cp = undef )
{
2019-02-27 11:52:59 +00:00
if ( a = = 0 ) {
children ( ) ; // May be slightly faster?
2019-03-23 04:13:18 +00:00
} else if ( is_def ( cp ) ) {
2018-12-10 20:11:34 +00:00
translate ( cp ) rotate ( [ 0 , a , 0 ] ) translate ( - cp ) children ( ) ;
2019-03-23 04:13:18 +00:00
} else {
rotate ( [ 0 , a , 0 ] ) children ( ) ;
2018-12-10 20:11:34 +00:00
}
}
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: zrot()
//
// Description:
// Rotates children around the Z axis by the given number of degrees.
//
// Usage:
// zrot(a, [cp]) ...
//
// Arguments:
2018-12-10 20:11:34 +00:00
// a = angle to rotate by in degrees.
// cp = centerpoint to rotate around. Default: [0,0,0]
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
// #cube(size=[60,20,40], center=true);
// zrot(90) cube(size=[60,20,40], center=true);
module zrot ( a = 0 , cp = undef )
{
2019-02-27 11:52:59 +00:00
if ( a = = 0 ) {
children ( ) ; // May be slightly faster?
2019-03-23 04:13:18 +00:00
} else if ( is_def ( cp ) ) {
2019-02-27 11:52:59 +00:00
translate ( cp ) rotate ( a ) translate ( - cp ) children ( ) ;
} else {
2019-03-23 04:13:18 +00:00
rotate ( a ) children ( ) ;
2018-12-10 20:11:34 +00:00
}
}
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
//////////////////////////////////////////////////////////////////////
// Section: Scaling and Mirroring
//////////////////////////////////////////////////////////////////////
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: xscale()
//
// Description:
// Scales children by the given factor on the X axis.
//
// Usage:
// xscale(x) ...
//
// Arguments:
// x = Factor to scale by along the X axis.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// xscale(3) sphere(r=10);
module xscale ( x ) scale ( [ x , 1 , 1 ] ) children ( ) ;
// Module: yscale()
//
// Description:
// Scales children by the given factor on the Y axis.
//
// Usage:
// yscale(y) ...
//
// Arguments:
// y = Factor to scale by along the Y axis.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// yscale(3) sphere(r=10);
module yscale ( y ) scale ( [ 1 , y , 1 ] ) children ( ) ;
// Module: zscale()
//
// Description:
// Scales children by the given factor on the Z axis.
//
// Usage:
// zscale(z) ...
//
// Arguments:
// z = Factor to scale by along the Z axis.
//
2018-11-27 03:55:18 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// zscale(3) sphere(r=10);
module zscale ( z ) scale ( [ 1 , 1 , z ] ) children ( ) ;
// Module: xflip()
//
// Description:
// Mirrors the children along the X axis, like `mirror([1,0,0])` or `xscale(-1)`
//
// Usage:
// xflip([cp]) ...
//
// Arguments:
// cp = A point that lies on the plane of reflection.
//
// Example:
// xflip() yrot(90) cylinder(d1=10, d2=0, h=20);
// color("blue", 0.25) cube([0.01,15,15], center=true);
// color("red", 0.333) yrot(90) cylinder(d1=10, d2=0, h=20);
//
// Example:
// xflip(cp=[-5,0,0]) yrot(90) cylinder(d1=10, d2=0, h=20);
// color("blue", 0.25) left(5) cube([0.01,15,15], center=true);
// color("red", 0.333) yrot(90) cylinder(d1=10, d2=0, h=20);
module xflip ( cp = [ 0 , 0 , 0 ] ) translate ( cp ) mirror ( [ 1 , 0 , 0 ] ) translate ( - cp ) children ( ) ;
// Module: yflip()
//
// Description:
// Mirrors the children along the Y axis, like `mirror([0,1,0])` or `yscale(-1)`
//
// Usage:
// yflip([cp]) ...
//
// Arguments:
// cp = A point that lies on the plane of reflection.
//
// Example:
// yflip() xrot(90) cylinder(d1=10, d2=0, h=20);
// color("blue", 0.25) cube([15,0.01,15], center=true);
// color("red", 0.333) xrot(90) cylinder(d1=10, d2=0, h=20);
//
2018-11-27 03:55:18 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// yflip(cp=[0,5,0]) xrot(90) cylinder(d1=10, d2=0, h=20);
// color("blue", 0.25) back(5) cube([15,0.01,15], center=true);
// color("red", 0.333) xrot(90) cylinder(d1=10, d2=0, h=20);
module yflip ( cp = [ 0 , 0 , 0 ] ) translate ( cp ) mirror ( [ 0 , 1 , 0 ] ) translate ( - cp ) children ( ) ;
// Module: zflip()
//
// Description:
// Mirrors the children along the Z axis, like `mirror([0,0,1])` or `zscale(-1)`
//
// Usage:
// zflip([cp]) ...
//
// Arguments:
// cp = A point that lies on the plane of reflection.
//
// Example:
// zflip() cylinder(d1=10, d2=0, h=20);
// color("blue", 0.25) cube([15,15,0.01], center=true);
// color("red", 0.333) cylinder(d1=10, d2=0, h=20);
//
// Example:
// zflip(cp=[0,0,-5]) cylinder(d1=10, d2=0, h=20);
// color("blue", 0.25) down(5) cube([15,15,0.01], center=true);
// color("red", 0.333) cylinder(d1=10, d2=0, h=20);
module zflip ( cp = [ 0 , 0 , 0 ] ) translate ( cp ) mirror ( [ 0 , 0 , 1 ] ) translate ( - cp ) children ( ) ;
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
//////////////////////////////////////////////////////////////////////
// Section: Skewing
//////////////////////////////////////////////////////////////////////
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: skew_xy() / skew_z()
//
// Description:
// Skews children on the X-Y plane, keeping constant in Z.
//
// Usage:
// skew_xy([xa], [ya]) ...
// skew_z([xa], [ya]) ...
//
// Arguments:
2017-08-30 19:59:30 +00:00
// xa = skew angle towards the X direction.
// ya = skew angle towards the Y direction.
2019-03-31 23:39:15 +00:00
// planar = If true, this becomes a 2D operation.
2019-03-23 04:13:18 +00:00
//
// Example(FlatSpin):
// #cube(size=10);
2018-11-27 03:55:18 +00:00
// skew_xy(xa=30, ya=15) cube(size=10);
2019-03-31 23:39:15 +00:00
// Example(2D):
// skew_xy(xa=15,ya=30,planar=true) square(30);
module skew_xy ( xa = 0 , ya = 0 , planar = false ) multmatrix ( m = planar ? matrix3_skew ( xa , ya ) : matrix4_skew_xy ( xa , ya ) ) children ( ) ;
module zskew ( xa = 0 , ya = 0 , planar = false ) multmatrix ( m = planar ? matrix3_skew ( xa , ya ) : matrix4_skew_xy ( xa , ya ) ) children ( ) ;
2019-03-23 04:13:18 +00:00
// Module: skew_yz() / skew_x()
//
// Description:
// Skews children on the Y-Z plane, keeping constant in X.
//
// Usage:
// skew_yz([ya], [za]) ...
// skew_x([ya], [za]) ...
//
// Arguments:
2017-08-30 19:59:30 +00:00
// ya = skew angle towards the Y direction.
// za = skew angle towards the Z direction.
2019-03-23 04:13:18 +00:00
//
// Example(FlatSpin):
// #cube(size=10);
2018-11-27 03:55:18 +00:00
// skew_yz(ya=30, za=15) cube(size=10);
2019-03-23 04:13:18 +00:00
module skew_yz ( ya = 0 , za = 0 ) multmatrix ( m = matrix4_skew_yz ( ya , za ) ) children ( ) ;
module xskew ( ya = 0 , za = 0 ) multmatrix ( m = matrix4_skew_yz ( ya , za ) ) children ( ) ;
// Module: skew_xz() / skew_y()
//
// Description:
// Skews children on the X-Z plane, keeping constant in Y.
//
// Usage:
// skew_xz([xa], [za]) ...
// skew_y([xa], [za]) ...
//
// Arguments:
2017-08-30 19:59:30 +00:00
// xa = skew angle towards the X direction.
// za = skew angle towards the Z direction.
2019-03-23 04:13:18 +00:00
//
// Example(FlatSpin):
// #cube(size=10);
// skew_xz(xa=15, za=-10) cube(size=10);
module skew_xz ( xa = 0 , za = 0 ) multmatrix ( m = matrix4_skew_xz ( xa , za ) ) children ( ) ;
module yskew ( xa = 0 , za = 0 ) multmatrix ( m = matrix4_skew_xz ( xa , za ) ) children ( ) ;
2017-08-30 00:00:16 +00:00
//////////////////////////////////////////////////////////////////////
2019-03-23 04:13:18 +00:00
// Section: Translational Distributors
2017-08-30 00:00:16 +00:00
//////////////////////////////////////////////////////////////////////
2019-03-23 04:13:18 +00:00
// Module: place_copies()
//
// Description:
// Makes copies of the given children at each of the given offsets.
//
// Usage:
// place_copies(a) ...
//
// Arguments:
// a = array of XYZ offset vectors. Default [[0,0,0]]
//
// Side Effects:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
//
2018-11-27 03:55:18 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// #sphere(r=10);
// place_copies([[-25,-25,0], [25,-25,0], [0,0,50], [0,25,0]]) sphere(r=10);
module place_copies ( a = [ [ 0 , 0 , 0 ] ] )
{
for ( off = a ) translate ( off ) children ( ) ;
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: spread()
//
// Description:
2019-03-25 10:27:21 +00:00
// Evenly distributes `n` copies of all children along a line.
// Copies every child at each position.
2019-03-23 04:13:18 +00:00
//
// Usage:
// spread(l, [n], [p1]) ...
// spread(l, spacing, [p1]) ...
// spread(spacing, [n], [p1]) ...
// spread(p1, p2, [n]) ...
// spread(p1, p2, spacing) ...
//
// Arguments:
// p1 = Starting point of line.
// p2 = Ending point of line.
// l = Length to spread copies over.
// spacing = A 3D vector indicating which direction and distance to place each subsequent copy at.
// n = Number of copies to distribute along the line. (Default: 2)
//
// Side Effects:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
// Example(FlatSpin):
// spread([0,0,0], [5,5,20], n=6) cube(size=[3,2,1],center=true);
// Examples:
// spread(l=40, n=6) cube(size=[3,2,1],center=true);
// spread(l=[15,30], n=6) cube(size=[3,2,1],center=true);
// spread(l=40, spacing=10) cube(size=[3,2,1],center=true);
// spread(spacing=[5,5,0], n=5) cube(size=[3,2,1],center=true);
2019-03-25 10:27:21 +00:00
// Example:
2019-03-25 11:52:59 +00:00
// spread(l=20, n=3) {
2019-03-25 10:27:21 +00:00
// cube(size=[1,3,1],center=true);
// cube(size=[3,1,1],center=true);
// }
2019-03-23 04:13:18 +00:00
module spread ( p1 = undef , p2 = undef , spacing = undef , l = undef , n = undef )
2017-08-30 00:00:16 +00:00
{
2019-03-23 04:13:18 +00:00
ll = (
is_def ( l ) ? scalar_vec3 ( l , 0 ) :
( is_def ( spacing ) && is_def ( n ) ) ? ( n * scalar_vec3 ( spacing , 0 ) ) :
( is_def ( p1 ) && is_def ( p2 ) ) ? point3d ( p2 - p1 ) :
undef
) ;
cnt = (
is_def ( n ) ? n :
( is_def ( spacing ) && is_def ( ll ) ) ? floor ( norm ( ll ) / norm ( scalar_vec3 ( spacing , 0 ) ) + 1.000001 ) :
2
) ;
spc = (
! is_def ( spacing ) ? ( ll / ( cnt - 1 ) ) :
is_scalar ( spacing ) && is_def ( ll ) ? ( ll / ( cnt - 1 ) ) :
scalar_vec3 ( spacing , 0 )
) ;
assertion ( is_def ( cnt ) , "Need two of `spacing`, 'l', 'n', or `p1`/`p2` arguments in `spread()`." ) ;
spos = is_def ( p1 ) ? point3d ( p1 ) : - ( cnt - 1 ) / 2 * spc ;
for ( i = [ 0 : cnt - 1 ] ) {
pos = i * spc + spos ;
$ pos = pos ;
$ idx = i ;
translate ( pos ) children ( ) ;
2017-08-30 00:00:16 +00:00
}
}
2018-10-07 05:26:38 +00:00
2019-03-23 04:13:18 +00:00
// Module: xspread()
//
// Description:
2019-04-02 02:03:49 +00:00
// Spreads out `n` copies of the children along a line on the X axis.
2019-03-23 04:13:18 +00:00
//
// Usage:
2019-04-02 02:03:49 +00:00
// xspread(spacing, [n], [sp]) ...
// xspread(l, [n], [sp]) ...
2019-03-23 04:13:18 +00:00
//
// Arguments:
// spacing = spacing between copies. (Default: 1.0)
// n = Number of copies to spread out. (Default: 2)
// l = Length to spread copies over.
2019-04-02 02:03:49 +00:00
// sp = If given, copies will be spread on a line to the right of starting position `sp`. If not given, copies will be spread along a line that is centered at [0,0,0].
2019-03-23 04:13:18 +00:00
//
// Side Effects:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
// Examples:
// xspread(20) sphere(3);
// xspread(20, n=3) sphere(3);
// xspread(spacing=15, l=50) sphere(3);
2019-04-02 02:03:49 +00:00
// xspread(n=4, l=30, sp=[0,10,0]) sphere(3);
2019-03-25 10:27:21 +00:00
// Example:
2019-03-25 11:52:59 +00:00
// xspread(10, n=3) {
2019-03-25 10:27:21 +00:00
// cube(size=[1,3,1],center=true);
// cube(size=[3,1,1],center=true);
// }
2019-04-02 02:03:49 +00:00
module xspread ( spacing = undef , n = undef , l = undef , sp = undef )
2019-03-23 04:13:18 +00:00
{
2019-04-02 02:03:49 +00:00
spread ( l = l * V_RIGHT , spacing = spacing * V_RIGHT , n = n , p1 = sp ) children ( ) ;
2019-03-23 04:13:18 +00:00
}
// Module: yspread()
//
// Description:
2019-04-02 02:03:49 +00:00
// Spreads out `n` copies of the children along a line on the Y axis.
2019-03-23 04:13:18 +00:00
//
// Usage:
2019-04-02 02:03:49 +00:00
// yspread(spacing, [n], [sp]) ...
// yspread(l, [n], [sp]) ...
2019-03-23 04:13:18 +00:00
//
// Arguments:
// spacing = spacing between copies. (Default: 1.0)
// n = Number of copies to spread out. (Default: 2)
// l = Length to spread copies over.
2019-04-02 02:03:49 +00:00
// sp = If given, copies will be spread on a line back from starting position `sp`. If not given, copies will be spread along a line that is centered at [0,0,0].
2019-03-23 04:13:18 +00:00
//
// Side Effects:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
// Examples:
// yspread(20) sphere(3);
// yspread(20, n=3) sphere(3);
// yspread(spacing=15, l=50) sphere(3);
2019-04-02 02:03:49 +00:00
// yspread(n=4, l=30, sp=[10,0,0]) sphere(3);
2019-03-25 10:27:21 +00:00
// Example:
2019-03-25 11:52:59 +00:00
// yspread(10, n=3) {
2019-03-25 10:27:21 +00:00
// cube(size=[1,3,1],center=true);
// cube(size=[3,1,1],center=true);
// }
2019-04-02 02:03:49 +00:00
module yspread ( spacing = undef , n = undef , l = undef , sp = undef )
2019-03-23 04:13:18 +00:00
{
2019-04-02 02:03:49 +00:00
spread ( l = l * V_BACK , spacing = spacing * V_BACK , n = n , p1 = sp ) children ( ) ;
2019-03-23 04:13:18 +00:00
}
// Module: zspread()
//
// Description:
2019-04-02 02:03:49 +00:00
// Spreads out `n` copies of the children along a line on the Z axis.
2019-03-23 04:13:18 +00:00
//
// Usage:
2019-04-02 02:03:49 +00:00
// zspread(spacing, [n], [sp]) ...
// zspread(l, [n], [sp]) ...
2019-03-23 04:13:18 +00:00
//
// Arguments:
// spacing = spacing between copies. (Default: 1.0)
// n = Number of copies to spread out. (Default: 2)
// l = Length to spread copies over.
2019-04-02 02:03:49 +00:00
// sp = If given, copies will be spread on a line up from starting position `sp`. If not given, copies will be spread along a line that is centered at [0,0,0].
2019-03-23 04:13:18 +00:00
//
// Side Effects:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
// Examples:
// zspread(20) sphere(3);
// zspread(20, n=3) sphere(3);
// zspread(spacing=15, l=50) sphere(3);
2019-04-02 02:03:49 +00:00
// zspread(n=4, l=30, sp=[10,0,0]) sphere(3);
2019-03-25 10:27:21 +00:00
// Example:
2019-03-25 11:52:59 +00:00
// zspread(10, n=3) {
2019-03-25 10:27:21 +00:00
// cube(size=[1,3,1],center=true);
// cube(size=[3,1,1],center=true);
// }
2019-04-02 02:03:49 +00:00
module zspread ( spacing = undef , n = undef , l = undef , sp = undef )
2019-03-23 04:13:18 +00:00
{
2019-04-02 02:03:49 +00:00
spread ( l = l * V_UP , spacing = spacing * V_UP , n = n , p1 = sp ) children ( ) ;
2019-03-23 04:13:18 +00:00
}
// Module: distribute()
//
// Description:
// Spreads out each individual child along the direction `dir`.
2019-03-25 10:27:21 +00:00
// Every child is placed at a different position, in order.
2019-03-23 04:13:18 +00:00
// This is useful for laying out groups of disparate objects
// where you only really care about the spacing between them.
//
// Usage:
// distribute(spacing, dir, [sizes]) ...
// distribute(l, dir, [sizes]) ...
//
// Arguments:
// spacing = Spacing to add between each child. (Default: 10.0)
// sizes = Array containing how much space each child will need.
// dir = Vector direction to distribute copies along.
// l = Length to distribute copies along.
//
// Side Effect:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
2018-10-07 05:26:38 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// distribute(sizes=[100, 30, 50], dir=V_UP) {
// sphere(r=50);
// cube([10,20,30], center=true);
// cylinder(d=30, h=50, center=true);
// }
module distribute ( spacing = undef , sizes = undef , dir = V_RIGHT , l = undef )
{
gaps = ( $children < 2 ) ? [ 0 ] :
is_def ( sizes ) ? [ for ( i = [ 0 : $children - 2 ] ) sizes [ i ] / 2 + sizes [ i + 1 ] / 2 ] :
[ for ( i = [ 0 : $children - 2 ] ) 0 ] ;
spc = is_def ( l ) ? ( ( l - sum ( gaps ) ) / ( $children - 1 ) ) : default ( spacing , 10 ) ;
gaps2 = [ for ( gap = gaps ) gap + spc ] ;
spos = dir * - sum ( gaps2 ) / 2 ;
for ( i = [ 0 : $children - 1 ] ) {
totspc = sum ( concat ( [ 0 ] , slice ( gaps2 , 0 , i ) ) ) ;
$ pos = spos + totspc * dir ;
$ idx = i ;
translate ( $ pos ) children ( i ) ;
}
}
2018-10-07 05:26:38 +00:00
2019-03-23 04:13:18 +00:00
// Module: xdistribute()
//
// Description:
// Spreads out each individual child along the X axis.
2019-03-25 10:27:21 +00:00
// Every child is placed at a different position, in order.
2019-03-23 04:13:18 +00:00
// This is useful for laying out groups of disparate objects
// where you only really care about the spacing between them.
//
// Usage:
// xdistribute(spacing, [sizes]) ...
// xdistribute(l, [sizes]) ...
//
// Arguments:
// spacing = spacing between each child. (Default: 10.0)
// sizes = Array containing how much space each child will need.
// l = Length to distribute copies along.
//
// Side Effect:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
2018-10-07 05:26:38 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// xdistribute(sizes=[100, 10, 30], spacing=40) {
// sphere(r=50);
// cube([10,20,30], center=true);
// cylinder(d=30, h=50, center=true);
// }
module xdistribute ( spacing = 10 , sizes = undef , l = undef )
{
dir = V_RIGHT ;
gaps = ( $children < 2 ) ? [ 0 ] :
is_def ( sizes ) ? [ for ( i = [ 0 : $children - 2 ] ) sizes [ i ] / 2 + sizes [ i + 1 ] / 2 ] :
[ for ( i = [ 0 : $children - 2 ] ) 0 ] ;
spc = is_def ( l ) ? ( ( l - sum ( gaps ) ) / ( $children - 1 ) ) : default ( spacing , 10 ) ;
gaps2 = [ for ( gap = gaps ) gap + spc ] ;
spos = dir * - sum ( gaps2 ) / 2 ;
for ( i = [ 0 : $children - 1 ] ) {
totspc = sum ( concat ( [ 0 ] , slice ( gaps2 , 0 , i ) ) ) ;
$ pos = spos + totspc * dir ;
$ idx = i ;
translate ( $ pos ) children ( i ) ;
}
}
2018-10-07 05:26:38 +00:00
2019-03-23 04:13:18 +00:00
// Module: ydistribute()
//
// Description:
// Spreads out each individual child along the Y axis.
2019-03-25 10:27:21 +00:00
// Every child is placed at a different position, in order.
2019-03-23 04:13:18 +00:00
// This is useful for laying out groups of disparate objects
// where you only really care about the spacing between them.
//
// Usage:
// ydistribute(spacing, [sizes])
// ydistribute(l, [sizes])
//
// Arguments:
// spacing = spacing between each child. (Default: 10.0)
// sizes = Array containing how much space each child will need.
// l = Length to distribute copies along.
//
// Side Effect:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
2018-10-07 05:26:38 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// ydistribute(sizes=[30, 20, 100], spacing=40) {
// cylinder(d=30, h=50, center=true);
// cube([10,20,30], center=true);
// sphere(r=50);
// }
module ydistribute ( spacing = 10 , sizes = undef , l = undef )
{
dir = V_BACK ;
gaps = ( $children < 2 ) ? [ 0 ] :
is_def ( sizes ) ? [ for ( i = [ 0 : $children - 2 ] ) sizes [ i ] / 2 + sizes [ i + 1 ] / 2 ] :
[ for ( i = [ 0 : $children - 2 ] ) 0 ] ;
spc = is_def ( l ) ? ( ( l - sum ( gaps ) ) / ( $children - 1 ) ) : default ( spacing , 10 ) ;
gaps2 = [ for ( gap = gaps ) gap + spc ] ;
spos = dir * - sum ( gaps2 ) / 2 ;
for ( i = [ 0 : $children - 1 ] ) {
totspc = sum ( concat ( [ 0 ] , slice ( gaps2 , 0 , i ) ) ) ;
$ pos = spos + totspc * dir ;
$ idx = i ;
translate ( $ pos ) children ( i ) ;
}
}
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: zdistribute()
//
// Description:
// Spreads out each individual child along the Z axis.
2019-03-25 10:27:21 +00:00
// Every child is placed at a different position, in order.
2019-03-23 04:13:18 +00:00
// This is useful for laying out groups of disparate objects
// where you only really care about the spacing between them.
//
// Usage:
// zdistribute(spacing, [sizes])
// zdistribute(l, [sizes])
//
// Arguments:
// spacing = spacing between each child. (Default: 10.0)
// sizes = Array containing how much space each child will need.
// l = Length to distribute copies along.
//
// Side Effect:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index number of each child being copied.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// zdistribute(sizes=[30, 20, 100], spacing=40) {
// cylinder(d=30, h=50, center=true);
// cube([10,20,30], center=true);
// sphere(r=50);
// }
module zdistribute ( spacing = 10 , sizes = undef , l = undef )
2017-08-30 00:00:16 +00:00
{
2019-03-23 04:13:18 +00:00
dir = V_UP ;
gaps = ( $children < 2 ) ? [ 0 ] :
is_def ( sizes ) ? [ for ( i = [ 0 : $children - 2 ] ) sizes [ i ] / 2 + sizes [ i + 1 ] / 2 ] :
[ for ( i = [ 0 : $children - 2 ] ) 0 ] ;
spc = is_def ( l ) ? ( ( l - sum ( gaps ) ) / ( $children - 1 ) ) : default ( spacing , 10 ) ;
gaps2 = [ for ( gap = gaps ) gap + spc ] ;
spos = dir * - sum ( gaps2 ) / 2 ;
for ( i = [ 0 : $children - 1 ] ) {
totspc = sum ( concat ( [ 0 ] , slice ( gaps2 , 0 , i ) ) ) ;
$ pos = spos + totspc * dir ;
$ idx = i ;
translate ( $ pos ) children ( i ) ;
}
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: grid2d()
//
// Description:
// Makes a square or hexagonal grid of copies of children.
//
// Usage:
// grid2d(size, spacing, [stagger], [scale], [in_poly], [orient], [align]) ...
// grid2d(size, cols, rows, [stagger], [scale], [in_poly], [orient], [align]) ...
// grid2d(spacing, cols, rows, [stagger], [scale], [in_poly], [orient], [align]) ...
// grid2d(spacing, in_poly, [stagger], [scale], [orient], [align]) ...
// grid2d(cols, rows, in_poly, [stagger], [scale], [orient], [align]) ...
//
// Arguments:
// size = The [X,Y] size to spread the copies over.
// spacing = Distance between copies in [X,Y] or scalar distance.
// cols = How many columns of copies to make. If staggered, count both staggered and unstaggered columns.
// rows = How many rows of copies to make. If staggered, count both staggered and unstaggered rows.
// stagger = If true, make a staggered (hexagonal) grid. If false, make square grid. If "alt", makes alternate staggered pattern. Default: false
// scale = [X,Y] scaling factors to reshape grid.
2019-03-25 10:27:21 +00:00
// in_poly = If given a list of polygon points, only creates copies whose center would be inside the polygon. Polygon can be concave and/or self crossing.
2019-03-23 04:13:18 +00:00
// orient = Orientation axis for the grid. Orientation is NOT applied to individual children.
// align = Alignment of the grid. Alignment is NOT applies to individual children.
//
// Side Effects:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$col` is set to the integer column number for each child.
// `$row` is set to the integer row number for each child.
//
// Examples:
// grid2d(size=50, spacing=10, stagger=false) cylinder(d=10, h=1);
// grid2d(spacing=10, rows=7, cols=13, stagger=true) cylinder(d=6, h=5);
// grid2d(spacing=10, rows=7, cols=13, stagger="alt") cylinder(d=6, h=5);
// grid2d(size=50, rows=11, cols=11, stagger=true) cylinder(d=5, h=1);
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// poly = [[-25,-25], [25,25], [-25,25], [25,-25]];
// grid2d(spacing=5, stagger=true, in_poly=poly)
// zrot(180/6) cylinder(d=5, h=1, $fn=6);
// %polygon(poly);
//
// Example:
// // Makes a grid of hexagon pillars whose tops are all
// // angled to reflect light at [0,0,50], if they were shiny.
// hexregion = [for (a = [0:60:359.9]) 50.01*[cos(a), sin(a)]];
// grid2d(spacing=10, stagger=true, in_poly=hexregion) {
// // Note: You must use for(var=[val]) or let(var=val)
// // to set vars from $pos or other special vars in this scope.
// let (ref_v = (normalize([0,0,50]-point3d($pos)) + V_UP)/2)
// half_of(v=-ref_v, cp=[0,0,5])
// zrot(180/6)
// cylinder(h=20, d=10/cos(180/6)+0.01, $fn=6);
// }
module grid2d ( size = undef , spacing = undef , cols = undef , rows = undef , stagger = false , scale = [ 1 , 1 , 1 ] , in_poly = undef , orient = ORIENT_Z , align = V_CENTER )
2017-08-30 00:00:16 +00:00
{
2019-03-23 04:13:18 +00:00
assert_in_list ( "stagger" , stagger , [ false , true , "alt" ] ) ;
scl = vmul ( scalar_vec3 ( scale , 1 ) , ( stagger ! = false ? [ 0.5 , sin ( 60 ) , 0 ] : [ 1 , 1 , 0 ] ) ) ;
if ( is_def ( size ) ) {
siz = scalar_vec3 ( size ) ;
if ( is_def ( spacing ) ) {
spc = vmul ( scalar_vec3 ( spacing ) , scl ) ;
maxcols = ceil ( siz [ 0 ] / spc [ 0 ] ) ;
maxrows = ceil ( siz [ 1 ] / spc [ 1 ] ) ;
grid2d ( spacing = spacing , cols = maxcols , rows = maxrows , stagger = stagger , scale = scale , in_poly = in_poly , orient = orient , align = align ) children ( ) ;
} else {
spc = [ siz [ 0 ] / cols , siz [ 1 ] / rows , 0 ] ;
grid2d ( spacing = spc , cols = cols , rows = rows , stagger = stagger , scale = scale , in_poly = in_poly , orient = orient , align = align ) children ( ) ;
2017-08-30 00:00:16 +00:00
}
} else {
2019-03-23 04:13:18 +00:00
spc = is_array ( spacing ) ? spacing : vmul ( scalar_vec3 ( spacing ) , scl ) ;
bounds = is_def ( in_poly ) ? pointlist_bounds ( in_poly ) : undef ;
bnds = is_def ( bounds ) ? [ for ( a = [ 0 : 1 ] ) 2 * max ( vabs ( [ for ( i = [ 0 , 1 ] ) bounds [ i ] [ a ] ] ) ) + 1 ] : undef ;
mcols = is_def ( cols ) ? cols : ( is_def ( spc ) && is_def ( bnds ) ) ? quantup ( ceil ( bnds [ 0 ] / spc [ 0 ] ) - 1 , 4 ) + 1 : undef ;
mrows = is_def ( rows ) ? rows : ( is_def ( spc ) && is_def ( bnds ) ) ? quantup ( ceil ( bnds [ 1 ] / spc [ 1 ] ) - 1 , 4 ) + 1 : undef ;
siz = vmul ( spc , [ mcols - 1 , mrows - 1 , 0 ] ) ;
staggermod = ( stagger = = "alt" ) ? 1 : 0 ;
if ( stagger = = false ) {
orient_and_align ( siz , orient , align ) {
for ( row = [ 0 : mrows - 1 ] ) {
for ( col = [ 0 : mcols - 1 ] ) {
pos = [ col * spc [ 0 ] , row * spc [ 1 ] ] - point2d ( siz / 2 ) ;
if ( ! is_def ( in_poly ) || point_in_polygon ( pos , in_poly ) >= 0 ) {
$ col = col ;
$ row = row ;
$ pos = pos ;
translate ( pos ) rot ( orient , reverse = true ) children ( ) ;
}
}
}
}
} else {
// stagger == true or stagger == "alt"
orient_and_align ( siz , orient , align ) {
cols1 = ceil ( mcols / 2 ) ;
cols2 = mcols - cols1 ;
for ( row = [ 0 : mrows - 1 ] ) {
rowcols = ( ( row % 2 ) = = staggermod ) ? cols1 : cols2 ;
if ( rowcols > 0 ) {
for ( col = [ 0 : rowcols - 1 ] ) {
rowdx = ( row % 2 ! = staggermod ) ? spc [ 0 ] : 0 ;
pos = [ 2 * col * spc [ 0 ] + rowdx , row * spc [ 1 ] ] - point2d ( siz / 2 ) ;
if ( ! is_def ( in_poly ) || point_in_polygon ( pos , in_poly ) >= 0 ) {
$ col = col * 2 + ( ( row % 2 ! = staggermod ) ? 1 : 0 ) ;
$ row = row ;
$ pos = pos ;
translate ( pos ) rot ( orient , reverse = true ) children ( ) ;
}
}
}
}
2017-08-30 00:00:16 +00:00
}
}
}
}
2019-03-23 04:13:18 +00:00
// Module: grid3d()
//
// Description:
// Makes a 3D grid of duplicate children.
//
// Usage:
// grid3d(n, spacing) ...
// grid3d(n=[Xn,Yn,Zn], spacing=[dX,dY,dZ]) ...
// grid3d([xa], [ya], [za]) ...
//
// Arguments:
// xa = array or range of X-axis values to offset by. (Default: [0])
// ya = array or range of Y-axis values to offset by. (Default: [0])
// za = array or range of Z-axis values to offset by. (Default: [0])
// n = Optional number of copies to have per axis.
// spacing = spacing of copies per axis. Use with `n`.
//
// Side Effect:
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the [Xidx,Yidx,Zidx] index values of each child copy, when using `count` and `n`.
//
// Examples(FlatSpin):
// grid3d(xa=[0:25:50],ya=[0,40],za=[-20:40:20]) sphere(r=5);
// grid3d(n=[3, 4, 2], spacing=[60, 50, 40]) sphere(r=10);
// Examples:
// grid3d(ya=[-60:40:60],za=[0,70]) sphere(r=10);
// grid3d(n=3, spacing=30) sphere(r=10);
// grid3d(n=[3, 1, 2], spacing=30) sphere(r=10);
// grid3d(n=[3, 4], spacing=[80, 60]) sphere(r=10);
// Examples:
// grid3d(n=[10, 10, 10], spacing=50) color($idx/9) cube(50, center=true);
module grid3d ( xa = [ 0 ] , ya = [ 0 ] , za = [ 0 ] , n = undef , spacing = undef )
2017-08-30 00:00:16 +00:00
{
2019-03-23 04:13:18 +00:00
n = scalar_vec3 ( n , 1 ) ;
spacing = scalar_vec3 ( spacing , undef ) ;
if ( is_def ( n ) && is_def ( spacing ) ) {
for ( xi = [ 0 : n . x - 1 ] ) {
for ( yi = [ 0 : n . y - 1 ] ) {
for ( zi = [ 0 : n . z - 1 ] ) {
$ idx = [ xi , yi , zi ] ;
$ pos = vmul ( spacing , $ idx - ( n - [ 1 , 1 , 1 ] ) / 2 ) ;
translate ( $ pos ) children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
}
} else {
2019-03-23 04:13:18 +00:00
for ( xoff = xa , yoff = ya , zoff = za ) {
$ pos = [ xoff , yoff , zoff ] ;
translate ( $ pos ) children ( ) ;
2017-08-30 00:00:16 +00:00
}
}
}
2019-03-23 04:13:18 +00:00
//////////////////////////////////////////////////////////////////////
// Section: Rotational Distributors
//////////////////////////////////////////////////////////////////////
// Module: rot_copies()
//
// Description:
// Given a number of XYZ rotation angles, or a list of angles and an axis `v`,
// rotates copies of the children to each of those angles.
//
// Usage:
// rot_copies(rots, [cp], [sa], [delta], [subrot]) ...
// rot_copies(rots, v, [cp], [sa], [delta], [subrot]) ...
// rot_copies(n, [v], [cp], [sa], [delta], [subrot]) ...
//
// Arguments:
// rots = A list of [X,Y,Z] rotation angles in degrees. If `v` is given, this will be a list of scalar angles in degrees to rotate around `v`.
// v = If given, this is the vector to rotate around.
// cp = Centerpoint to rotate around.
// n = Optional number of evenly distributed copies, rotated around the ring.
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise.
// delta = [X,Y,Z] amount to move away from cp before rotating. Makes rings of copies.
// subrot = If false, don't sub-rotate children as they are copied around the ring.
//
// Side Effects:
// `$ang` is set to the rotation angle (or XYZ rotation triplet) of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index value of each child copy.
//
// Example:
// #cylinder(h=20, r1=5, r2=0);
// rot_copies([[45,0,0],[0,45,90],[90,-45,270]]) cylinder(h=20, r1=5, r2=0);
//
// Example:
// rot_copies([45, 90, 135], v=V_DOWN+V_BACK)
// yrot(90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
//
// Example:
// rot_copies(n=6, v=V_DOWN+V_BACK)
// yrot(90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
//
// Example:
// rot_copies(n=6, v=V_DOWN+V_BACK, delta=[10,0,0])
// yrot(90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
//
// Example:
// rot_copies(n=6, v=V_UP+V_FWD, delta=[10,0,0], sa=45)
// yrot(90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// rot_copies(n=6, v=V_DOWN+V_BACK, delta=[20,0,0], subrot=false)
// yrot(90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
module rot_copies ( rots = [ ] , v = undef , cp = [ 0 , 0 , 0 ] , count = undef , n = undef , sa = 0 , offset = 0 , delta = [ 0 , 0 , 0 ] , subrot = true )
2017-08-30 00:00:16 +00:00
{
2019-03-23 04:13:18 +00:00
cnt = first_defined ( [ count , n ] ) ;
sang = sa + offset ;
angs = is_def ( cnt ) ? ( cnt < = 0 ? [ ] : [ for ( i = [ 0 : cnt - 1 ] ) i / cnt * 360 + sang ] ) : rots ;
if ( cp ! = [ 0 , 0 , 0 ] ) {
translate ( cp ) rot_copies ( rots = rots , v = v , n = cnt , sa = sang , delta = delta , subrot = subrot ) children ( ) ;
} else if ( subrot ) {
for ( $ idx = [ 0 : len ( angs ) - 1 ] ) {
$ ang = angs [ $ idx ] ;
rotate ( a = $ ang , v = v ) translate ( delta ) rot ( a = sang , v = v , reverse = true ) children ( ) ;
2017-08-30 00:00:16 +00:00
}
} else {
2019-03-23 04:13:18 +00:00
for ( $ idx = [ 0 : len ( angs ) - 1 ] ) {
$ ang = angs [ $ idx ] ;
rotate ( a = $ ang , v = v ) translate ( delta ) rot ( a = $ ang , v = v , reverse = true ) children ( ) ;
2017-08-30 00:00:16 +00:00
}
}
}
2019-03-23 04:13:18 +00:00
// Module: xrot_copies()
//
// Description:
// Given an array of angles, rotates copies of the children
// to each of those angles around the X axis.
//
// Usage:
// xrot_copies(rots, [r], [cp], [sa], [subrot]) ...
// xrot_copies(n, [r], [cp], [sa], [subrot]) ...
//
// Arguments:
// rots = Optional array of rotation angles, in degrees, to make copies at.
// cp = Centerpoint to rotate around.
// n = Optional number of evenly distributed copies to be rotated around the ring.
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from Y+, when facing the origin from X+. First unrotated copy is placed at that angle.
// r = Radius to move children back, away from cp, before rotating. Makes rings of copies.
// subrot = If false, don't sub-rotate children as they are copied around the ring.
//
// Side Effects:
// `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
//
2017-08-30 00:00:16 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// xrot_copies([180, 270, 315])
// cylinder(h=20, r1=5, r2=0);
// color("red",0.333) cylinder(h=20, r1=5, r2=0);
//
// Example:
// xrot_copies(n=6)
// cylinder(h=20, r1=5, r2=0);
// color("red",0.333) cylinder(h=20, r1=5, r2=0);
//
// Example:
// xrot_copies(n=6, r=10)
// xrot(-90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) xrot(-90) cylinder(h=20, r1=5, r2=0);
//
// Example:
// xrot_copies(n=6, r=10, sa=45)
// xrot(-90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) xrot(-90) cylinder(h=20, r1=5, r2=0);
//
// Example:
// xrot_copies(n=6, r=20, subrot=false)
// xrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
// color("red",0.333) xrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
module xrot_copies ( rots = [ ] , cp = [ 0 , 0 , 0 ] , n = undef , count = undef , sa = 0 , offset = 0 , r = 0 , subrot = true )
2017-08-30 00:00:16 +00:00
{
2019-03-23 04:13:18 +00:00
cnt = first_defined ( [ count , n ] ) ;
sang = sa + offset ;
rot_copies ( rots = rots , v = V_RIGHT , cp = cp , n = cnt , sa = sang , delta = [ 0 , r , 0 ] , subrot = subrot ) children ( ) ;
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: yrot_copies()
//
// Description:
// Given an array of angles, rotates copies of the children
// to each of those angles around the Y axis.
//
// Usage:
// yrot_copies(rots, [r], [cp], [sa], [subrot]) ...
// yrot_copies(n, [r], [cp], [sa], [subrot]) ...
//
// Arguments:
// rots = Optional array of rotation angles, in degrees, to make copies at.
// cp = Centerpoint to rotate around.
// n = Optional number of evenly distributed copies to be rotated around the ring.
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from X-, when facing the origin from Y+.
// r = Radius to move children left, away from cp, before rotating. Makes rings of copies.
// subrot = If false, don't sub-rotate children as they are copied around the ring.
//
// Side Effects:
// `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
//
// Example:
// yrot_copies([180, 270, 315])
// cylinder(h=20, r1=5, r2=0);
// color("red",0.333) cylinder(h=20, r1=5, r2=0);
//
// Example:
// yrot_copies(n=6)
// cylinder(h=20, r1=5, r2=0);
// color("red",0.333) cylinder(h=20, r1=5, r2=0);
//
// Example:
// yrot_copies(n=6, r=10)
// yrot(-90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0);
//
// Example:
// yrot_copies(n=6, r=10, sa=45)
// yrot(-90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0);
//
// Example:
// yrot_copies(n=6, r=20, subrot=false)
// yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
// color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
module yrot_copies ( rots = [ ] , cp = [ 0 , 0 , 0 ] , n = undef , count = undef , sa = 0 , offset = 0 , r = 0 , subrot = true )
{
cnt = first_defined ( [ count , n ] ) ;
sang = sa + offset ;
rot_copies ( rots = rots , v = V_BACK , cp = cp , n = cnt , sa = sang , delta = [ - r , 0 , 0 ] , subrot = subrot ) children ( ) ;
}
// Module: zrot_copies()
//
// Description:
// Given an array of angles, rotates copies of the children
// to each of those angles around the Z axis.
//
// Usage:
// zrot_copies(rots, [r], [cp], [sa], [subrot]) ...
// zrot_copies(n, [r], [cp], [sa], [subrot]) ...
//
// Arguments:
// rots = Optional array of rotation angles, in degrees, to make copies at.
// cp = Centerpoint to rotate around.
// n = Optional number of evenly distributed copies to be rotated around the ring.
// sa = Starting angle, in degrees. For use with `n`. Angle is in degrees counter-clockwise from X+, when facing the origin from Z+.
// r = Radius to move children right, away from cp, before rotating. Makes rings of copies.
// subrot = If false, don't sub-rotate children as they are copied around the ring.
//
// Side Effects:
// `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
//
// Example:
// zrot_copies([180, 270, 315])
// yrot(90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
//
// Example:
// zrot_copies(n=6)
// yrot(90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
//
// Example:
// zrot_copies(n=6, r=10)
// yrot(90) cylinder(h=20, r1=5, r2=0);
// color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
//
// Example:
// zrot_copies(n=6, r=20, sa=45)
// yrot(90) cylinder(h=20, r1=5, r2=0, center=true);
// color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0, center=true);
//
// Example:
// zrot_copies(n=6, r=20, subrot=false)
// yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
// color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true);
module zrot_copies ( rots = [ ] , cp = [ 0 , 0 , 0 ] , n = undef , count = undef , sa = 0 , offset = 0 , r = 0 , subrot = true )
{
cnt = first_defined ( [ count , n ] ) ;
sang = sa + offset ;
rot_copies ( rots = rots , v = V_UP , cp = cp , n = cnt , sa = sang , delta = [ r , 0 , 0 ] , subrot = subrot ) children ( ) ;
}
// Module: xring()
// Description:
// Distributes `n` copies of the given children on a circle of radius `r`
// around the X axis. If `rot` is true, each copy is rotated in place to keep
// the same side towards the center. The first, unrotated copy will be at the
// starting angle `sa`.
// Usage:
2019-03-31 20:47:33 +00:00
// xring(n, r, [sa], [cp], [rot]) ...
2019-03-23 04:13:18 +00:00
// Arguments:
2019-03-31 20:47:33 +00:00
// n = Number of copies of children to distribute around the circle. (Default: 2)
// r = Radius of ring to distribute children around. (Default: 0)
// sa = Start angle for first (unrotated) copy. (Default: 0)
// cp = Centerpoint of ring. Default: [0,0,0]
2019-03-23 04:13:18 +00:00
// rot = If true, rotate each copy to keep the same side towards the center of the ring. Default: true.
// Side Effects:
// `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index value of each child copy.
2017-08-30 00:00:16 +00:00
// Examples:
2019-03-23 04:13:18 +00:00
// xring(n=6, r=10) xrot(-90) cylinder(h=20, r1=5, r2=0);
// xring(n=6, r=10, sa=45) xrot(-90) cylinder(h=20, r1=5, r2=0);
// xring(n=6, r=20, rot=false) cylinder(h=20, r1=6, r2=0, center=true);
module xring ( n = 2 , r = 0 , sa = 0 , cp = [ 0 , 0 , 0 ] , rot = true )
{
2019-03-31 20:47:33 +00:00
xrot_copies ( count = n , r = r , sa = sa , cp = cp , subrot = rot ) children ( ) ;
2019-03-23 04:13:18 +00:00
}
// Module: yring()
//
// Description:
// Distributes `n` copies of the given children on a circle of radius `r`
// around the Y axis. If `rot` is true, each copy is rotated in place to keep
// the same side towards the center. The first, unrotated copy will be at the
// starting angle `sa`.
//
// Usage:
2019-03-31 20:47:33 +00:00
// yring(n, r, [sa], [cp], [rot]) ...
2019-03-23 04:13:18 +00:00
//
// Arguments:
2019-03-31 20:47:33 +00:00
// n = Number of copies of children to distribute around the circle. (Default: 2)
// r = Radius of ring to distribute children around. (Default: 0)
// sa = Start angle for first (unrotated) copy. (Default: 0)
// cp = Centerpoint of ring. Default: [0,0,0]
2019-03-23 04:13:18 +00:00
// rot = If true, rotate each copy to keep the same side towards the center of the ring. Default: true.
//
// Side Effects:
// `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index value of each child copy.
//
// Examples:
// yring(n=6, r=10) yrot(-90) cylinder(h=20, r1=5, r2=0);
// yring(n=6, r=10, sa=45) yrot(-90) cylinder(h=20, r1=5, r2=0);
// yring(n=6, r=20, rot=false) cylinder(h=20, r1=6, r2=0, center=true);
module yring ( n = 2 , r = 0 , sa = 0 , cp = [ 0 , 0 , 0 ] , rot = true )
2017-08-30 00:00:16 +00:00
{
2019-03-31 20:47:33 +00:00
yrot_copies ( count = n , r = r , sa = sa , cp = cp , subrot = rot ) children ( ) ;
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: zring()
//
// Description:
// Distributes `n` copies of the given children on a circle of radius `r`
// around the Z axis. If `rot` is true, each copy is rotated in place to keep
// the same side towards the center. The first, unrotated copy will be at the
// starting angle `sa`.
//
// Usage:
// zring(r, n, [sa], [cp], [rot]) ...
//
// Arguments:
// n = Number of copies of children to distribute around the circle. (Default: 2)
// r = Radius of ring to distribute children around. (Default: 0)
// sa = Start angle for first (unrotated) copy. (Default: 0)
2019-03-31 20:47:33 +00:00
// cp = Centerpoint of ring. Default: [0,0,0]
2019-03-23 04:13:18 +00:00
// rot = If true, rotate each copy to keep the same side towards the center of the ring. Default: true.
//
// Side Effects:
// `$ang` is set to the relative angle from `cp` of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index value of each child copy.
//
// Examples:
// zring(n=6, r=10) yrot(90) cylinder(h=20, r1=5, r2=0);
// zring(n=6, r=10, sa=45) yrot(90) cylinder(h=20, r1=5, r2=0);
// zring(n=6, r=20, rot=false) yrot(90) cylinder(h=20, r1=6, r2=0, center=true);
module zring ( n = 2 , r = 0 , sa = 0 , cp = [ 0 , 0 , 0 ] , rot = true )
{
2019-03-31 20:47:33 +00:00
zrot_copies ( count = n , r = r , sa = sa , cp = cp , subrot = rot ) children ( ) ;
2019-03-23 04:13:18 +00:00
}
// Module: arc_of()
//
// Description:
// Evenly distributes n duplicate children around an ovoid arc on the XY plane.
//
// Usage:
// arc_of(r|d, n, [sa], [ea], [rot]
// arc_of(rx|dx, ry|dy, n, [sa], [ea], [rot]
//
// Arguments:
2017-08-30 00:00:16 +00:00
// n = number of copies to distribute around the circle. (Default: 6)
// r = radius of circle (Default: 1)
// rx = radius of ellipse on X axis. Used instead of r.
// ry = radius of ellipse on Y axis. Used instead of r.
// d = diameter of circle. (Default: 2)
// dx = diameter of ellipse on X axis. Used instead of d.
// dy = diameter of ellipse on Y axis. Used instead of d.
// rot = whether to rotate the copied children. (Default: false)
// sa = starting angle. (Default: 0.0)
// ea = ending angle. Will distribute copies CCW from sa to ea. (Default: 360.0)
2019-03-23 04:13:18 +00:00
//
// Side Effects:
// `$ang` is set to the rotation angle of each child copy, and can be used to modify each child individually.
// `$pos` is set to the relative centerpoint of each child copy, and can be used to modify each child individually.
// `$idx` is set to the index value of each child copy.
//
// Example:
// #cube(size=[10,3,3],center=true);
// arc_of(d=40, n=5) cube(size=[10,3,3],center=true);
//
// Example:
// #cube(size=[10,3,3],center=true);
// arc_of(d=40, n=5, sa=45, ea=225) cube(size=[10,3,3],center=true);
//
// Example:
// #cube(size=[10,3,3],center=true);
// arc_of(r=15, n=8, rot=false) cube(size=[10,3,3],center=true);
//
// Example:
// #cube(size=[10,3,3],center=true);
// arc_of(rx=20, ry=10, n=8) cube(size=[10,3,3],center=true);
2017-08-30 00:00:16 +00:00
module arc_of (
2019-03-23 04:13:18 +00:00
n = 6 ,
r = undef , rx = undef , ry = undef ,
d = undef , dx = undef , dy = undef ,
sa = 0 , ea = 360 ,
rot = true
2017-08-30 00:00:16 +00:00
) {
2019-03-23 04:13:18 +00:00
rx = get_radius ( rx , r , dx , d , 1 ) ;
ry = get_radius ( ry , r , dy , d , 1 ) ;
sa = posmod ( sa , 360 ) ;
ea = posmod ( ea , 360 ) ;
2017-08-30 00:00:16 +00:00
n = ( abs ( ea - sa ) < 0.01 ) ? ( n + 1 ) : n ;
delt = ( ( ( ea < = sa ) ? 360.0 : 0 ) + ea - sa ) / ( n - 1 ) ;
2019-03-23 04:13:18 +00:00
for ( $ idx = [ 0 : n - 1 ] ) {
$ ang = sa + ( $ idx * delt ) ;
$ pos = [ rx * cos ( $ ang ) , ry * sin ( $ ang ) , 0 ] ;
translate ( $ pos ) {
zrot ( rot ? atan2 ( ry * sin ( $ ang ) , rx * cos ( $ ang ) ) : 0 ) {
2017-08-30 00:00:16 +00:00
children ( ) ;
}
}
}
}
2019-03-23 04:13:18 +00:00
// Module: ovoid_spread()
//
// Description:
// Spreads children semi-evenly over the surface of a sphere.
//
// Usage:
// ovoid_spread(r|d, n, [cone_ang], [scale], [perp]) ...
//
// Arguments:
// r = Radius of the sphere to distribute over
// d = Diameter of the sphere to distribute over
// n = How many copies to evenly spread over the surface.
// cone_ang = Angle of the cone, in degrees, to limit how much of the sphere gets covered. For full sphere coverage, use 180. Measured pre-scaling. Default: 180
// scale = The [X,Y,Z] scaling factors to reshape the sphere being covered.
// perp = If true, rotate children to be perpendicular to the sphere surface. Default: true
//
// Side Effects:
// `$pos` is set to the relative post-scaled centerpoint of each child copy, and can be used to modify each child individually.
// `$theta` is set to the theta angle of the child from the center of the sphere.
// `$phi` is set to the pre-scaled phi angle of the child from the center of the sphere.
// `$rad` is set to the pre-scaled radial distance of the child from the center of the sphere.
// `$idx` is set to the index number of each child being copied.
//
// Example:
// ovoid_spread(n=250, d=100, cone_ang=45, scale=[3,3,1])
// cylinder(d=10, h=10, center=false);
//
2018-10-08 03:00:50 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// ovoid_spread(n=500, d=100, cone_ang=180)
// color(normalize(point3d(vabs($pos))))
// cylinder(d=8, h=10, center=false);
module ovoid_spread ( r = undef , d = undef , n = 100 , cone_ang = 90 , scale = [ 1 , 1 , 1 ] , perp = true )
{
r = get_radius ( r = r , d = d , dflt = 50 ) ;
cnt = ceil ( n / ( cone_ang / 180 ) ) ;
// Calculate an array of [theta,phi] angles for `n` number of
// points, almost evenly spaced across the surface of a sphere.
// This approximation is based on the golden spiral method.
theta_phis = [ for ( x = [ 0 : n - 1 ] ) [ 180 * ( 1 + sqrt ( 5 ) ) * ( x + 0.5 ) % 360 , acos ( 1 - 2 * ( x + 0.5 ) / cnt ) ] ] ;
for ( $ idx = [ 0 : len ( theta_phis ) - 1 ] ) {
2019-04-17 02:16:50 +00:00
tp = theta_phis [ $ idx ] ;
2019-03-23 04:13:18 +00:00
xyz = spherical_to_xyz ( r , tp [ 0 ] , tp [ 1 ] ) ;
$ pos = vmul ( xyz , scale ) ;
$t heta = tp [ 0 ] ;
$ phi = tp [ 1 ] ;
$ rad = r ;
translate ( $ pos ) {
if ( perp ) {
rot ( from = V_UP , to = xyz ) children ( ) ;
} else {
children ( ) ;
}
}
}
}
2018-10-08 03:00:50 +00:00
2019-03-23 04:13:18 +00:00
//////////////////////////////////////////////////////////////////////
// Section: Reflectional Distributors
//////////////////////////////////////////////////////////////////////
2018-10-08 03:00:50 +00:00
2019-03-23 04:13:18 +00:00
// Module: mirror_copy()
//
// Description:
// Makes a copy of the children, mirrored across the given plane.
//
// Usage:
// mirror_copy(v, [cp], [offset]) ...
//
// Arguments:
// v = The normal vector of the plane to mirror across.
// offset = distance to offset away from the plane.
// cp = A point that lies on the mirroring plane.
//
// Side Effects:
// `$orig` is true for the original instance of children. False for the copy.
// `$idx` is set to the index value of each copy.
//
// Example:
// mirror_copy([1,-1,0]) zrot(-45) yrot(90) cylinder(d1=10, d2=0, h=20);
// color("blue",0.25) zrot(-45) cube([0.01,15,15], center=true);
//
2018-10-08 03:00:50 +00:00
// Example:
2019-03-23 04:13:18 +00:00
// mirror_copy([1,1,0], offset=5) rot(a=90,v=[-1,1,0]) cylinder(d1=10, d2=0, h=20);
// color("blue",0.25) zrot(45) cube([0.01,15,15], center=true);
//
// Example:
// mirror_copy(V_UP+V_BACK, cp=[0,-5,-5]) rot(from=V_UP, to=V_BACK+V_UP) cylinder(d1=10, d2=0, h=20);
// color("blue",0.25) translate([0,-5,-5]) rot(from=V_UP, to=V_BACK+V_UP) cube([15,15,0.01], center=true);
module mirror_copy ( v = [ 0 , 0 , 1 ] , offset = 0 , cp = [ 0 , 0 , 0 ] )
{
nv = v / norm ( v ) ;
off = nv * offset ;
if ( cp = = [ 0 , 0 , 0 ] ) {
translate ( off ) {
$ orig = true ;
$ idx = 0 ;
children ( ) ;
}
mirror ( nv ) translate ( off ) {
$ orig = false ;
$ idx = 1 ;
children ( ) ;
}
} else {
translate ( off ) children ( ) ;
translate ( cp ) mirror ( nv ) translate ( - cp ) translate ( off ) children ( ) ;
}
}
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: xflip_copy()
//
// Description:
// Makes a copy of the children, mirrored across the X axis.
//
// Usage:
// xflip_copy([cp], [offset]) ...
//
// Arguments:
// offset = Distance to offset children right, before copying.
// cp = A point that lies on the mirroring plane.
//
// Side Effects:
// `$orig` is true for the original instance of children. False for the copy.
// `$idx` is set to the index value of each copy.
//
// Example:
// xflip_copy() yrot(90) cylinder(h=20, r1=4, r2=0);
// color("blue",0.25) cube([0.01,15,15], center=true);
//
// Example:
// xflip_copy(offset=5) yrot(90) cylinder(h=20, r1=4, r2=0);
// color("blue",0.25) cube([0.01,15,15], center=true);
//
// Example:
// xflip_copy(cp=[-5,0,0]) yrot(90) cylinder(h=20, r1=4, r2=0);
// color("blue",0.25) left(5) cube([0.01,15,15], center=true);
module xflip_copy ( offset = 0 , cp = [ 0 , 0 , 0 ] )
{
mirror_copy ( v = [ 1 , 0 , 0 ] , offset = offset , cp = cp ) children ( ) ;
}
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: yflip_copy()
//
// Description:
// Makes a copy of the children, mirrored across the Y axis.
//
// Usage:
// yflip_copy([cp], [offset]) ...
//
// Arguments:
// offset = Distance to offset children back, before copying.
// cp = A point that lies on the mirroring plane.
//
// Side Effects:
// `$orig` is true for the original instance of children. False for the copy.
// `$idx` is set to the index value of each copy.
//
// Example:
// yflip_copy() xrot(-90) cylinder(h=20, r1=4, r2=0);
// color("blue",0.25) cube([15,0.01,15], center=true);
//
// Example:
// yflip_copy(offset=5) xrot(-90) cylinder(h=20, r1=4, r2=0);
// color("blue",0.25) cube([15,0.01,15], center=true);
//
// Example:
// yflip_copy(cp=[0,-5,0]) xrot(-90) cylinder(h=20, r1=4, r2=0);
// color("blue",0.25) fwd(5) cube([15,0.01,15], center=true);
module yflip_copy ( offset = 0 , cp = [ 0 , 0 , 0 ] )
{
mirror_copy ( v = [ 0 , 1 , 0 ] , offset = offset , cp = cp ) children ( ) ;
}
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
// Module: zflip_copy()
//
// Description:
// Makes a copy of the children, mirrored across the Z axis.
//
// Usage:
// zflip_copy([cp], [offset]) ...
// `$idx` is set to the index value of each copy.
//
// Arguments:
// offset = Distance to offset children up, before copying.
// cp = A point that lies on the mirroring plane.
//
// Side Effects:
// `$orig` is true for the original instance of children. False for the copy.
//
// Example:
// zflip_copy() cylinder(h=20, r1=4, r2=0);
// color("blue",0.25) cube([15,15,0.01], center=true);
//
// Example:
// zflip_copy(offset=5) cylinder(h=20, r1=4, r2=0);
// color("blue",0.25) cube([15,15,0.01], center=true);
//
// Example:
// zflip_copy(cp=[0,0,-5]) cylinder(h=20, r1=4, r2=0);
// color("blue",0.25) down(5) cube([15,15,0.01], center=true);
module zflip_copy ( offset = 0 , cp = [ 0 , 0 , 0 ] )
{
mirror_copy ( v = [ 0 , 0 , 1 ] , offset = offset , cp = cp ) children ( ) ;
}
2017-08-30 00:00:16 +00:00
2019-03-23 04:13:18 +00:00
//////////////////////////////////////////////////////////////////////
// Section: Mutators
//////////////////////////////////////////////////////////////////////
2018-11-19 23:14:02 +00:00
2019-03-23 04:13:18 +00:00
// Module: half_of()
//
// Usage:
// half_of(v, [cp], [s]) ...
//
// Description:
// Slices an object at a cut plane, and masks away everything that is on one side.
//
// Arguments:
// v = Normal of plane to slice at. Keeps everything on the side the normal points to. Default: [0,0,1] (V_UP)
2019-03-31 22:36:13 +00:00
// cp = If given as a scalar, moves the cut plane along the normal by the given amount. If given as a point, specifies a point on the cut plane. This can be used to shift where it slices the object at. Default: [0,0,0]
2019-03-23 04:13:18 +00:00
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, it messes with centering your view. Default: 100
2019-03-31 23:39:15 +00:00
// planar = If true, this becomes a 2D operation. When planar, a `v` of `V_UP` or `V_DOWN` becomes equivalent of `V_BACK` and `V_FWD` respectively.
2019-03-23 04:13:18 +00:00
//
2018-11-19 23:14:02 +00:00
// Examples:
2019-03-23 04:13:18 +00:00
// half_of(V_DOWN+V_BACK, cp=[0,-10,0]) cylinder(h=40, r1=10, r2=0, center=false);
// half_of(V_DOWN+V_LEFT, s=200) sphere(d=150);
2019-03-31 23:39:15 +00:00
// Example(2D):
// half_of([1,1], planar=true) circle(d=50);
module half_of ( v = V_UP , cp = [ 0 , 0 , 0 ] , s = 100 , planar = false )
2019-03-23 04:13:18 +00:00
{
2019-03-31 23:39:15 +00:00
cp = is_scalar ( cp ) ? cp * normalize ( v ) : cp ;
2019-03-23 04:13:18 +00:00
if ( cp ! = [ 0 , 0 , 0 ] ) {
2019-03-31 23:39:15 +00:00
translate ( cp ) half_of ( v = v , s = s , planar = planar ) translate ( - cp ) children ( ) ;
} else if ( planar ) {
v = ( v = = V_UP ) ? V_BACK : ( v = = V_DOWN ) ? V_FWD : v ;
ang = atan2 ( v . y , v . x ) ;
difference ( ) {
children ( ) ;
rotate ( ang + 90 ) {
back ( s / 2 ) square ( s , center = true ) ;
}
}
2019-03-23 04:13:18 +00:00
} else {
difference ( ) {
children ( ) ;
rot ( from = V_UP , to = - v ) {
up ( s / 2 ) cube ( s , center = true ) ;
}
}
}
}
2018-11-19 23:14:02 +00:00
2019-03-23 04:13:18 +00:00
// Module: top_half()
//
// Usage:
// top_half([cp], [s]) ...
//
// Description:
// Slices an object at a horizontal X-Y cut plane, and masks away everything that is below it.
//
// Arguments:
2019-03-31 22:36:13 +00:00
// cp = If given as a scalar, moves the cut plane up by the given amount. If given as a point, specifies a point on the cut plane. Default: [0,0,0]
2019-03-23 04:13:18 +00:00
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, it messes with centering your view. Default: 100
2019-03-31 23:39:15 +00:00
// planar = If true, this becomes equivalent to a planar `back_half()`.
2019-03-23 04:13:18 +00:00
//
// Examples(Spin):
// top_half() sphere(r=20);
2019-03-31 22:36:13 +00:00
// top_half(cp=5) sphere(r=20);
// top_half(cp=[0,0,-8]) sphere(r=20);
2019-03-31 23:39:15 +00:00
// Example(2D):
// top_half(planar=true) circle(r=20);
module top_half ( s = 100 , cp = [ 0 , 0 , 0 ] , planar = false )
2019-03-31 22:36:13 +00:00
{
2019-03-31 23:39:15 +00:00
dir = planar ? V_BACK : V_UP ;
2019-03-31 22:36:13 +00:00
cp = is_scalar ( cp ) ? cp * dir : cp ;
translate ( cp ) difference ( ) {
translate ( - cp ) children ( ) ;
2019-03-31 23:39:15 +00:00
translate ( - dir * s / 2 ) {
if ( planar ) {
square ( s , center = true ) ;
} else {
cube ( s , center = true ) ;
}
}
2019-03-31 22:36:13 +00:00
}
}
2019-03-23 04:13:18 +00:00
// Module: bottom_half()
//
// Usage:
// bottom_half([cp], [s]) ...
//
// Description:
// Slices an object at a horizontal X-Y cut plane, and masks away everything that is above it.
//
// Arguments:
2019-03-31 22:36:13 +00:00
// cp = If given as a scalar, moves the cut plane down by the given amount. If given as a point, specifies a point on the cut plane. Default: [0,0,0]
2019-03-23 04:13:18 +00:00
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, it messes with centering your view. Default: 100
2019-03-31 23:39:15 +00:00
// planar = If true, this becomes equivalent to a planar `front_half()`.
2019-03-23 04:13:18 +00:00
//
// Examples:
// bottom_half() sphere(r=20);
2019-03-31 23:39:15 +00:00
// bottom_half(cp=-10) sphere(r=20);
2019-03-23 04:13:18 +00:00
// bottom_half(cp=[0,0,10]) sphere(r=20);
2019-03-31 23:39:15 +00:00
// Example(2D):
// bottom_half(planar=true) circle(r=20);
module bottom_half ( s = 100 , cp = [ 0 , 0 , 0 ] , planar = false )
2019-03-31 22:36:13 +00:00
{
2019-03-31 23:39:15 +00:00
dir = planar ? V_FWD : V_DOWN ;
2019-03-31 22:36:13 +00:00
cp = is_scalar ( cp ) ? cp * dir : cp ;
translate ( cp ) difference ( ) {
translate ( - cp ) children ( ) ;
2019-03-31 23:39:15 +00:00
translate ( - dir * s / 2 ) {
if ( planar ) {
square ( s , center = true ) ;
} else {
cube ( s , center = true ) ;
}
}
2019-03-31 22:36:13 +00:00
}
}
2019-03-23 04:13:18 +00:00
// Module: left_half()
//
// Usage:
// left_half([cp], [s]) ...
//
// Description:
// Slices an object at a vertical Y-Z cut plane, and masks away everything that is right of it.
//
// Arguments:
2019-03-31 22:36:13 +00:00
// cp = If given as a scalar, moves the cut plane left by the given amount. If given as a point, specifies a point on the cut plane. Default: [0,0,0]
2019-03-23 04:13:18 +00:00
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, it messes with centering your view. Default: 100
2019-03-31 23:39:15 +00:00
// planar = If true, this becomes a 2D operation.
2019-03-23 04:13:18 +00:00
//
2018-11-19 23:14:02 +00:00
// Examples:
2019-03-23 04:13:18 +00:00
// left_half() sphere(r=20);
2019-03-31 23:39:15 +00:00
// left_half(cp=-8) sphere(r=20);
2019-03-23 04:13:18 +00:00
// left_half(cp=[8,0,0]) sphere(r=20);
2019-03-31 23:39:15 +00:00
// Example(2D):
// left_half(planar=true) circle(r=20);
module left_half ( s = 100 , cp = [ 0 , 0 , 0 ] , planar = false )
2019-03-31 22:36:13 +00:00
{
dir = V_LEFT ;
cp = is_scalar ( cp ) ? cp * dir : cp ;
translate ( cp ) difference ( ) {
translate ( - cp ) children ( ) ;
2019-03-31 23:39:15 +00:00
translate ( - dir * s / 2 ) {
if ( planar ) {
square ( s , center = true ) ;
} else {
cube ( s , center = true ) ;
}
}
2019-03-31 22:36:13 +00:00
}
}
2019-03-23 04:13:18 +00:00
// Module: right_half()
//
// Usage:
// right_half([cp], [s]) ...
//
// Description:
// Slices an object at a vertical Y-Z cut plane, and masks away everything that is left of it.
//
// Arguments:
2019-03-31 22:36:13 +00:00
// cp = If given as a scalar, moves the cut plane right by the given amount. If given as a point, specifies a point on the cut plane. Default: [0,0,0]
2019-03-23 04:13:18 +00:00
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, it messes with centering your view. Default: 100
2019-03-31 23:39:15 +00:00
// planar = If true, this becomes a 2D operation.
2019-03-23 04:13:18 +00:00
//
// Examples(FlatSpin):
// right_half() sphere(r=20);
2019-03-31 22:36:13 +00:00
// right_half(cp=-5) sphere(r=20);
2019-03-23 04:13:18 +00:00
// right_half(cp=[-5,0,0]) sphere(r=20);
2019-03-31 23:39:15 +00:00
// Example(2D):
// right_half(planar=true) circle(r=20);
module right_half ( s = 100 , cp = [ 0 , 0 , 0 ] , planar = false )
2019-03-31 22:36:13 +00:00
{
dir = V_RIGHT ;
cp = is_scalar ( cp ) ? cp * dir : cp ;
translate ( cp ) difference ( ) {
translate ( - cp ) children ( ) ;
2019-03-31 23:39:15 +00:00
translate ( - dir * s / 2 ) {
if ( planar ) {
square ( s , center = true ) ;
} else {
cube ( s , center = true ) ;
}
}
2019-03-31 22:36:13 +00:00
}
}
2019-03-23 04:13:18 +00:00
// Module: front_half()
//
// Usage:
// front_half([cp], [s]) ...
//
// Description:
// Slices an object at a vertical X-Z cut plane, and masks away everything that is behind it.
//
// Arguments:
2019-03-31 22:36:13 +00:00
// cp = If given as a scalar, moves the cut plane forward by the given amount. If given as a point, specifies a point on the cut plane. Default: [0,0,0]
2019-03-23 04:13:18 +00:00
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, it messes with centering your view. Default: 100
2019-03-31 23:39:15 +00:00
// planar = If true, this becomes a 2D operation.
2019-03-23 04:13:18 +00:00
//
// Examples(FlatSpin):
// front_half() sphere(r=20);
2019-03-31 22:36:13 +00:00
// front_half(cp=5) sphere(r=20);
2019-03-23 04:13:18 +00:00
// front_half(cp=[0,5,0]) sphere(r=20);
2019-03-31 23:39:15 +00:00
// Example(2D):
// front_half(planar=true) circle(r=20);
module front_half ( s = 100 , cp = [ 0 , 0 , 0 ] , planar = false )
2019-03-31 22:36:13 +00:00
{
dir = V_FWD ;
cp = is_scalar ( cp ) ? cp * dir : cp ;
translate ( cp ) difference ( ) {
translate ( - cp ) children ( ) ;
2019-03-31 23:39:15 +00:00
translate ( - dir * s / 2 ) {
if ( planar ) {
square ( s , center = true ) ;
} else {
cube ( s , center = true ) ;
}
}
2019-03-31 22:36:13 +00:00
}
}
2019-03-23 04:13:18 +00:00
// Module: back_half()
//
// Usage:
// back_half([cp], [s]) ...
//
// Description:
// Slices an object at a vertical X-Z cut plane, and masks away everything that is in front of it.
//
// Arguments:
2019-03-31 22:36:13 +00:00
// cp = If given as a scalar, moves the cut plane back by the given amount. If given as a point, specifies a point on the cut plane. Default: [0,0,0]
2019-03-23 04:13:18 +00:00
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, it messes with centering your view. Default: 100
2019-03-31 23:39:15 +00:00
// planar = If true, this becomes a 2D operation.
2019-03-23 04:13:18 +00:00
//
// Examples:
// back_half() sphere(r=20);
2019-03-31 22:36:13 +00:00
// back_half(cp=8) sphere(r=20);
2019-03-23 04:13:18 +00:00
// back_half(cp=[0,-10,0]) sphere(r=20);
2019-03-31 23:39:15 +00:00
// Example(2D):
// back_half(planar=true) circle(r=20);
module back_half ( s = 100 , cp = [ 0 , 0 , 0 ] , planar = false )
2019-03-31 22:36:13 +00:00
{
dir = V_BACK ;
cp = is_scalar ( cp ) ? cp * dir : cp ;
translate ( cp ) difference ( ) {
translate ( - cp ) children ( ) ;
2019-03-31 23:39:15 +00:00
translate ( - dir * s / 2 ) {
if ( planar ) {
square ( s , center = true ) ;
} else {
cube ( s , center = true ) ;
}
}
2019-03-31 22:36:13 +00:00
}
}
2019-03-23 04:13:18 +00:00
// Module: chain_hull()
//
// Usage:
// chain_hull() ...
//
// Description:
// Performs hull operations between consecutive pairs of children,
// then unions all of the hull results. This can be a very slow
// operation, but it can provide results that are hard to get
// otherwise.
//
// Example:
// chain_hull() {
// cube(5, center=true);
// translate([30, 0, 0]) sphere(d=15);
// translate([60, 30, 0]) cylinder(d=10, h=20);
// translate([60, 60, 0]) cube([10,1,20], center=false);
2018-11-19 23:14:02 +00:00
// }
2019-03-23 04:13:18 +00:00
module chain_hull ( )
{
union ( ) {
if ( $children = = 1 ) {
children ( ) ;
} else if ( $children > 1 ) {
for ( i = [ 1 : $children - 1 ] ) {
hull ( ) {
children ( i - 1 ) ;
children ( i ) ;
}
}
}
}
}
2018-11-19 23:14:02 +00:00
2019-03-23 04:13:18 +00:00
// Module: extrude_arc()
//
// Description:
// Extrudes 2D shapes around a partial circle arc, with optional rounded caps.
// This is mostly useful for backwards compatability with older OpenSCAD versions
// without the `angle` argument in rotate_extrude.
//
// Usage:
// extrude_arc(arc, r|d, [sa], [caps], [orient], [align], [masksize]) ...
//
// Arguments:
// arc = Number of degrees to traverse.
// sa = Start angle in degrees.
// r = Radius of arc.
// d = Diameter of arc.
// orient = The axis to align to. Use `ORIENT_` constants from `constants.scad`
// align = The side of the origin the part should be aligned with. Use `V_` constants from `constants.scad`
// masksize = size of mask used to clear unused part of circle arc. should be larger than height or width of 2D shapes to extrude.
// caps = If true, spin the 2D shapes to make rounded caps the ends of the arc.
// convexity = Max number of times a ray passes through the 2D shape's walls.
//
2019-04-04 07:39:00 +00:00
// Example(Med):
2019-03-23 04:13:18 +00:00
// pts=[[-5/2, -5], [-5/2, 0], [-5/2-3, 5], [5/2+3, 5], [5/2, 0], [5/2, -5]];
// #polygon(points=pts);
// extrude_arc(arc=270, sa=45, r=40, caps=true, convexity=4, $fa=2, $fs=2) {
// polygon(points=pts);
// }
module extrude_arc ( arc = 90 , sa = 0 , r = undef , d = undef , orient = ORIENT_Z , align = V_CENTER , masksize = 100 , caps = false , convexity = 4 )
{
eps = 0.001 ;
r = get_radius ( r = r , d = d , dflt = 100 ) ;
orient_and_align ( [ 2 * r , 2 * r , 0 ] , orient , align ) {
zrot ( sa ) {
if ( caps ) {
place_copies ( [ [ r , 0 , 0 ] , cylindrical_to_xyz ( r , arc , 0 ) ] ) {
rotate_extrude ( convexity = convexity ) {
difference ( ) {
children ( ) ;
left ( masksize / 2 ) square ( masksize , center = true ) ;
}
2017-08-30 00:00:16 +00:00
}
}
}
2019-03-23 04:13:18 +00:00
difference ( ) {
rotate_extrude ( angle = arc , convexity = convexity * 2 ) {
right ( r ) {
2017-08-30 00:00:16 +00:00
children ( ) ;
}
}
2019-03-23 04:13:18 +00:00
if ( version_num ( ) < 20190000 ) {
maxd = r + masksize ;
if ( arc < 180 ) rotate ( arc ) back ( maxd / 2 ) cube ( [ 2 * maxd , maxd , masksize + 0.1 ] , center = true ) ;
difference ( ) {
fwd ( maxd / 2 ) cube ( [ 2 * maxd , maxd , masksize + 0.2 ] , center = true ) ;
if ( arc > 180 ) rotate ( arc - 180 ) back ( maxd / 2 ) cube ( [ 2 * maxd , maxd , masksize + 0.1 ] , center = true ) ;
}
}
2017-08-30 00:00:16 +00:00
}
}
}
}
2019-04-03 01:52:37 +00:00
//////////////////////////////////////////////////////////////////////
// Section: 2D Mutators
//////////////////////////////////////////////////////////////////////
// Module: round2d()
// Usage:
// round2d(r) ...
// round2d(or) ...
// round2d(ir) ...
// round2d(or, ir) ...
// Description:
// Rounds an arbitrary 2d objects. Giving `r` rounds all concave and
// convex corners. Giving just `ir` rounds just concave corners.
// Giving just `or` rounds convex corners. Giving both `ir` and `or`
// can let you round to different radii for concave and convex corners.
// The 2d object must not have any parts narrower than twice the `or`
// radius. Such parts will disappear.
// Arguments:
// r = Radius to round all concave and convex corners to.
// or = Radius to round only outside (convex) corners to. Use instead of `r`.
// ir = Radius to round/fillet only inside (concave) corners to. Use instead of `r`.
// Examples(2D):
// round2d(r=10) {square([40,100], center=true); square([100,40], center=true);}
// round2d(or=10) {square([40,100], center=true); square([100,40], center=true);}
// round2d(ir=10) {square([40,100], center=true); square([100,40], center=true);}
// round2d(or=16,ir=8) {square([40,100], center=true); square([100,40], center=true);}
module round2d ( r , or , ir )
{
or = get_radius ( r1 = or , r = r , dflt = 0 ) ;
ir = get_radius ( r1 = ir , r = r , dflt = 0 ) ;
offset ( or ) offset ( - ir - or ) offset ( delta = ir ) children ( ) ;
}
// Module: shell2d()
// Usage:
// shell2d(thickness, [or], [ir], [fill], [round])
// Description:
// Creates a hollow shell from 2d children, with optional rounding.
// Arguments:
// thickness = Thickness of the shell. Positive to expand outward, negative to shrink inward, or a two-element list to do both.
// or = Radius to round convex corners/pointy bits on the outside of the shell.
// ir = Radius to round/fillet concave corners on the outside of the shell.
// round = Radius to round convex corners/pointy bits on the inside of the shell.
// fill = Radius to round/fillet concave corners on the inside of the shell.
// Examples(2D):
// shell2d(10) {square([40,100], center=true); square([100,40], center=true);}
// shell2d(-10) {square([40,100], center=true); square([100,40], center=true);}
// shell2d([-10,10]) {square([40,100], center=true); square([100,40], center=true);}
// shell2d(10,or=10) {square([40,100], center=true); square([100,40], center=true);}
// shell2d(10,ir=10) {square([40,100], center=true); square([100,40], center=true);}
// shell2d(10,round=10) {square([40,100], center=true); square([100,40], center=true);}
// shell2d(10,fill=10) {square([40,100], center=true); square([100,40], center=true);}
// shell2d(8,or=16,ir=8,round=16,fill=8) {square([40,100], center=true); square([100,40], center=true);}
module shell2d ( thickness , or = 0 , ir = 0 , fill = 0 , round = 0 )
{
thickness = is_scalar ( thickness ) ? (
thickness < 0 ? [ thickness , 0 ] : [ 0 , thickness ]
) : ( thickness [ 0 ] > thickness [ 1 ] ) ? (
[ thickness [ 1 ] , thickness [ 0 ] ]
) : thickness ;
difference ( ) {
round2d ( or = or , ir = ir )
offset ( delta = thickness [ 1 ] )
children ( ) ;
round2d ( or = fill , ir = round )
offset ( delta = thickness [ 0 ] )
children ( ) ;
}
}
2019-03-23 04:13:18 +00:00
//////////////////////////////////////////////////////////////////////
// Section: Miscellaneous
//////////////////////////////////////////////////////////////////////
// Module: orient_and_align()
//
// Description:
// Takes a vertically oriented shape, and re-orients and aligns it.
// This is useful for making a custom shape available in various
// orientations and alignments without extra translate()s and rotate()s.
// Children should be vertically (Z-axis) oriented, and centered.
// Non-extremity alignment points should be named via the `alignments` arg.
// Named alignments, as well as `ALIGN_NEG`/`ALIGN_POS` are aligned pre-rotation.
//
// Usage:
2019-04-17 02:16:50 +00:00
// orient_and_align(size, [orient], [align], [center], [noncentered], [orig_orient], [orig_align], [alignments], [chain]) ...
2019-03-23 04:13:18 +00:00
//
// Arguments:
2019-04-17 02:16:50 +00:00
// size = The [X,Y,Z] size of the part.
// size2 = The [X,Y] size of the top of the part.
// shift = The [X,Y] offset of the top of the part, compared to the bottom of the part.
// orient = The axis to align to. Use `ORIENT_` constants from `constants.scad`.
2019-03-23 04:13:18 +00:00
// align = The side of the origin the part should be aligned with.
// center = If given, overrides `align`. If true, centers vertically. If false, `align` will be set to the value in `noncentered`.
// noncentered = The value to set `align` to if `center` == `false`. Default: `V_UP`.
// orig_orient = The original orientation of the part. Default: `ORIENT_Z`.
// orig_align = The original alignment of the part. Default: `V_CENTER`.
2019-04-17 02:16:50 +00:00
// alignments = A list of extra, non-standard connectors that can be aligned to.
// chain = If true, allow attachable children.
//
// Side Effects:
// `$parent_size` is set to the parent object's cubical region size.
// `$parent_size2` is set to the parent object's top [X,Y] size.
// `$parent_shift` is set to the parent object's `shift` value, if any.
// `$parent_orient` is set to the parent object's `orient` value.
// `$parent_align` is set to the parent object's `align` value.
// `$parent_conns` is set to the parent object's list of non-standard extra connectors.
2019-03-23 04:13:18 +00:00
//
// Example:
// #cylinder(d=5, h=10);
// orient_and_align([5,5,10], orient=ORIENT_Y, align=V_BACK, orig_align=V_UP) cylinder(d=5, h=10);
module orient_and_align (
size = undef , orient = ORIENT_Z , align = V_CENTER ,
center = undef , noncentered = ALIGN_POS ,
orig_orient = ORIENT_Z , orig_align = V_CENTER ,
2019-04-17 02:16:50 +00:00
size2 = undef , shift = [ 0 , 0 ] ,
alignments = [ ] , chain = false
2019-03-23 04:13:18 +00:00
) {
2019-04-17 02:16:50 +00:00
size2 = point2d ( default ( size2 , size ) ) ;
shift = point2d ( shift ) ;
align = is_def ( center ) ? ( center ? V_CENTER : noncentered ) : align ;
m = matrix4_mult ( concat (
( orig_align = = V_CENTER ) ? [ ] : [
// If original alignment is not centered, center it.
matrix4_translate ( vmul ( size / 2 , - orig_align ) )
] ,
( orig_orient = = ORIENT_Z ) ? [ ] : [
// If original orientation is not upright, rotate it upright.
matrix4_zrot ( - orig_orient . z ) ,
matrix4_yrot ( - orig_orient . y ) ,
matrix4_xrot ( - orig_orient . x )
] ,
( $ attach_to ! = undef ) ? (
let (
conn = find_connector ( $ attach_to , size . z , size , size2 = size2 , shift = shift ) ,
ang = vector_angle ( conn [ 2 ] , V_DOWN ) ,
axis = vector_axis ( conn [ 2 ] , V_DOWN ) ,
ang2 = ( conn [ 2 ] = = V_UP || conn [ 2 ] = = V_DOWN ) ? 0 : 180 - conn [ 3 ] ,
axis2 = rotate_points3d ( [ axis ] , [ 0 , 0 , ang2 ] ) [ 0 ]
) [
matrix4_translate ( - conn [ 1 ] ) ,
matrix4_zrot ( ang2 ) ,
matrix4_rot_by_axis ( axis2 , ang )
]
) : concat (
( ! is_scalar ( align ) && ! is_str ( align ) ) ? [ ] : [
let ( conn = find_connector ( align , size . z , size , size2 = size2 , shift = shift , extra_conns = alignments ) )
matrix4_translate ( - conn [ 1 ] )
] ,
( orient = = ORIENT_Z ) ? [ ] : [
matrix4_xrot ( orient . x ) ,
matrix4_yrot ( orient . y ) ,
matrix4_zrot ( orient . z )
] ,
( ! is_array ( align ) || align = = [ 0 , 0 , 0 ] ) ? [ ] : [
let ( conn = find_connector ( align , size . z , size , size2 = size2 , shift = shift ) )
matrix4_translate ( conn [ 1 ] )
]
)
) ) ;
$ attach_to = undef ;
$ parent_size = size ;
$ parent_size2 = size2 ;
$ parent_shift = shift ;
$ parent_orient = orient ;
$ parent_align = align ;
$ parent_conns = alignments ;
tags = _str_char_split ( $t ags , " " ) ;
s_tags = $t ags_shown ;
h_tags = $t ags_hidden ;
shown = ! s_tags || any ( [ for ( tag = tags ) in_list ( tag , s_tags ) ] ) ;
hidden = any ( [ for ( tag = tags ) in_list ( tag , h_tags ) ] ) ;
echo ( tags = tags , shown = shown , hidden = hidden , view = shown && ! hidden ) ;
multmatrix ( m ) {
if ( $children > 1 && chain ) {
if ( shown && ! hidden ) color ( $ color ) for ( i = [ 0 : $children - 2 ] ) children ( i ) ;
children ( $children - 1 ) ;
2019-03-23 04:13:18 +00:00
} else {
2019-04-17 02:16:50 +00:00
if ( shown && ! hidden ) color ( $ color ) children ( ) ;
2019-03-23 04:13:18 +00:00
}
2019-04-17 02:16:50 +00:00
}
}
// Internal. Not exposed.
function _str_char_split ( s , delim , n = 0 , acc = [ ] , word = "" ) =
( n >= len ( s ) ) ? concat ( acc , [ word ] ) :
( s [ n ] = = delim ) ?
_str_char_split ( s , delim , n + 1 , concat ( acc , [ word ] ) , "" ) :
_str_char_split ( s , delim , n + 1 , acc , str ( word , s [ n ] ) ) ;
// Function: connector()
// Usage:
// connector(name, pos, dir, [rot])
// Description:
// Creates a connector data structure.
// Arguments:
// name = The string name of the connector. Lowercase. Words separated by single dashes. No spaces.
// pos = The [X,Y,Z] position of the connector.
// dir = A vector pointing in the direction parts should project from the connector position.
// rot = If needed, the angle to rotate the part around the direction vector.
function connector ( name , pos = [ 0 , 0 , 0 ] , dir = V_UP , rot = 0 ) = [ name , pos , dir , rot ] ;
// Function: find_connector()
// Usage:
// find_connector(align, h, size, [size2], [shift], [edges], [corners]);
// Description:
// Generates a list of typical connectors for a cubical region of the given size.
// Arguments:
// align = Named alignment/connector string.
// h = Height of the region.
// size = The [X,Y] size of the bottom of the cubical region.
// size2 = The [X,Y] size of the top of the cubical region.
// shift = The [X,Y] amount to shift the center of the top with respect to the center of the bottom.
// extra_conns = A list of extra named connectors.
function find_connector ( align , h , size , size2 = undef , shift = [ 0 , 0 ] , extra_conns = [ ] ) =
let (
eps = 1e-9 ,
shift = point3d ( shift ) ,
size = point3d ( point2d ( size ) ) ,
size2 = ( size2 ! = undef ) ? point3d ( point2d ( size2 ) ) : size ,
found = ! is_str ( align ) ? [ ] : search ( [ align ] , extra_conns , num_returns_per_match = 1 ) [ 0 ]
) ( found ! = [ ] ) ? extra_conns [ found ] : let (
words = is_scalar ( align ) ? (
align = = ALIGN_NEG ? [ "top" ] :
align = = ALIGN_POS ? [ "bottom" ] :
[ "center" ]
) : is_array ( align ) ? align : _str_char_split ( align , "-" ) ,
ovec = is_array ( align ) ? align :
sum ( [
for ( word = words )
word = = "left" ? V_LEFT :
word = = "right" ? V_RIGHT :
word = = "front" ? V_FWD :
word = = "back" ? V_BACK :
word = = "top" ? V_UP :
word = = "bottom" ? V_DOWN :
word = = "center" ? V_ZERO :
assertion ( false ,
str (
"Alignment label '" , align , "' is not known." ,
( ! extra_conns ? "" : str (
" Try one of " , [ for ( v = extra_conns ) v [ 0 ] ] , " or the standard alignments."
) )
)
)
] ) ,
top = [ - size2 / 2 + shift , shift , size2 / 2 + shift ] ,
bot = [ - size / 2 , V_ZERO , size / 2 ] ,
toppt = [ top [ ovec . x + 1 ] . x , top [ ovec . y + 1 ] . y , h / 2 ] ,
botpt = [ bot [ ovec . x + 1 ] . x , bot [ ovec . y + 1 ] . y , - h / 2 ] ,
pos = lerp ( botpt , toppt , ( ovec . z + 1 ) / 2 ) ,
oang = (
ovec = = V_UP ? 0 :
ovec = = V_DOWN ? 0 :
( norm ( [ ovec . x , ovec . y ] ) < eps ) ? 0 : atan2 ( ovec . y , ovec . x ) + 90
) ,
vec = (
abs ( ovec . z ) > eps ? ovec :
rotate_points3d ( [ ovec ] , from = V_UP , to = toppt - botpt ) [ 0 ]
)
) [ align , pos , vec , oang ] ;
// Module: attach()
// Usage:
// attach(name, [overlap], [norot]) ...
// attach(name, to, [overlap]) ...
// Description:
// Attaches children to a parent object at an attachment point and orientation.
// Arguments:
// name = The name of the parent attachment point to attach to.
// to = The name of the child attachment point.
// overlap = Amount to sink child into the parent.
// norot = If true, don't rotate children when aligning to the attachment point.
// Example:
// spheroid(d=20) {
// attach("top") down(1.5) cyl(l=11.5, d1=10, d2=5, align="bottom");
// attach("right", "bottom") down(1.5) cyl(l=11.5, d1=10, d2=5);
// attach("front") down(1.5) cyl(l=11.5, d1=10, d2=5, align="bottom");
// }
module attach ( name , to = undef , overlap = undef , norot = false )
{
assertion ( $ parent_size ! = undef , "No object to attach to!" ) ;
overlap = ( overlap ! = undef ) ? overlap : $ overlap ;
conn = find_connector ( name , $ parent_size . z , point2d ( $ parent_size ) , size2 = $ parent_size2 , shift = $ parent_shift , extra_conns = $ parent_conns ) ;
pos = conn [ 1 ] ;
vec = conn [ 2 ] ;
ang = conn [ 3 ] ;
$ attach_to = to ;
$ attach_conn = conn ;
if ( norot || ( norm ( vec - V_UP ) < 1e-9 && ang = = 0 ) ) {
translate ( pos ) translate ( [ 0 , 0 , - overlap ] ) children ( ) ;
2019-03-23 04:13:18 +00:00
} else {
2019-04-17 02:16:50 +00:00
translate ( pos ) rot ( ang , from = V_UP , to = vec ) translate ( [ 0 , 0 , - overlap ] ) children ( ) ;
}
}
// Module: tags()
// Usage:
// tags(tags) ...
// Description:
// Marks all children with the given tags.
// Arguments:
// tags = String containing space delimited set of tags to apply.
module tags ( tags )
{
$t ags = tags ;
children ( ) ;
}
// Module: recolor()
// Usage:
// recolor(c) ...
// Description:
// Sets the color for children that can use the $color special variable.
// Example:
// recolor("red") cyl(l=20, d=10);
module recolor ( c )
{
$ color = c ;
children ( ) ;
}
// Module: hide()
// Usage:
// hide(tags) ...
// Description: Hides all children with the given tags.
module hide ( tags = "" )
{
$t ags_hidden = tags = = "" ? [ ] : _str_char_split ( tags , " " ) ;
children ( ) ;
}
// Module: show()
// Usage:
// show(tags) ...
// Description: Shows only children with the given tags.
module show ( tags = "" )
{
$t ags_shown = tags = = "" ? [ ] : _str_char_split ( tags , " " ) ;
children ( ) ;
}
// Module: diff()
// Usage:
// diff(neg, [keep]) ...
// diff(neg, pos, [keep]) ...
// Description:
// If `neg` is given, takes the union of all children with tags
// that are in `neg`, and differences them from the union of all
// children with tags in `pos`. If `pos` is not given, then all
// items in `neg` are differenced from all items not in `neg`. If
// `keep` is given, all children with tags in `keep` are then unioned
// with the result. If `keep` is not given, all children without
// tags in `pos` or `neg` are then unioned with the result.
// Arguments:
// neg = String containing space delimited set of tag names of children to difference away.
// pos = String containing space delimited set of tag names of children to be differenced away from.
// keep = String containing space delimited set of tag names of children to keep whole.
module diff ( neg , pos = undef , keep = undef )
{
difference ( ) {
if ( pos ! = undef ) {
show ( pos ) children ( ) ;
} else {
if ( keep = = undef ) {
hide ( neg ) children ( ) ;
} else {
hide ( str ( neg , " " , keep ) ) children ( ) ;
}
}
show ( neg ) children ( ) ;
}
if ( keep ! = undef ) {
show ( keep ) children ( ) ;
} else if ( pos ! = undef ) {
hide ( str ( pos , " " , neg ) ) children ( ) ;
2019-03-23 04:13:18 +00:00
}
}
2017-08-30 00:00:16 +00:00
2019-04-17 02:16:50 +00:00
// Module: intersect()
// Usage:
// intersect(a, [keep]) ...
// intersect(a, b, [keep]) ...
// Description:
// If `a` is given, takes the union of all children with tags that
// are in `a`, and intersection()s them with the union of all
// children with tags in `b`. If `b` is not given, then the union
// of all items with tags in `a` are intersection()ed with the union
// of all items without tags in `a`. If `keep` is given, then the
// result is unioned with all the children with tags in `keep`. If
// `keep` is not given, all children without tags in `a` or `b` are
// unioned with the result.
// Arguments:
// a = String containing space delimited set of tag names of children.
// b = String containing space delimited set of tag names of children.
// keep = String containing space delimited set of tag names of children to keep whole.
module intersect ( a , b = undef , keep = undef )
{
intersection ( ) {
if ( b ! = undef ) {
show ( b ) children ( ) ;
} else {
if ( keep = = undef ) {
hide ( a ) children ( ) ;
} else {
hide ( str ( a , " " , keep ) ) children ( ) ;
}
}
show ( a ) children ( ) ;
}
if ( keep ! = undef ) {
show ( keep ) children ( ) ;
} else if ( b ! = undef ) {
hide ( str ( a , " " , b ) ) children ( ) ;
}
}
2017-08-30 00:00:16 +00:00
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap