2019-07-10 04:54:28 +00:00
//////////////////////////////////////////////////////////////////////
// LibFile: partitions.scad
2021-12-17 03:31:49 +00:00
// Cut objects with a plane, or partition them into interlocking pieces for easy printing of large objects.
2021-01-05 09:20:01 +00:00
// Includes:
2019-07-10 04:54:28 +00:00
// include <BOSL2/std.scad>
2022-03-17 22:38:20 +00:00
// FileGroup: Basic Modeling
2021-12-17 03:31:49 +00:00
// FileSummary: Cut objects with a plane or partition them into interlocking pieces.
// FileFootnotes: STD=Included in std.scad
2019-07-10 04:54:28 +00:00
//////////////////////////////////////////////////////////////////////
2021-12-17 03:31:49 +00:00
// Section: Planar Cutting
// Function&Module: half_of()
2023-04-04 00:58:12 +00:00
// Synopsis: Masks half of an object at a cut plane.
2023-04-14 22:53:35 +00:00
// SynTags: Geom, VNF, Path, Region
2023-04-04 00:58:12 +00:00
// Topics: Partitions, Masking
2023-05-30 04:48:48 +00:00
// See Also: back_half(), front_half(), left_half(), right_half(), top_half(), bottom_half(), intersection()
2021-12-17 03:31:49 +00:00
//
// Usage: as module
2022-03-31 22:36:09 +00:00
// half_of(v, [cp], [s], [planar]) CHILDREN;
2021-12-17 03:31:49 +00:00
// Usage: as function
// result = half_of(p,v,[cp]);
//
// Description:
2023-05-30 04:48:48 +00:00
// Slices an object at a cut plane, and masks away everything that is on one side. The v parameter
// is either a plane specification or a normal vector. The `s` parameter is needed for the module
// version to control the size of the masking cube. If `s` is too large then the preview display
// will flip around and display the wrong half, but if it is too small it won't fully mask your
// model. When called as a function, you must supply a vnf, path or region in p. If planar is set
// to true for the module version the operation is performed in 2D and UP and DOWN are treated as
// equivalent to BACK and FWD respectively.
2021-12-17 03:31:49 +00:00
//
// Arguments:
// p = path, region or VNF to slice. (Function version)
// v = Normal of plane to slice at. Keeps everything on the side the normal points to. Default: [0,0,1] (UP)
// 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. Default: [0,0,0]
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100
// planar = If true, perform a 2D operation. When planar, a `v` of `UP` or `DOWN` becomes equivalent of `BACK` and `FWD` respectively. (Module version). Default: false.
//
// Examples:
// half_of(DOWN+BACK, cp=[0,-10,0]) cylinder(h=40, r1=10, r2=0, center=false);
// half_of(DOWN+LEFT, s=200) sphere(d=150);
// Example(2D):
// half_of([1,1], planar=true) circle(d=50);
module half_of ( v = UP , cp , s = 100 , planar = false )
{
2022-03-31 22:36:09 +00:00
req_children ( $children ) ;
2021-12-17 03:31:49 +00:00
cp = is_vector ( v , 4 ) ? assert ( cp = = undef , "Don't use cp with plane definition." ) plane_normal ( v ) * v [ 3 ] :
is_vector ( cp ) ? cp :
is_num ( cp ) ? cp * unit ( v ) :
[ 0 , 0 , 0 ] ;
v = is_vector ( v , 4 ) ? plane_normal ( v ) : v ;
if ( cp ! = [ 0 , 0 , 0 ] ) {
translate ( cp ) half_of ( v = v , s = s , planar = planar ) translate ( - cp ) children ( ) ;
} else if ( planar ) {
v = ( v = = UP ) ? BACK : ( v = = DOWN ) ? FWD : v ;
ang = atan2 ( v . y , v . x ) ;
difference ( ) {
children ( ) ;
rotate ( ang + 90 ) {
back ( s / 2 ) square ( s , center = true ) ;
}
}
} else {
difference ( ) {
children ( ) ;
rot ( from = UP , to = - v ) {
up ( s / 2 ) cube ( s , center = true ) ;
}
}
}
}
function half_of ( p , v = UP , cp ) =
is_vnf ( p ) ?
assert ( is_vector ( v ) && ( len ( v ) = = 3 || len ( v ) = = 4 ) , str ( "Must give 3-vector or plane specification" , v ) )
assert ( select ( v , 0 , 2 ) ! = [ 0 , 0 , 0 ] , "vector v must be nonzero" )
let (
plane = is_vector ( v , 4 ) ? assert ( cp = = undef , "Don't use cp with plane definition." ) v
: is_undef ( cp ) ? [ each v , 0 ]
: is_num ( cp ) ? [ each v , cp * ( v * v ) / norm ( v ) ]
: assert ( is_vector ( cp , 3 ) , "Centerpoint must be a 3-vector" )
[ each v , cp * v ]
)
vnf_halfspace ( plane , p )
: is_path ( p ) || is_region ( p ) ?
let (
v = ( v = = UP ) ? BACK : ( v = = DOWN ) ? FWD : v ,
cp = is_undef ( cp ) ? [ 0 , 0 ]
: is_num ( cp ) ? v * cp
: assert ( is_vector ( cp , 2 ) || ( is_vector ( cp , 3 ) && cp . z = = 0 ) , "Centerpoint must be 2-vector" )
cp
)
assert ( is_vector ( v , 2 ) || ( is_vector ( v , 3 ) && v . z = = 0 ) , "Must give 2-vector" )
assert ( ! all_zero ( v ) , "Vector v must be nonzero" )
let (
2024-01-02 02:49:57 +00:00
v = unit ( v ) ,
bounds = pointlist_bounds ( is_region ( p ) ? flatten ( p ) : p ) ,
L = 2 * max ( norm ( bounds [ 0 ] - cp ) , norm ( bounds [ 1 ] - cp ) ) ,
u = [ - v . y , v . x ] ,
2021-12-17 03:31:49 +00:00
box = [ cp + u * L , cp + ( v + u ) * L , cp + ( v - u ) * L , cp - u * L ]
)
intersection ( box , p )
: assert ( false , "Input must be a region, path or VNF" ) ;
/* This code cut 3d paths but leaves behind connecting line segments
is_path ( p ) ?
//assert(len(p[0]) == d, str("path must have dimension ", d))
let ( z = [ for ( x = p ) ( x - cp ) * v ] )
[ for ( i = [ 0 : len ( p ) - 1 ] ) each concat ( z [ i ] >= 0 ? [ p [ i ] ] : [ ] ,
// we assume a closed path here;
// to make this correct for an open path,
// just replace this by [] when i==len(p)-1:
let ( j = ( i + 1 ) % len ( p ) )
// the remaining path may have flattened sections, but this cannot
// create self-intersection or whiskers:
z [ i ] * z [ j ] >= 0 ? [ ] : [ ( z [ j ] * p [ i ] - z [ i ] * p [ j ] ) / ( z [ j ] - z [ i ] ) ] ) ]
:
* /
// Function&Module: left_half()
2023-04-04 00:58:12 +00:00
// Synopsis: Masks the right half of an object along the Y-Z plane, leaving the left half.
2023-04-14 22:53:35 +00:00
// SynTags: Geom, VNF, Path, Region
2023-04-04 00:58:12 +00:00
// Topics: Partitions, Masking
2023-05-30 04:48:48 +00:00
// See Also: back_half(), front_half(), right_half(), top_half(), bottom_half(), half_of(), intersection()
2021-12-17 03:31:49 +00:00
//
// Usage: as module
2022-03-31 22:36:09 +00:00
// left_half([s], [x]) CHILDREN;
// left_half(planar=true, [s], [x]) CHILDREN;
2021-12-17 03:31:49 +00:00
// Usage: as function
// result = left_half(p, [x]);
//
// Description:
// Slices an object at a vertical Y-Z cut plane, and masks away everything that is right of it.
2023-05-30 04:48:48 +00:00
// The `s` parameter is needed for the module version to control the size of the masking cube.
// If `s` is too large then the preview display will flip around and display the wrong half,
// but if it is too small it won't fully mask your model.
2021-12-17 03:31:49 +00:00
//
// Arguments:
// p = VNF, region or path to slice (function version)
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100
// x = The X coordinate of the cut-plane. Default: 0
// planar = If true, perform a 2D operation. (Module version) Default: false.
// Examples:
// left_half() sphere(r=20);
// left_half(x=-8) sphere(r=20);
// Example(2D):
// left_half(planar=true) circle(r=20);
module left_half ( s = 100 , x = 0 , planar = false )
{
2022-03-31 22:36:09 +00:00
req_children ( $children ) ;
2021-12-17 03:31:49 +00:00
dir = LEFT ;
difference ( ) {
children ( ) ;
translate ( [ x , 0 , 0 ] - dir * s / 2 ) {
if ( planar ) {
square ( s , center = true ) ;
} else {
cube ( s , center = true ) ;
}
}
}
}
function left_half ( p , x = 0 ) = half_of ( p , LEFT , [ x , 0 , 0 ] ) ;
// Function&Module: right_half()
2023-04-14 22:53:35 +00:00
// SynTags: Geom, VNF, Path, Region
2023-04-04 00:58:12 +00:00
// Synopsis: Masks the left half of an object along the Y-Z plane, leaving the right half.
// Topics: Partitions, Masking
2023-05-30 04:48:48 +00:00
// See Also: back_half(), front_half(), left_half(), top_half(), bottom_half(), half_of(), intersection()
2021-12-17 03:31:49 +00:00
//
// Usage: as module
2022-03-31 22:36:09 +00:00
// right_half([s=], [x=]) CHILDREN;
// right_half(planar=true, [s=], [x=]) CHILDREN;
2021-12-17 03:31:49 +00:00
// Usage: as function
2022-03-31 22:36:09 +00:00
// result = right_half(p, [x=]);
2021-12-17 03:31:49 +00:00
//
// Description:
// Slices an object at a vertical Y-Z cut plane, and masks away everything that is left of it.
2023-05-30 04:48:48 +00:00
// The `s` parameter is needed for the module version to control the size of the masking cube.
// If `s` is too large then the preview display will flip around and display the wrong half,
// but if it is too small it won't fully mask your model.
2021-12-17 03:31:49 +00:00
// Arguments:
// p = VNF, region or path to slice (function version)
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100
// x = The X coordinate of the cut-plane. Default: 0
// planar = If true, perform a 2D operation. (Module version) Default: false.
// Examples(FlatSpin,VPD=175):
// right_half() sphere(r=20);
// right_half(x=-5) sphere(r=20);
// Example(2D):
// right_half(planar=true) circle(r=20);
module right_half ( s = 100 , x = 0 , planar = false )
{
dir = RIGHT ;
difference ( ) {
children ( ) ;
translate ( [ x , 0 , 0 ] - dir * s / 2 ) {
if ( planar ) {
square ( s , center = true ) ;
} else {
cube ( s , center = true ) ;
}
}
}
}
function right_half ( p , x = 0 ) = half_of ( p , RIGHT , [ x , 0 , 0 ] ) ;
// Function&Module: front_half()
2023-04-04 00:58:12 +00:00
// Synopsis: Masks the back half of an object along the X-Z plane, leaving the front half.
2023-04-14 22:53:35 +00:00
// SynTags: Geom, VNF, Path, Region
2023-04-04 00:58:12 +00:00
// Topics: Partitions, Masking
2023-05-30 04:48:48 +00:00
// See Also: back_half(), left_half(), right_half(), top_half(), bottom_half(), half_of(), intersection()
2021-12-17 03:31:49 +00:00
//
// Usage:
2022-03-31 22:36:09 +00:00
// front_half([s], [y]) CHILDREN;
// front_half(planar=true, [s], [y]) CHILDREN;
2021-12-17 03:31:49 +00:00
// Usage: as function
// result = front_half(p, [y]);
//
// Description:
// Slices an object at a vertical X-Z cut plane, and masks away everything that is behind it.
2023-05-30 04:48:48 +00:00
// The `s` parameter is needed for the module version to control the size of the masking cube.
// If `s` is too large then the preview display will flip around and display the wrong half,
// but if it is too small it won't fully mask your model.
2021-12-17 03:31:49 +00:00
// Arguments:
// p = VNF, region or path to slice (function version)
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100
// y = The Y coordinate of the cut-plane. Default: 0
// planar = If true, perform a 2D operation. (Module version) Default: false.
// Examples(FlatSpin,VPD=175):
// front_half() sphere(r=20);
// front_half(y=5) sphere(r=20);
// Example(2D):
// front_half(planar=true) circle(r=20);
module front_half ( s = 100 , y = 0 , planar = false )
{
2022-03-31 22:36:09 +00:00
req_children ( $children ) ;
2021-12-17 03:31:49 +00:00
dir = FWD ;
difference ( ) {
children ( ) ;
translate ( [ 0 , y , 0 ] - dir * s / 2 ) {
if ( planar ) {
square ( s , center = true ) ;
} else {
cube ( s , center = true ) ;
}
}
}
}
function front_half ( p , y = 0 ) = half_of ( p , FRONT , [ 0 , y , 0 ] ) ;
// Function&Module: back_half()
2023-04-04 00:58:12 +00:00
// Synopsis: Masks the front half of an object along the X-Z plane, leaving the back half.
2023-04-14 22:53:35 +00:00
// SynTags: Geom, VNF, Path, Region
2023-04-04 00:58:12 +00:00
// Topics: Partitions, Masking
2023-05-30 04:48:48 +00:00
// See Also: front_half(), left_half(), right_half(), top_half(), bottom_half(), half_of(), intersection()
2021-12-17 03:31:49 +00:00
//
// Usage:
2022-03-31 22:36:09 +00:00
// back_half([s], [y]) CHILDREN;
// back_half(planar=true, [s], [y]) CHILDREN;
2021-12-17 03:31:49 +00:00
// Usage: as function
// result = back_half(p, [y]);
//
// Description:
// Slices an object at a vertical X-Z cut plane, and masks away everything that is in front of it.
2023-05-30 04:48:48 +00:00
// The `s` parameter is needed for the module version to control the size of the masking cube.
// If `s` is too large then the preview display will flip around and display the wrong half,
// but if it is too small it won't fully mask your model.
2021-12-17 03:31:49 +00:00
// Arguments:
// p = VNF, region or path to slice (function version)
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100
// y = The Y coordinate of the cut-plane. Default: 0
// planar = If true, perform a 2D operation. (Module version) Default: false.
// Examples:
// back_half() sphere(r=20);
// back_half(y=8) sphere(r=20);
// Example(2D):
// back_half(planar=true) circle(r=20);
module back_half ( s = 100 , y = 0 , planar = false )
{
2022-03-31 22:36:09 +00:00
req_children ( $children ) ;
2021-12-17 03:31:49 +00:00
dir = BACK ;
difference ( ) {
children ( ) ;
translate ( [ 0 , y , 0 ] - dir * s / 2 ) {
if ( planar ) {
square ( s , center = true ) ;
} else {
cube ( s , center = true ) ;
}
}
}
}
function back_half ( p , y = 0 ) = half_of ( p , BACK , [ 0 , y , 0 ] ) ;
// Function&Module: bottom_half()
2023-04-04 00:58:12 +00:00
// Synopsis: Masks the top half of an object along the X-Y plane, leaving the bottom half.
2023-04-14 22:53:35 +00:00
// SynTags: Geom, VNF, Path, Region
2023-04-04 00:58:12 +00:00
// Topics: Partitions, Masking
2023-05-30 04:48:48 +00:00
// See Also: back_half(), front_half(), left_half(), right_half(), top_half(), half_of(), intersection()
2021-12-17 03:31:49 +00:00
//
// Usage:
2022-03-31 22:36:09 +00:00
// bottom_half([s], [z]) CHILDREN;
2021-12-17 03:31:49 +00:00
// Usage: as function
// result = bottom_half(p, [z]);
//
// Description:
// Slices an object at a horizontal X-Y cut plane, and masks away everything that is above it.
2023-05-30 04:48:48 +00:00
// The `s` parameter is needed for the module version to control the size of the masking cube.
// If `s` is too large then the preview display will flip around and display the wrong half,
// but if it is too small it won't fully mask your model.
2021-12-17 03:31:49 +00:00
// Arguments:
// p = VNF, region or path to slice (function version)
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100
// z = The Z coordinate of the cut-plane. Default: 0
// Examples:
// bottom_half() sphere(r=20);
// bottom_half(z=-10) sphere(r=20);
module bottom_half ( s = 100 , z = 0 )
{
2022-03-31 22:36:09 +00:00
req_children ( $children ) ;
2021-12-17 03:31:49 +00:00
dir = DOWN ;
difference ( ) {
children ( ) ;
translate ( [ 0 , 0 , z ] - dir * s / 2 ) {
cube ( s , center = true ) ;
}
}
}
function bottom_half ( p , z = 0 ) = half_of ( p , BOTTOM , [ 0 , 0 , z ] ) ;
// Function&Module: top_half()
2023-04-04 00:58:12 +00:00
// Synopsis: Masks the bottom half of an object along the X-Y plane, leaving the top half.
2023-04-14 22:53:35 +00:00
// SynTags: Geom, VNF, Path, Region
2023-04-04 00:58:12 +00:00
// Topics: Partitions, Masking
2023-05-30 04:48:48 +00:00
// See Also: back_half(), front_half(), left_half(), right_half(), bottom_half(), half_of(), intersection()
2021-12-17 03:31:49 +00:00
//
2022-03-31 22:36:09 +00:00
// Usage: as module
// top_half([s], [z]) CHILDREN;
// Usage: as function
2021-12-17 03:31:49 +00:00
// result = top_half(p, [z]);
//
// Description:
// Slices an object at a horizontal X-Y cut plane, and masks away everything that is below it.
2023-05-30 04:48:48 +00:00
// The `s` parameter is needed for the module version to control the size of the masking cube.
// If `s` is too large then the preview display will flip around and display the wrong half,
// but if it is too small it won't fully mask your model.
2021-12-17 03:31:49 +00:00
// Arguments:
// p = VNF, region or path to slice (function version)
// s = Mask size to use. Use a number larger than twice your object's largest axis. If you make this too large, OpenSCAD's preview rendering may display the wrong half. (Module version) Default: 100
// z = The Z coordinate of the cut-plane. Default: 0
// Examples(Spin,VPD=175):
// top_half() sphere(r=20);
// top_half(z=5) sphere(r=20);
module top_half ( s = 100 , z = 0 )
{
2022-03-31 22:36:09 +00:00
req_children ( $children ) ;
2021-12-17 03:31:49 +00:00
dir = UP ;
difference ( ) {
children ( ) ;
translate ( [ 0 , 0 , z ] - dir * s / 2 ) {
cube ( s , center = true ) ;
}
}
}
function top_half ( p , z = 0 ) = half_of ( p , UP , [ 0 , 0 , z ] ) ;
// Section: Partioning into Interlocking Pieces
2019-07-10 04:54:28 +00:00
2021-06-29 01:04:27 +00:00
function _partition_subpath ( type ) =
type = = "flat" ? [ [ 0 , 0 ] , [ 1 , 0 ] ] :
type = = "sawtooth" ? [ [ 0 , - 0.5 ] , [ 0.5 , 0.5 ] , [ 1 , - 0.5 ] ] :
type = = "sinewave" ? [ for ( a = [ 0 : 5 : 360 ] ) [ a / 360 , sin ( a ) / 2 ] ] :
type = = "comb" ? let ( dx = 0.5 * sin ( 2 ) ) [ [ 0 , 0 ] , [ 0 + dx , 0.5 ] , [ 0.5 - dx , 0.5 ] , [ 0.5 + dx , - 0.5 ] , [ 1 - dx , - 0.5 ] , [ 1 , 0 ] ] :
type = = "finger" ? let ( dx = 0.5 * sin ( 20 ) ) [ [ 0 , 0 ] , [ 0 + dx , 0.5 ] , [ 0.5 - dx , 0.5 ] , [ 0.5 + dx , - 0.5 ] , [ 1 - dx , - 0.5 ] , [ 1 , 0 ] ] :
type = = "dovetail" ? [ [ 0 , - 0.5 ] , [ 0.3 , - 0.5 ] , [ 0.2 , 0.5 ] , [ 0.8 , 0.5 ] , [ 0.7 , - 0.5 ] , [ 1 , - 0.5 ] ] :
type = = "hammerhead" ? [ [ 0 , - 0.5 ] , [ 0.35 , - 0.5 ] , [ 0.35 , 0 ] , [ 0.15 , 0 ] , [ 0.15 , 0.5 ] , [ 0.85 , 0.5 ] , [ 0.85 , 0 ] , [ 0.65 , 0 ] , [ 0.65 , - 0.5 ] , [ 1 , - 0.5 ] ] :
type = = "jigsaw" ? concat (
arc ( r = 5 / 16 , cp = [ 0 , - 3 / 16 ] , start = 270 , angle = 125 ) ,
arc ( r = 5 / 16 , cp = [ 1 / 2 , 3 / 16 ] , start = 215 , angle = - 250 ) ,
arc ( r = 5 / 16 , cp = [ 1 , - 3 / 16 ] , start = 145 , angle = 125 )
) :
assert ( false , str ( "Unsupported cutpath type: " , type ) ) ;
2019-07-10 04:54:28 +00:00
function _partition_cutpath ( l , h , cutsize , cutpath , gap ) =
2020-05-30 02:04:34 +00:00
let (
2020-08-03 06:23:50 +00:00
check = assert ( is_finite ( l ) )
assert ( is_finite ( h ) )
assert ( is_finite ( gap ) )
assert ( is_finite ( cutsize ) || is_vector ( cutsize , 2 ) )
assert ( is_string ( cutpath ) || is_path ( cutpath , 2 ) ) ,
2020-05-30 02:04:34 +00:00
cutsize = is_vector ( cutsize ) ? cutsize : [ cutsize * 2 , cutsize ] ,
2021-06-29 01:04:27 +00:00
cutpath = is_path ( cutpath ) ? cutpath :
_partition_subpath ( cutpath ) ,
2020-05-30 02:04:34 +00:00
reps = ceil ( l / ( cutsize . x + gap ) ) ,
cplen = ( cutsize . x + gap ) * reps ,
path = deduplicate ( concat (
[ [ - l / 2 , cutpath [ 0 ] . y * cutsize . y ] ] ,
2021-06-15 03:28:49 +00:00
[ for ( i = [ 0 : 1 : reps - 1 ] , pt = cutpath ) v_mul ( pt , cutsize ) + [ i * ( cutsize . x + gap ) + gap / 2 - cplen / 2 , 0 ] ] ,
2020-05-30 02:04:34 +00:00
[ [ l / 2 , cutpath [ len ( cutpath ) - 1 ] . y * cutsize . y ] ]
2021-06-29 01:04:27 +00:00
) ) ,
stidxs = [ for ( i = idx ( path ) ) if ( path [ i ] . x < - l / 2 ) i ] ,
enidxs = [ for ( i = idx ( path ) ) if ( path [ i ] . x > + l / 2 ) i ] ,
stidx = stidxs ? last ( stidxs ) : 0 ,
enidx = enidxs ? enidxs [ 0 ] : - 1 ,
trunc = select ( path , stidx , enidx )
) trunc ;
2019-07-10 04:54:28 +00:00
// Module: partition_mask()
2023-04-04 00:58:12 +00:00
// Synopsis: Creates a mask to remove half an object with the remaining half suitable for reassembly.
2023-04-14 22:53:35 +00:00
// SynTags: Geom
2023-04-04 00:58:12 +00:00
// Topics: Partitions, Masking, Paths
// See Also: partition_cut_mask(), partition()
2019-07-10 04:54:28 +00:00
// Usage:
2022-11-10 03:22:30 +00:00
// partition_mask(l, w, h, [cutsize], [cutpath], [gap], [inverse], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
2019-07-10 04:54:28 +00:00
// Description:
2023-05-30 04:48:48 +00:00
// Creates a mask that you can use to difference or intersect with an object to remove half of it,
// leaving behind a side designed to allow assembly of the sub-parts.
2019-07-10 04:54:28 +00:00
// Arguments:
2024-08-29 23:29:43 +00:00
// l = The length of the cut axis.
2019-07-10 04:54:28 +00:00
// w = The width of the part to be masked, back from the cut plane.
// h = The height of the part to be masked.
// cutsize = The width of the cut pattern to be used.
// cutpath = The cutpath to use. Standard named paths are "flat", "sawtooth", "sinewave", "comb", "finger", "dovetail", "hammerhead", and "jigsaw". Alternatively, you can give a cutpath as a 2D path, where X is between 0 and 1, and Y is between -0.5 and 0.5.
// gap = Empty gaps between cutpath iterations. Default: 0
// inverse = If true, create a cutpath that is meant to mate to a non-inverted cutpath.
2021-11-20 03:33:16 +00:00
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP`
2021-06-29 01:04:27 +00:00
// $slop = The amount to shrink the mask by, to correct for printer-specific fitting.
2019-07-10 04:54:28 +00:00
// Examples:
// partition_mask(w=50, gap=0, cutpath="jigsaw");
// partition_mask(w=50, gap=30, cutpath="jigsaw");
// partition_mask(w=50, gap=30, cutpath="jigsaw", inverse=true);
// partition_mask(w=50, gap=30, cutsize=15, cutpath="jigsaw");
// partition_mask(w=50, cutsize=[20,20], gap=30, cutpath="jigsaw");
// Examples(2D):
2019-07-10 05:02:55 +00:00
// partition_mask(w=20, cutpath="sawtooth");
// partition_mask(w=20, cutpath="sinewave");
// partition_mask(w=20, cutpath="comb");
// partition_mask(w=20, cutpath="finger");
// partition_mask(w=20, cutpath="dovetail");
// partition_mask(w=20, cutpath="hammerhead");
// partition_mask(w=20, cutpath="jigsaw");
2021-06-29 01:04:27 +00:00
module partition_mask ( l = 100 , w = 100 , h = 100 , cutsize = 10 , cutpath = "jigsaw" , gap = 0 , inverse = false , anchor = CENTER , spin = 0 , orient = UP )
2019-07-10 04:54:28 +00:00
{
2020-08-03 06:23:50 +00:00
cutsize = is_vector ( cutsize ) ? point2d ( cutsize ) : [ cutsize * 2 , cutsize ] ;
2020-05-30 02:04:34 +00:00
path = _partition_cutpath ( l , h , cutsize , cutpath , gap ) ;
2021-06-29 01:04:27 +00:00
midpath = select ( path , 1 , - 2 ) ;
2022-04-12 02:18:52 +00:00
sizepath = concat ( [ path [ 0 ] + [ - get_slop ( ) , 0 ] ] , midpath , [ last ( path ) + [ get_slop ( ) , 0 ] ] , [ [ + ( l / 2 + get_slop ( ) ) , ( w + get_slop ( ) ) * ( inverse ? - 1 : 1 ) ] , [ - ( l / 2 + get_slop ( ) ) , ( w + get_slop ( ) ) * ( inverse ? - 1 : 1 ) ] ] ) ;
2021-06-29 01:04:27 +00:00
bnds = pointlist_bounds ( sizepath ) ;
fullpath = concat ( path , [ [ last ( path ) . x , w * ( inverse ? - 1 : 1 ) ] , [ path [ 0 ] . x , w * ( inverse ? - 1 : 1 ) ] ] ) ;
attachable ( anchor , spin , orient , size = point3d ( bnds [ 1 ] - bnds [ 0 ] , h ) ) {
linear_extrude ( height = h , center = true , convexity = 10 ) {
intersection ( ) {
2022-04-12 02:18:52 +00:00
offset ( delta = - get_slop ( ) ) polygon ( fullpath ) ;
2021-06-29 01:04:27 +00:00
square ( [ l , w * 2 ] , center = true ) ;
2020-05-30 02:04:34 +00:00
}
}
2021-06-29 01:04:27 +00:00
children ( ) ;
2020-05-30 02:04:34 +00:00
}
2019-07-10 04:54:28 +00:00
}
// Module: partition_cut_mask()
2023-04-04 00:58:12 +00:00
// Synopsis: Creates a mask to cut an object into two subparts that can be reassembled.
2023-04-14 22:53:35 +00:00
// SynTags: Geom
2023-04-04 00:58:12 +00:00
// Topics: Partitions, Masking, Paths
// See Also: partition_mask(), partition()
2019-07-10 04:54:28 +00:00
// Usage:
2024-08-29 23:29:43 +00:00
// partition_cut_mask(l, [cutsize], [cutpath], [gap], [inverse], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
2019-07-10 04:54:28 +00:00
// Description:
// Creates a mask that you can use to difference with an object to cut it into two sub-parts that can be assembled.
2021-09-17 02:03:13 +00:00
// The `$slop` value is important to get the proper fit and should probably be smaller than 0.2. The examples below
// use larger values to make the mask easier to see.
2019-07-10 04:54:28 +00:00
// Arguments:
// l = The length of the cut axis.
// h = The height of the part to be masked.
2024-08-29 23:29:43 +00:00
// cutsize = The width of the cut pattern to be used. Default: 10
2021-06-29 01:04:27 +00:00
// cutpath = The cutpath to use. Standard named paths are "flat", "sawtooth", "sinewave", "comb", "finger", "dovetail", "hammerhead", and "jigsaw". Alternatively, you can give a cutpath as a 2D path, where X is between 0 and 1, and Y is between -0.5 and 0.5. Default: "jigsaw"
2019-07-10 04:54:28 +00:00
// gap = Empty gaps between cutpath iterations. Default: 0
2021-11-20 03:33:16 +00:00
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP`
2022-11-10 03:22:30 +00:00
// $slop = The width of the cut mask, to correct for printer-specific fitting.
2019-07-10 05:02:55 +00:00
// Examples:
2019-07-10 04:54:28 +00:00
// partition_cut_mask(gap=0, cutpath="dovetail");
// partition_cut_mask(gap=30, cutpath="dovetail");
// partition_cut_mask(gap=30, cutsize=15, cutpath="dovetail");
// partition_cut_mask(gap=30, cutsize=[20,20], cutpath="dovetail");
2019-07-10 05:30:27 +00:00
// Examples(2DMed):
2021-09-17 02:03:13 +00:00
// partition_cut_mask(cutpath="sawtooth",$slop=0.5);
// partition_cut_mask(cutpath="sinewave",$slop=0.5);
// partition_cut_mask(cutpath="comb",$slop=0.5);
// partition_cut_mask(cutpath="finger",$slop=0.5);
// partition_cut_mask(cutpath="dovetail",$slop=1);
// partition_cut_mask(cutpath="hammerhead",$slop=1);
// partition_cut_mask(cutpath="jigsaw",$slop=0.5);
2021-06-29 01:04:27 +00:00
module partition_cut_mask ( l = 100 , h = 100 , cutsize = 10 , cutpath = "jigsaw" , gap = 0 , anchor = CENTER , spin = 0 , orient = UP )
2019-07-10 04:54:28 +00:00
{
2020-05-30 02:04:34 +00:00
cutsize = is_vector ( cutsize ) ? cutsize : [ cutsize * 2 , cutsize ] ;
path = _partition_cutpath ( l , h , cutsize , cutpath , gap ) ;
2021-06-29 01:04:27 +00:00
attachable ( anchor , spin , orient , size = [ l , cutsize . y , h ] ) {
linear_extrude ( height = h , center = true , convexity = 10 ) {
2022-04-12 02:18:52 +00:00
stroke ( path , width = max ( 0.1 , get_slop ( ) * 2 ) ) ;
2020-05-30 02:04:34 +00:00
}
2021-06-29 01:07:29 +00:00
children ( ) ;
2020-05-30 02:04:34 +00:00
}
2019-07-10 04:54:28 +00:00
}
// Module: partition()
2023-04-14 22:53:35 +00:00
// Synopsis: Cuts an object in two with matched joining edges, then separates the parts.
// SynTags: Geom, VNF, Path, Region
2023-04-04 00:58:12 +00:00
// Topics: Partitions, Masking, Paths
// See Also: partition_cut_mask(), partition_mask()
2019-07-10 04:54:28 +00:00
// Usage:
2022-11-10 03:22:30 +00:00
// partition(size, [spread], [cutsize], [cutpath], [gap], [spin], [$slop=]) CHILDREN;
2019-07-10 04:54:28 +00:00
// Description:
// Partitions an object into two parts, spread apart a small distance, with matched joining edges.
// Arguments:
// size = The [X,Y,Z] size of the object to partition.
// spread = The distance to spread the two parts by.
// cutsize = The width of the cut pattern to be used.
// cutpath = The cutpath to use. Standard named paths are "flat", "sawtooth", "sinewave", "comb", "finger", "dovetail", "hammerhead", and "jigsaw". Alternatively, you can give a cutpath as a 2D path, where X is between 0 and 1, and Y is between -0.5 and 0.5.
// gap = Empty gaps between cutpath iterations. Default: 0
2021-11-20 03:33:16 +00:00
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0`
2022-11-10 03:22:30 +00:00
// ---
// $slop = Extra gap to leave to correct for printer-specific fitting.
2019-07-10 05:30:27 +00:00
// Examples(Med):
2019-07-10 04:54:28 +00:00
// partition(spread=12, cutpath="dovetail") cylinder(h=50, d=80, center=false);
// partition(spread=12, gap=30, cutpath="dovetail") cylinder(h=50, d=80, center=false);
// partition(spread=20, gap=20, cutsize=15, cutpath="dovetail") cylinder(h=50, d=80, center=false);
// partition(spread=25, gap=15, cutsize=[20,20], cutpath="dovetail") cylinder(h=50, d=80, center=false);
2024-09-29 09:43:04 +00:00
// Side Effects:
// `$idx` is set to 0 on the back part and 1 on the front part.
2019-07-10 05:30:27 +00:00
// Examples(2DMed):
2019-07-10 04:54:28 +00:00
// partition(cutpath="sawtooth") cylinder(h=50, d=80, center=false);
// partition(cutpath="sinewave") cylinder(h=50, d=80, center=false);
// partition(cutpath="comb") cylinder(h=50, d=80, center=false);
// partition(cutpath="finger") cylinder(h=50, d=80, center=false);
// partition(spread=12, cutpath="dovetail") cylinder(h=50, d=80, center=false);
// partition(spread=12, cutpath="hammerhead") cylinder(h=50, d=80, center=false);
// partition(cutpath="jigsaw") cylinder(h=50, d=80, center=false);
2021-06-29 01:04:27 +00:00
module partition ( size = 100 , spread = 10 , cutsize = 10 , cutpath = "jigsaw" , gap = 0 , spin = 0 )
2019-07-10 04:54:28 +00:00
{
2022-03-31 22:36:09 +00:00
req_children ( $children ) ;
2020-05-30 02:04:34 +00:00
size = is_vector ( size ) ? size : [ size , size , size ] ;
cutsize = is_vector ( cutsize ) ? cutsize : [ cutsize * 2 , cutsize ] ;
2021-06-15 03:28:49 +00:00
rsize = v_abs ( rot ( spin , p = size ) ) ;
2020-05-30 02:04:34 +00:00
vec = rot ( spin , p = BACK ) * spread / 2 ;
move ( vec ) {
2024-09-29 09:43:04 +00:00
$ idx = 0 ;
2020-05-30 02:04:34 +00:00
intersection ( ) {
children ( ) ;
partition_mask ( l = rsize . x , w = rsize . y , h = rsize . z , cutsize = cutsize , cutpath = cutpath , gap = gap , spin = spin ) ;
}
}
move ( - vec ) {
2024-09-29 09:43:04 +00:00
$ idx = 1 ;
2020-05-30 02:04:34 +00:00
intersection ( ) {
children ( ) ;
partition_mask ( l = rsize . x , w = rsize . y , h = rsize . z , cutsize = cutsize , cutpath = cutpath , gap = gap , inverse = true , spin = spin ) ;
}
}
2019-07-10 04:54:28 +00:00
}
2020-05-30 02:04:34 +00:00
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap