2020-02-01 01:03:41 +00:00
//////////////////////////////////////////////////////////////////////
2021-01-05 10:19:46 +00:00
// LibFile: mutators.scad
2020-02-01 01:03:41 +00:00
// Functions and modules to mutate children in various ways.
2021-01-05 09:20:01 +00:00
// Includes:
2020-02-01 01:03:41 +00:00
// include <BOSL2/std.scad>
2021-12-13 23:48:30 +00:00
// FileGroup: Basic Modeling
// FileSummary: Modules and Functions to mutate items.
// FileFootnotes: STD=Included in std.scad
2020-02-01 01:03:41 +00:00
//////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////
2021-12-17 03:31:49 +00:00
// Section: Bounding Box
2020-02-01 01:03:41 +00:00
//////////////////////////////////////////////////////////////////////
2020-09-14 04:50:39 +00:00
// Module: bounding_box()
2023-03-30 00:19:52 +00:00
// Synopsis: Creates the smallest bounding box that contains all the children.
// Topics: Mutators, Bounds, Bounding Boxes
2023-03-31 02:28:29 +00:00
// See Also: pointlist_bounds()
2020-09-14 04:50:39 +00:00
// Usage:
2022-04-08 23:37:46 +00:00
// bounding_box([excess],[planar]) CHILDREN;
2020-09-14 04:50:39 +00:00
// Description:
2021-01-08 04:16:28 +00:00
// Returns the smallest axis-aligned square (or cube) shape that contains all the 2D (or 3D)
2022-04-08 23:37:46 +00:00
// children given. The module children() must 3d when planar=false and
// 2d when planar=true, or you will get a warning of mixing dimension
2021-01-08 04:16:28 +00:00
// or scaling by 0.
2020-09-14 04:50:39 +00:00
// Arguments:
// excess = The amount that the bounding box should be larger than needed to bound the children, in each axis.
2020-12-30 05:02:46 +00:00
// planar = If true, creates a 2D bounding rectangle. Is false, creates a 3D bounding cube. Default: false
2021-01-08 04:16:28 +00:00
// Example(3D):
// module shapes() {
2020-09-14 04:50:39 +00:00
// translate([10,8,4]) cube(5);
// translate([3,0,12]) cube(2);
// }
2021-01-08 04:16:28 +00:00
// #bounding_box() shapes();
// shapes();
// Example(2D):
// module shapes() {
// translate([10,8]) square(5);
// translate([3,0]) square(2);
// }
// #bounding_box(planar=true) shapes();
// shapes();
2021-01-05 10:06:39 +00:00
module bounding_box ( excess = 0 , planar = false ) {
2021-01-08 04:16:28 +00:00
// a 3d (or 2d when planar=true) approx. of the children projection on X axis
module _xProjection ( ) {
2020-12-30 05:02:46 +00:00
if ( planar ) {
2020-12-19 03:04:28 +00:00
projection ( )
rotate ( [ 90 , 0 , 0 ] )
2021-01-08 04:16:28 +00:00
linear_extrude ( 1 , center = true )
2020-12-30 05:02:46 +00:00
hull ( )
children ( ) ;
} else {
2021-01-08 04:16:28 +00:00
xs = excess < . 1 ? 1 : excess ;
2020-12-30 05:02:46 +00:00
linear_extrude ( xs , center = true )
projection ( )
rotate ( [ 90 , 0 , 0 ] )
linear_extrude ( xs , center = true )
projection ( )
hull ( )
children ( ) ;
}
2021-01-08 04:16:28 +00:00
}
2020-09-14 04:50:39 +00:00
// a bounding box with an offset of 1 in all axis
module _oversize_bbox ( ) {
2020-12-30 06:04:47 +00:00
if ( planar ) {
minkowski ( ) {
_xProjection ( ) children ( ) ; // x axis
rotate ( - 90 ) _xProjection ( ) rotate ( 90 ) children ( ) ; // y axis
}
} else {
minkowski ( ) {
_xProjection ( ) children ( ) ; // x axis
2020-12-30 05:02:46 +00:00
rotate ( - 90 ) _xProjection ( ) rotate ( 90 ) children ( ) ; // y axis
2020-12-30 06:04:47 +00:00
rotate ( [ 0 , - 90 , 0 ] ) _xProjection ( ) rotate ( [ 0 , 90 , 0 ] ) children ( ) ; // z axis
2020-12-30 05:02:46 +00:00
}
2020-09-14 04:50:39 +00:00
}
}
2021-01-08 04:16:28 +00:00
// offsets a cube by `excess`
2020-09-14 04:50:39 +00:00
module _shrink_cube ( ) {
intersection ( ) {
2021-01-08 04:16:28 +00:00
translate ( ( 1 - excess ) * [ 1 , 1 , 1 ] ) children ( ) ;
translate ( ( 1 - excess ) * [ - 1 , - 1 , - 1 ] ) children ( ) ;
2020-09-14 04:50:39 +00:00
}
}
2021-01-08 04:16:28 +00:00
if ( planar ) {
offset ( excess - 1 / 2 ) _oversize_bbox ( ) children ( ) ;
2020-09-14 04:50:39 +00:00
} else {
2021-01-08 04:16:28 +00:00
render ( convexity = 2 )
if ( excess > . 1 ) {
_oversize_bbox ( ) children ( ) ;
} else {
_shrink_cube ( ) _oversize_bbox ( ) children ( ) ;
}
2020-09-14 04:50:39 +00:00
}
}
2020-02-01 01:03:41 +00:00
//////////////////////////////////////////////////////////////////////
2021-01-17 09:36:43 +00:00
// Section: Warp Mutators
2020-02-01 01:03:41 +00:00
//////////////////////////////////////////////////////////////////////
2021-01-17 09:36:43 +00:00
2020-02-01 01:03:41 +00:00
// Module: chain_hull()
2023-03-30 00:19:52 +00:00
// Synopsis: Performs the union of hull operations between consecutive pairs of children.
// Topics: Mutators
2023-03-31 02:28:29 +00:00
// See Also: hull()
2020-02-01 01:03:41 +00:00
// Usage:
2022-04-08 23:37:46 +00:00
// chain_hull() CHILDREN;
2020-02-01 01:03:41 +00:00
//
// 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.
//
// Side Effects:
// `$idx` is set to the index value of the first child of each hulling pair, and can be used to modify each child pair individually.
// `$primary` is set to true when the child is the first in a chain pair.
//
// 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);
// }
// Example: Using `$idx` and `$primary`
// chain_hull() {
// zrot( 0) right(100) if ($primary) cube(5+3*$idx,center=true); else sphere(r=10+3*$idx);
// zrot( 45) right(100) if ($primary) cube(5+3*$idx,center=true); else sphere(r=10+3*$idx);
// zrot( 90) right(100) if ($primary) cube(5+3*$idx,center=true); else sphere(r=10+3*$idx);
// zrot(135) right(100) if ($primary) cube(5+3*$idx,center=true); else sphere(r=10+3*$idx);
// zrot(180) right(100) if ($primary) cube(5+3*$idx,center=true); else sphere(r=10+3*$idx);
// }
module chain_hull ( )
{
2020-05-30 02:04:34 +00:00
union ( ) {
if ( $children = = 1 ) {
children ( ) ;
} else if ( $children > 1 ) {
for ( i = [ 1 : 1 : $children - 1 ] ) {
$ idx = i ;
hull ( ) {
let ( $ primary = true ) children ( i - 1 ) ;
let ( $ primary = false ) children ( i ) ;
}
}
}
}
2020-02-01 01:03:41 +00:00
}
2021-01-17 09:36:43 +00:00
// Module: path_extrude2d()
2023-03-30 00:19:52 +00:00
// Synopsis: Extrudes 2D children along a 2D path.
// Topics: Mutators, Extrusion
// See Also: path_sweep(), path_extrude()
2021-01-17 09:36:43 +00:00
// Usage:
2022-04-08 23:37:46 +00:00
// path_extrude2d(path, [caps=], [closed=], [s=], [convexity=]) 2D-CHILDREN;
2021-01-17 09:36:43 +00:00
// Description:
2021-11-07 22:53:53 +00:00
// Extrudes 2D children along the given 2D path, with optional rounded endcaps.
// It works by constructing straight sections corresponding to each segment of the path and inserting rounded joints at each corner.
// If the children are symmetric across the Y axis line then you can set caps=true to produce rounded caps on the ends of the profile.
// If you set caps to true for asymmetric children then incorrect caps will be generated.
2021-01-17 09:36:43 +00:00
// Arguments:
// path = The 2D path to extrude the geometry along.
2022-04-08 23:37:46 +00:00
// ---
2021-11-10 03:27:55 +00:00
// caps = If true, caps each end of the path with a rounded copy of the children. Children must by symmetric across the Y axis, or results are wrong. Default: false
2021-10-31 03:15:59 +00:00
// closed = If true, connect the starting point of the path to the ending point. Default: false
2021-11-07 22:53:53 +00:00
// convexity = The max number of times a line could pass though a wall. Default: 10
// 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: The length of the diagonal of the path's bounding box.
2021-01-17 09:36:43 +00:00
// Example:
// path = [
// each right(50, p=arc(d=100,angle=[90,180])),
// each left(50, p=arc(d=100,angle=[0,-90])),
// ];
// path_extrude2d(path,caps=false) {
// fwd(2.5) square([5,6],center=true);
// fwd(6) square([10,5],center=true);
// }
// Example:
2021-10-31 03:15:59 +00:00
// path_extrude2d(arc(d=100,angle=[180,270]),caps=true)
2021-01-17 09:36:43 +00:00
// trapezoid(w1=10, w2=5, h=10, anchor=BACK);
// Example:
// include <BOSL2/beziers.scad>
2022-01-07 19:23:01 +00:00
// path = bezpath_curve([
2021-01-17 09:36:43 +00:00
// [-50,0], [-25,50], [0,0], [50,0]
// ]);
// path_extrude2d(path, caps=false)
2021-11-07 22:53:53 +00:00
// trapezoid(w1=10, w2=3, h=5, anchor=BACK);
// Example: Un-Closed Path
// $fn=16;
// spath = star(id=15,od=35,n=5);
// path_extrude2d(spath, caps=false, closed=false)
// move_copies([[-3.5,1.5],[0.0,3.0],[3.5,1.5]])
// circle(r=1.5);
// Example: Complex Endcaps
// $fn=16;
// spath = star(id=15,od=35,n=5);
// path_extrude2d(spath, caps=true, closed=false)
// move_copies([[-3.5,1.5],[0.0,3.0],[3.5,1.5]])
// circle(r=1.5);
module path_extrude2d ( path , caps = false , closed = false , s , convexity = 10 ) {
2021-10-31 19:35:45 +00:00
extra_ang = 0.1 ; // Extra angle for overlap of joints
2022-11-09 21:54:27 +00:00
check =
assert ( caps = = false || closed = = false , "Cannot have caps on a closed extrusion" )
assert ( is_path ( path , 2 ) ) ;
2021-01-17 09:36:43 +00:00
path = deduplicate ( path ) ;
2021-11-07 22:53:53 +00:00
s = s ! = undef ? s :
let ( b = pointlist_bounds ( path ) )
norm ( b [ 1 ] - b [ 0 ] ) ;
2022-11-09 21:54:27 +00:00
check2 = assert ( is_finite ( s ) ) ;
2021-11-07 22:53:53 +00:00
L = len ( path ) ;
for ( i = [ 0 : 1 : L - ( closed ? 1 : 2 ) ] ) {
seg = select ( path , i , i + 1 ) ;
segv = seg [ 1 ] - seg [ 0 ] ;
seglen = norm ( segv ) ;
translate ( ( seg [ 0 ] + seg [ 1 ] ) / 2 ) {
rot ( from = BACK , to = segv ) {
difference ( ) {
xrot ( 90 ) {
linear_extrude ( height = seglen , center = true , convexity = convexity ) {
children ( ) ;
}
}
if ( closed || i > 0 ) {
pt = select ( path , i - 1 ) ;
pang = v_theta ( rot ( from = - segv , to = RIGHT , p = pt - seg [ 0 ] ) ) ;
fwd ( seglen / 2 + 0.01 ) zrot ( pang / 2 ) cube ( s , anchor = BACK ) ;
}
if ( closed || i < L - 2 ) {
pt = select ( path , i + 2 ) ;
pang = v_theta ( rot ( from = segv , to = RIGHT , p = pt - seg [ 1 ] ) ) ;
back ( seglen / 2 + 0.01 ) zrot ( pang / 2 ) cube ( s , anchor = FWD ) ;
}
}
2021-11-04 02:30:01 +00:00
}
2021-11-07 22:53:53 +00:00
}
2021-11-04 02:30:01 +00:00
}
for ( t = triplet ( path , wrap = closed ) ) {
ang = - ( 180 - vector_angle ( t ) ) * sign ( _point_left_of_line2d ( t [ 2 ] , [ t [ 0 ] , t [ 1 ] ] ) ) ;
delt = point3d ( t [ 2 ] - t [ 1 ] ) ;
if ( ang ! = 0 )
translate ( t [ 1 ] ) {
2021-10-31 19:35:45 +00:00
frame_map ( y = delt , z = UP )
rotate ( - sign ( ang ) * extra_ang / 2 )
rotate_extrude ( angle = ang + sign ( ang ) * extra_ang )
if ( ang < 0 )
right_half ( planar = true ) children ( ) ;
else
left_half ( planar = true ) children ( ) ;
2021-01-17 09:36:43 +00:00
}
2021-10-31 04:36:51 +00:00
2021-01-17 09:36:43 +00:00
}
2021-11-01 22:14:31 +00:00
if ( caps ) {
2021-11-07 22:53:53 +00:00
bseg = select ( path , 0 , 1 ) ;
move ( bseg [ 0 ] )
rot ( from = BACK , to = bseg [ 0 ] - bseg [ 1 ] )
rotate_extrude ( angle = 180 )
right_half ( planar = true ) children ( ) ;
eseg = select ( path , - 2 , - 1 ) ;
move ( eseg [ 1 ] )
rot ( from = BACK , to = eseg [ 1 ] - eseg [ 0 ] )
rotate_extrude ( angle = 180 )
right_half ( planar = true ) children ( ) ;
2021-01-17 09:36:43 +00:00
}
}
2020-02-01 01:03:41 +00:00
2020-05-15 20:59:27 +00:00
// Module: cylindrical_extrude()
2023-03-30 00:19:52 +00:00
// Synopsis: Extrudes 2D children outwards around a cylinder.
// Topics: Mutators, Extrusion, Rotation
2023-03-31 02:28:29 +00:00
// See Also: heightfield(), cylindrical_heightfield(), cyl()
2020-05-15 20:59:27 +00:00
// Usage:
2022-04-08 23:37:46 +00:00
// cylindrical_extrude(ir|id=, or|od=, [size=], [convexity=], [spin=], [orient=]) 2D-CHILDREN;
2020-05-15 20:59:27 +00:00
// Description:
2022-04-08 23:37:46 +00:00
// Extrudes its 2D children outwards, curved around a cylindrical shape. Uses $fn/$fa/$fs to
// control the faceting of the extrusion.
2020-05-15 20:59:27 +00:00
// Arguments:
2022-04-08 23:37:46 +00:00
// ir = The inner radius to extrude from.
2020-05-15 20:59:27 +00:00
// or = The outer radius to extrude to.
2022-04-08 23:37:46 +00:00
// ---
2020-05-15 20:59:27 +00:00
// od = The outer diameter to extrude to.
// id = The inner diameter to extrude from.
// size = The [X,Y] size of the 2D children to extrude. Default: [1000,1000]
// convexity = The max number of times a line could pass though a wall. Default: 10
// spin = Amount in degrees to spin around cylindrical axis. Default: 0
// orient = The orientation of the cylinder to wrap around, given as a vector. Default: UP
// Example:
// cylindrical_extrude(or=50, ir=45)
// text(text="Hello World!", size=10, halign="center", valign="center");
// Example: Spin Around the Cylindrical Axis
// cylindrical_extrude(or=50, ir=45, spin=90)
// text(text="Hello World!", size=10, halign="center", valign="center");
// Example: Orient to the Y Axis.
// cylindrical_extrude(or=40, ir=35, orient=BACK)
// text(text="Hello World!", size=10, halign="center", valign="center");
2022-04-08 23:37:46 +00:00
module cylindrical_extrude ( ir , or , od , id , size = 1000 , convexity = 10 , spin = 0 , orient = UP ) {
2022-11-09 21:54:27 +00:00
check1 = assert ( is_num ( size ) || is_vector ( size , 2 ) ) ;
2020-05-30 02:04:34 +00:00
size = is_num ( size ) ? [ size , size ] : size ;
ir = get_radius ( r = ir , d = id ) ;
or = get_radius ( r = or , d = od ) ;
2022-11-09 21:54:27 +00:00
check2 = assert ( all_positive ( [ ir , or ] ) , "Must supply positive inner and outer radius or diameter" ) ;
2020-05-30 02:04:34 +00:00
index_r = or ;
circumf = 2 * PI * index_r ;
width = min ( size . x , circumf ) ;
2022-11-09 21:54:27 +00:00
check3 = assert ( width < = circumf , "Shape would more than completely wrap around." ) ;
2020-05-30 02:04:34 +00:00
sides = segs ( or ) ;
step = circumf / sides ;
steps = ceil ( width / step ) ;
rot ( from = UP , to = orient ) rot ( spin ) {
for ( i = [ 0 : 1 : steps - 2 ] ) {
x = ( i + 0.5 - steps / 2 ) * step ;
zrot ( 360 * x / circumf ) {
fwd ( or * cos ( 180 / sides ) ) {
xrot ( - 90 ) {
linear_extrude ( height = or - ir , scale = [ ir / or , 1 ] , center = false , convexity = convexity ) {
yflip ( )
intersection ( ) {
left ( x ) children ( ) ;
2021-12-14 00:31:14 +00:00
rect ( [ quantup ( step , pow ( 2 , - 15 ) ) , size . y ] ) ;
2020-05-30 02:04:34 +00:00
}
}
}
}
}
}
}
2020-05-15 20:59:27 +00:00
}
2021-09-17 21:07:18 +00:00
// Module: extrude_from_to()
2023-03-30 00:19:52 +00:00
// Extrudes 2D children between two points in 3D space.
// Topics: Extrusion, Mutators
2023-03-31 02:28:29 +00:00
// See Also: path_sweep(), path_extrude2d()
2022-04-08 23:37:46 +00:00
// Usage:
// extrude_from_to(pt1, pt2, [convexity=], [twist=], [scale=], [slices=]) 2D-CHILDREN;
2021-09-17 21:07:18 +00:00
// Description:
2022-04-08 23:37:46 +00:00
// Extrudes the 2D children linearly between the 3d points pt1 and pt2. The origin of the 2D children are placed on
// pt1 and pt2, and oriented perpendicular to the line between the points.
2021-09-17 21:07:18 +00:00
// Arguments:
// pt1 = starting point of extrusion.
// pt2 = ending point of extrusion.
2022-04-08 23:37:46 +00:00
// ---
2021-09-17 21:07:18 +00:00
// convexity = max number of times a line could intersect a wall of the 2D shape being extruded.
// twist = number of degrees to twist the 2D shape over the entire extrusion length.
// scale = scale multiplier for end of extrusion compared the start.
// slices = Number of slices along the extrusion to break the extrusion into. Useful for refining `twist` extrusions.
// Example(FlatSpin,VPD=200,VPT=[0,0,15]):
// extrude_from_to([0,0,0], [10,20,30], convexity=4, twist=360, scale=3.0, slices=40) {
// xcopies(3) circle(3, $fn=32);
// }
module extrude_from_to ( pt1 , pt2 , convexity , twist , scale , slices ) {
2022-11-09 21:54:27 +00:00
check =
assert ( is_vector ( pt1 ) , "First point must be a vector" )
assert ( is_vector ( pt2 ) , "Second point must be a vector" ) ;
2021-09-17 21:07:18 +00:00
pt1 = point3d ( pt1 ) ;
pt2 = point3d ( pt2 ) ;
rtp = xyz_to_spherical ( pt2 - pt1 ) ;
translate ( pt1 ) {
rotate ( [ 0 , rtp [ 2 ] , rtp [ 1 ] ] ) {
if ( rtp [ 0 ] > 0 ) {
linear_extrude ( height = rtp [ 0 ] , convexity = convexity , center = false , slices = slices , twist = twist , scale = scale ) {
children ( ) ;
}
}
}
}
}
// Module: path_extrude()
2023-03-30 00:19:52 +00:00
// Synopsis: Extrudes 2D children along a 3D path.
// Topics: Paths, Extrusion, Mutators
// See Also: path_sweep(), path_extrude2d()
2022-04-08 23:37:46 +00:00
// Usage: path_extrude(path, [convexity], [clipsize]) 2D-CHILDREN;
2021-09-17 21:07:18 +00:00
// Description:
2022-04-08 23:37:46 +00:00
// Extrudes 2D children along a 3D path. This may be slow and can have problems with twisting.
2021-09-17 21:07:18 +00:00
// Arguments:
2021-11-17 03:40:50 +00:00
// path = Array of points for the bezier path to extrude along.
// convexity = Maximum number of walls a ray can pass through.
// clipsize = Increase if artifacts are left. Default: 100
2021-09-17 21:07:18 +00:00
// Example(FlatSpin,VPD=600,VPT=[75,16,20]):
// path = [ [0, 0, 0], [33, 33, 33], [66, 33, 40], [100, 0, 0], [150,0,0] ];
// path_extrude(path) circle(r=10, $fn=6);
module path_extrude ( path , convexity = 10 , clipsize = 100 ) {
2022-02-26 14:54:21 +00:00
rotmats = cumprod ( [
for ( i = idx ( path , e = - 2 ) ) let (
vec1 = i = = 0 ? UP : unit ( path [ i ] - path [ i - 1 ] , UP ) ,
vec2 = unit ( path [ i + 1 ] - path [ i ] , UP )
) rot ( from = vec1 , to = vec2 )
] ) ;
// This adds a rotation midway between each item on the list
2022-03-14 01:40:55 +00:00
interp = rot_resample ( rotmats , n = 2 , method = "count" ) ;
2021-09-17 21:07:18 +00:00
epsilon = 0.0001 ; // Make segments ever so slightly too long so they overlap.
ptcount = len ( path ) ;
for ( i = [ 0 : 1 : ptcount - 2 ] ) {
pt1 = path [ i ] ;
pt2 = path [ i + 1 ] ;
2022-02-26 14:54:21 +00:00
dist = norm ( pt2 - pt1 ) ;
T = rotmats [ i ] ;
2021-09-17 21:07:18 +00:00
difference ( ) {
translate ( pt1 ) {
2022-02-26 14:54:21 +00:00
multmatrix ( T ) {
2021-09-17 21:07:18 +00:00
down ( clipsize / 2 / 2 ) {
if ( ( dist + clipsize / 2 ) > 0 ) {
linear_extrude ( height = dist + clipsize / 2 , convexity = convexity ) {
children ( ) ;
}
}
}
}
}
translate ( pt1 ) {
2022-02-26 14:54:21 +00:00
hq = ( i > 0 ) ? interp [ 2 * i - 1 ] : T ;
multmatrix ( hq ) down ( clipsize / 2 + epsilon ) cube ( clipsize , center = true ) ;
2021-09-17 21:07:18 +00:00
}
translate ( pt2 ) {
2022-02-26 14:54:21 +00:00
hq = ( i < ptcount - 2 ) ? interp [ 2 * i + 1 ] : T ;
multmatrix ( hq ) up ( clipsize / 2 + epsilon ) cube ( clipsize , center = true ) ;
2021-09-17 21:07:18 +00:00
}
}
}
}
2020-02-01 01:03:41 +00:00
//////////////////////////////////////////////////////////////////////
// Section: Offset Mutators
//////////////////////////////////////////////////////////////////////
2020-12-30 05:02:46 +00:00
// Module: minkowski_difference()
2023-03-30 00:19:52 +00:00
// Synopsis: Removes diff shapes from base shape surface.
// Topics: Mutators
2023-03-30 00:46:32 +00:00
// See Also: offset3d()
2020-02-01 01:03:41 +00:00
// Usage:
2022-04-08 23:37:46 +00:00
// minkowski_difference() { BASE; DIFF1; DIFF2; ... }
2020-02-01 01:03:41 +00:00
// Description:
2020-12-30 05:02:46 +00:00
// Takes a 3D base shape and one or more 3D diff shapes, carves out the diff shapes from the
// surface of the base shape, in a way complementary to how `minkowski()` unions shapes to the
// surface of its base shape.
2020-02-01 01:03:41 +00:00
// Arguments:
2020-12-30 05:02:46 +00:00
// planar = If true, performs minkowski difference in 2D. Default: false (3D)
// Example:
// minkowski_difference() {
// union() {
// cube([120,70,70], center=true);
// cube([70,120,70], center=true);
// cube([70,70,120], center=true);
// }
// sphere(r=10);
// }
module minkowski_difference ( planar = false ) {
difference ( ) {
bounding_box ( excess = 0 , planar = planar ) children ( 0 ) ;
render ( convexity = 20 ) {
2020-05-30 02:04:34 +00:00
minkowski ( ) {
difference ( ) {
2020-12-30 05:02:46 +00:00
bounding_box ( excess = 1 , planar = planar ) children ( 0 ) ;
children ( 0 ) ;
2020-05-30 02:04:34 +00:00
}
2020-12-30 05:02:46 +00:00
for ( i = [ 1 : 1 : $children - 1 ] ) children ( i ) ;
2020-05-30 02:04:34 +00:00
}
}
}
2020-02-01 01:03:41 +00:00
}
2020-12-30 05:02:46 +00:00
// Module: offset3d()
2023-03-30 00:19:52 +00:00
// Synopsis: Expands or contracts the surface of a 3D object.
// Topics: Mutators
2023-03-31 02:28:29 +00:00
// See Also: minkowski_difference(), round3d()
2020-09-14 04:50:39 +00:00
// Usage:
2022-04-08 23:37:46 +00:00
// offset3d(r, [size], [convexity]) CHILDREN;
2020-09-14 04:50:39 +00:00
// Description:
2020-12-30 05:02:46 +00:00
// Expands or contracts the surface of a 3D object by a given amount. This is very, very slow.
// No really, this is unbearably slow. It uses `minkowski()`. Use this as a last resort.
// This is so slow that no example images will be rendered.
// Arguments:
2022-04-08 23:37:46 +00:00
// r = Radius to expand object by. Negative numbers contract the object.
2020-12-30 05:02:46 +00:00
// size = Maximum size of object to be contracted, given as a scalar. Default: 100
// convexity = Max number of times a line could intersect the walls of the object. Default: 10
2022-04-08 23:37:46 +00:00
module offset3d ( r , size = 100 , convexity = 10 ) {
2020-12-30 05:02:46 +00:00
n = quant ( max ( 8 , segs ( abs ( r ) ) ) , 4 ) ;
if ( r = = 0 ) {
children ( ) ;
} else if ( r > 0 ) {
render ( convexity = convexity )
minkowski ( ) {
children ( ) ;
sphere ( r , $fn = n ) ;
}
} else {
size2 = size * [ 1 , 1 , 1 ] ;
size1 = size2 * 1.02 ;
render ( convexity = convexity )
difference ( ) {
cube ( size2 , center = true ) ;
2020-09-14 04:50:39 +00:00
minkowski ( ) {
difference ( ) {
2020-12-30 05:02:46 +00:00
cube ( size1 , center = true ) ;
children ( ) ;
2020-09-14 04:50:39 +00:00
}
2020-12-30 05:02:46 +00:00
sphere ( - r , $fn = n ) ;
2020-09-14 04:50:39 +00:00
}
}
}
}
2020-12-30 05:02:46 +00:00
// Module: round3d()
2023-03-30 00:19:52 +00:00
// Synopsis: Rounds arbitrary 3d objects.
// Topics: Rounding, Mutators
2023-03-31 02:28:29 +00:00
// See Also: offset3d(), minkowski_difference()
2020-12-30 05:02:46 +00:00
// Usage:
2022-04-08 23:37:46 +00:00
// round3d(r) CHILDREN;
// round3d(or) CHILDREN;
// round3d(ir) CHILDREN;
// round3d(or, ir) CHILDREN;
2020-12-30 05:02:46 +00:00
// Description:
// Rounds arbitrary 3D 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 3D object must not have
// any parts narrower than twice the `or` radius. Such parts will disappear. This is an *extremely*
// slow operation. I cannot emphasize enough just how slow it is. It uses `minkowski()` multiple times.
// Use this as a last resort. This is so slow that no example images will be rendered.
// 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 only inside (concave) corners to. Use instead of `r`.
module round3d ( r , or , ir , size = 100 )
{
or = get_radius ( r1 = or , r = r , dflt = 0 ) ;
ir = get_radius ( r1 = ir , r = r , dflt = 0 ) ;
offset3d ( or , size = size )
offset3d ( - ir - or , size = size )
offset3d ( ir , size = size )
children ( ) ;
}
2020-09-14 04:50:39 +00:00
2020-05-30 02:04:34 +00:00
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap