mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-29 15:59:36 +00:00
Merge master.
This commit is contained in:
commit
3500f01e8f
8 changed files with 654 additions and 8 deletions
132
attachments.scad
132
attachments.scad
|
@ -382,6 +382,138 @@ module attach(from, to=undef, overlap=undef, norot=false)
|
|||
}
|
||||
|
||||
|
||||
// Module: edge_profile()
|
||||
// Usage:
|
||||
// edge_profile([edges], [except], [convexity]) ...
|
||||
// Description:
|
||||
// Takes a 2D mask shape and attaches it to the selected edges, with the appropriate orientation
|
||||
// and extruded length to be `diff()`ed away, to give the edge a matching profile.
|
||||
// Arguments:
|
||||
// edges = Edges to mask. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: All edges.
|
||||
// except = Edges to explicitly NOT mask. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: No edges.
|
||||
// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10
|
||||
// Side Effects:
|
||||
// Sets `$tags = "mask"` for all children.
|
||||
// Example:
|
||||
// diff("mask")
|
||||
// cube([50,60,70],center=true)
|
||||
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
|
||||
// mask2d_roundover(r=10, inset=2);
|
||||
module edge_profile(edges=EDGES_ALL, except=[], convexity=10) {
|
||||
assert($parent_size != undef, "No object to attach to!");
|
||||
edges = edges(edges, except=except);
|
||||
vecs = [
|
||||
for (i = [0:3], axis=[0:2])
|
||||
if (edges[axis][i]>0)
|
||||
EDGE_OFFSETS[axis][i]
|
||||
];
|
||||
for (vec = vecs) {
|
||||
vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0);
|
||||
assert(vcount == 2, "Not an edge vector!");
|
||||
anch = find_anchor(vec, $parent_size.z, point2d($parent_size), size2=$parent_size2, shift=$parent_shift, offset=$parent_offset, anchors=$parent_anchors, geometry=$parent_geom, two_d=$parent_2d);
|
||||
$attach_to = undef;
|
||||
$attach_anchor = anch;
|
||||
$attach_norot = true;
|
||||
$tags = "mask";
|
||||
length = sum(vmul($parent_size, [for (x=vec) x?0:1]))+0.1;
|
||||
rotang =
|
||||
vec.z<0? [90,0,180+vang(point2d(vec))] :
|
||||
vec.z==0 && sign(vec.x)==sign(vec.y)? 135+vang(point2d(vec)) :
|
||||
vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+vang(point2d(vec))] :
|
||||
[-90,0,180+vang(point2d(vec))];
|
||||
translate(anch[1]) {
|
||||
rot(rotang) {
|
||||
linear_extrude(height=length, center=true, convexity=convexity) {
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: edge_mask()
|
||||
// Usage:
|
||||
// edge_mask([edges], [except]) ...
|
||||
// Description:
|
||||
// Takes a 3D mask shape, and attaches it to the given edges, with the
|
||||
// appropriate orientation to be `diff()`ed away.
|
||||
// Arguments:
|
||||
// edges = Edges to mask. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: All edges.
|
||||
// except = Edges to explicitly NOT mask. See the docs for [`edges()`](edges.scad#edges) to see acceptable values. Default: No edges.
|
||||
// Side Effects:
|
||||
// Sets `$tags = "mask"` for all children.
|
||||
// Example:
|
||||
// diff("mask")
|
||||
// cube([50,60,70],center=true)
|
||||
// edge_mask([TOP,"Z"],except=[BACK,TOP+LEFT])
|
||||
// rounding_mask_z(l=71,r=10);
|
||||
module edge_mask(edges=EDGES_ALL, except=[]) {
|
||||
assert($parent_size != undef, "No object to attach to!");
|
||||
edges = edges(edges, except=except);
|
||||
vecs = [
|
||||
for (i = [0:3], axis=[0:2])
|
||||
if (edges[axis][i]>0)
|
||||
EDGE_OFFSETS[axis][i]
|
||||
];
|
||||
for (vec = vecs) {
|
||||
vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0);
|
||||
assert(vcount == 2, "Not an edge vector!");
|
||||
anch = find_anchor(vec, $parent_size.z, point2d($parent_size), size2=$parent_size2, shift=$parent_shift, offset=$parent_offset, anchors=$parent_anchors, geometry=$parent_geom, two_d=$parent_2d);
|
||||
$attach_to = undef;
|
||||
$attach_anchor = anch;
|
||||
$attach_norot = true;
|
||||
$tags = "mask";
|
||||
rotang =
|
||||
vec.z<0? [90,0,180+vang(point2d(vec))] :
|
||||
vec.z==0 && sign(vec.x)==sign(vec.y)? 135+vang(point2d(vec)) :
|
||||
vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+vang(point2d(vec))] :
|
||||
[-90,0,180+vang(point2d(vec))];
|
||||
translate(anch[1]) rot(rotang) children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: corner_mask()
|
||||
// Usage:
|
||||
// corner_mask([corners], [except]) ...
|
||||
// Description:
|
||||
// Takes a 3D mask shape, and attaches it to the given corners, with the appropriate
|
||||
// orientation to be `diff()`ed away. The 3D corner mask shape should be designed to
|
||||
// mask away the X+Y+Z+ octant.
|
||||
// Arguments:
|
||||
// corners = Edges to mask. See the docs for [`corners()`](edges.scad#corners) to see acceptable values. Default: All corners.
|
||||
// except = Edges to explicitly NOT mask. See the docs for [`corners()`](edges.scad#corners) to see acceptable values. Default: No corners.
|
||||
// Side Effects:
|
||||
// Sets `$tags = "mask"` for all children.
|
||||
// Example:
|
||||
// diff("mask")
|
||||
// cube(100, center=true)
|
||||
// corner_mask([TOP,FRONT],LEFT+FRONT+TOP)
|
||||
// difference() {
|
||||
// translate(-0.01*[1,1,1]) cube(20);
|
||||
// translate([20,20,20]) sphere(r=20);
|
||||
// }
|
||||
module corner_mask(corners=CORNERS_ALL, except=[]) {
|
||||
assert($parent_size != undef, "No object to attach to!");
|
||||
corners = corners(corners, except=except);
|
||||
vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]];
|
||||
for (vec = vecs) {
|
||||
vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0);
|
||||
assert(vcount == 3, "Not an edge vector!");
|
||||
anch = find_anchor(vec, $parent_size.z, point2d($parent_size), size2=$parent_size2, shift=$parent_shift, offset=$parent_offset, anchors=$parent_anchors, geometry=$parent_geom, two_d=$parent_2d);
|
||||
$attach_to = undef;
|
||||
$attach_anchor = anch;
|
||||
$attach_norot = true;
|
||||
$tags = "mask";
|
||||
rotang = vec.z<0?
|
||||
[ 0,0,180+vang(point2d(vec))-45] :
|
||||
[180,0,-90+vang(point2d(vec))-45];
|
||||
translate(anch[1]) rot(rotang) children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: tags()
|
||||
// Usage:
|
||||
// tags(tags) ...
|
||||
|
|
|
@ -73,6 +73,14 @@ function is_int(n) = is_num(n) && n == round(n);
|
|||
function is_integer(n) = is_num(n) && n == round(n);
|
||||
|
||||
|
||||
// Function: is_nan()
|
||||
// Usage:
|
||||
// is_nan(x);
|
||||
// Description:
|
||||
// Returns true if a given value `x` is nan, a floating point value representing "not a number".
|
||||
function is_nan(x) = (x!=x);
|
||||
|
||||
|
||||
|
||||
// Section: Handling `undef`s.
|
||||
|
||||
|
|
160
edges.scad
160
edges.scad
|
@ -33,6 +33,21 @@
|
|||
// cuboid(size=size,chamfer=chamfer,edges=edges);
|
||||
// fwd(size/2) text3d(lbl2, size=txtsize);
|
||||
// }
|
||||
// module corner_cube(size=20, txtsize=3, corners="ALL") {
|
||||
// corner_set = _corner_set(corners);
|
||||
// lbl = is_string(corners)? [str("\"",corners,"\"")] : concat(
|
||||
// corners.z>0? ["TOP"] : corners.z<0? ["BTM"] : [],
|
||||
// corners.y>0? ["BACK"] : corners.y<0? ["FWD"] : [],
|
||||
// corners.x>0? ["RIGHT"] : corners.x<0? ["LEFT"] : []
|
||||
// );
|
||||
// lbl2 = [for (i=idx(lbl)) i<len(lbl)-1? str(lbl[i],"+") : lbl[i]];
|
||||
// for (i=[0:7]) if (corner_set[i]>0)
|
||||
// translate(CORNER_OFFSETS[i]*size/2)
|
||||
// color("red")
|
||||
// cube(1, center=true);
|
||||
// fwd(size/2) text3d(lbl2, size=txtsize);
|
||||
// color("yellow",0.7) cuboid(size=size);
|
||||
// }
|
||||
|
||||
|
||||
// Section: Sets of Edges
|
||||
|
@ -225,6 +240,151 @@ EDGE_OFFSETS = [ // Array of XYZ offsets to the center of each edge.
|
|||
];
|
||||
|
||||
|
||||
// Section: Corner Sets
|
||||
// Constants for specifying corners.
|
||||
|
||||
CORNERS_NONE = [0,0,0,0,0,0,0,0]; // No corners.
|
||||
CORNERS_ALL = [1,1,1,1,1,1,1,1]; // All corners.
|
||||
|
||||
|
||||
// Section: Corner Helpers
|
||||
|
||||
// Function: is_corner_array()
|
||||
// Usage:
|
||||
// is_corner_array(v)
|
||||
// Description:
|
||||
// Returns true if the given value has the form of a corner array.
|
||||
function is_corner_array(v) = is_vector(v) && len(v)==8 && all([for (x=v) x==1||x==0]);
|
||||
|
||||
|
||||
// Function: normalize_corners()
|
||||
// Usage:
|
||||
// normalize_corners(v);
|
||||
// Description:
|
||||
// Normalizes all values in a corner array to be `1`, if it was originally greater than `0`,
|
||||
// or `0`, if it was originally less than or equal to `0`.
|
||||
function normalize_corners(v) = [for (x=v) x>0? 1 : 0];
|
||||
|
||||
|
||||
function _corner_set(v) =
|
||||
is_corner_array(v)? v : [
|
||||
for (i=[0:7]) let(
|
||||
v2 = CORNER_OFFSETS[i]
|
||||
) (
|
||||
is_string(v)? (
|
||||
v=="ALL"? true : // Return all corners.
|
||||
v=="NONE"? false : // Return no corners.
|
||||
let(valid_values = ["ALL", "NONE"])
|
||||
assert(
|
||||
in_list(v, valid_values),
|
||||
str(v, " must be a vector, corner array, or one of ", valid_values)
|
||||
) v
|
||||
) :
|
||||
all([for (i=[0:2]) !v[i] || (v[i]==v2[i])])
|
||||
)? 1 : 0
|
||||
];
|
||||
|
||||
|
||||
// Function: corners()
|
||||
// Usage:
|
||||
// corners(v)
|
||||
// corners(v, except)
|
||||
// Description:
|
||||
// Takes a list of corner set descriptors, and returns a normalized corners array
|
||||
// that represents all those given corners. If the `except` argument is given
|
||||
// a list of corner set descriptors, then all those corners will be removed
|
||||
// from the returned corners array. If either argument only has a single corner
|
||||
// set descriptor, you do not have to pass it in a list.
|
||||
// Each corner set descriptor can be any of:
|
||||
// - A vector pointing towards an edge indicating both corners at the ends of that edge.
|
||||
// - A vector pointing towards a face, indicating all the corners of that face.
|
||||
// - A vector pointing towards a corner, indicating just that corner.
|
||||
// - The string `"ALL"`, indicating all corners.
|
||||
// - The string `"NONE"`, indicating no corners at all.
|
||||
// - A raw corners array, where each corner is represented by a 1 or a 0. The corner ordering is:
|
||||
// ```
|
||||
// [X-Y-Z-, X+Y-Z-, X-Y+Z-, X+Y+Z-, X-Y-Z+, X+Y-Z+, X-Y+Z+, X+Y+Z+]
|
||||
// ```
|
||||
// Figure(3DBig): Edge Vectors
|
||||
// ydistribute(55) {
|
||||
// xdistribute(35) {
|
||||
// corner_cube(corners=BOT+RIGHT);
|
||||
// corner_cube(corners=BOT+BACK);
|
||||
// corner_cube(corners=BOT+LEFT);
|
||||
// corner_cube(corners=BOT+FRONT);
|
||||
// }
|
||||
// xdistribute(35) {
|
||||
// corner_cube(corners=FWD+RIGHT);
|
||||
// corner_cube(corners=BACK+RIGHT);
|
||||
// corner_cube(corners=BACK+LEFT);
|
||||
// corner_cube(corners=FWD+LEFT);
|
||||
// }
|
||||
// xdistribute(35) {
|
||||
// corner_cube(corners=TOP+RIGHT);
|
||||
// corner_cube(corners=TOP+BACK);
|
||||
// corner_cube(corners=TOP+LEFT);
|
||||
// corner_cube(corners=TOP+FRONT);
|
||||
// }
|
||||
// }
|
||||
// Figure(3DBig): Corner Vector Edge Sets
|
||||
// ydistribute(55) {
|
||||
// xdistribute(35) {
|
||||
// corner_cube(corners=FRONT+LEFT+TOP);
|
||||
// corner_cube(corners=FRONT+RIGHT+TOP);
|
||||
// corner_cube(corners=FRONT+LEFT+BOT);
|
||||
// corner_cube(corners=FRONT+RIGHT+BOT);
|
||||
// }
|
||||
// xdistribute(35) {
|
||||
// corner_cube(corners=TOP+LEFT+BACK);
|
||||
// corner_cube(corners=TOP+RIGHT+BACK);
|
||||
// corner_cube(corners=BOT+LEFT+BACK);
|
||||
// corner_cube(corners=BOT+RIGHT+BACK);
|
||||
// }
|
||||
// }
|
||||
// Figure(3D): Face Vector Edge Sets
|
||||
// ydistribute(55) {
|
||||
// xdistribute(35) {
|
||||
// corner_cube(corners=LEFT);
|
||||
// corner_cube(corners=FRONT);
|
||||
// corner_cube(corners=RIGHT);
|
||||
// }
|
||||
// xdistribute(35) {
|
||||
// corner_cube(corners=TOP);
|
||||
// corner_cube(corners=BACK);
|
||||
// corner_cube(corners=BOTTOM);
|
||||
// }
|
||||
// }
|
||||
// Figure(3D): Named Edge Sets
|
||||
// xdistribute(35) {
|
||||
// corner_cube(corners="ALL");
|
||||
// corner_cube(corners="NONE");
|
||||
// }
|
||||
// Example: Just the front-top-right corner
|
||||
// corners(FRONT+TOP+RIGHT)
|
||||
// Example: All corners surrounding either the front or top faces
|
||||
// corners([FRONT,TOP])
|
||||
// Example: All corners around the bottom face, except any that are also on the front
|
||||
// corners(BTM, except=FRONT)
|
||||
// Example: All corners except those around the bottom face.
|
||||
// corners("ALL", except=BOTTOM)
|
||||
// Example: All corners around the bottom or front faces, except those on the bottom-front edge.
|
||||
// corners([BOTTOM,FRONT], except=BOTTOM+FRONT)
|
||||
function corners(v, except=[]) =
|
||||
(is_string(v) || is_vector(v) || is_corner_array(v))? corners([v], except=except) :
|
||||
(is_string(except) || is_vector(except) || is_corner_array(except))? corners(v, except=[except]) :
|
||||
except==[]? normalize_corners(sum([for (x=v) _corner_set(x)])) :
|
||||
let(
|
||||
a = normalize_corners(sum([for (x=v) _corner_set(x)])),
|
||||
b = normalize_corners(sum([for (x=except) _corner_set(x)]))
|
||||
) normalize_corners(a - b);
|
||||
|
||||
|
||||
CORNER_OFFSETS = [ // Array of XYZ offsets to each corner.
|
||||
[-1,-1,-1], [ 1,-1,-1], [-1, 1,-1], [ 1, 1,-1],
|
||||
[-1,-1, 1], [ 1,-1, 1], [-1, 1, 1], [ 1, 1, 1]
|
||||
];
|
||||
|
||||
|
||||
// Function: corner_edge_count()
|
||||
// Description: Counts how many given edges intersect at a specific corner.
|
||||
// Arguments:
|
||||
|
|
19
masks.scad
19
masks.scad
|
@ -411,10 +411,21 @@ module rounding_mask(l=undef, r=undef, r1=undef, r2=undef, anchor=CENTER, spin=0
|
|||
r2 = get_radius(r1=r2, r=r, dflt=1);
|
||||
sides = quantup(segs(max(r1,r2)),4);
|
||||
orient_and_anchor([2*r1, 2*r1, l], orient, anchor, spin=spin, size2=[2*r2,2*r2], chain=true) {
|
||||
linear_extrude(height=l+0.1, convexity=4, center=true, scale=r2/r1) {
|
||||
difference() {
|
||||
square(2*r1, center=true);
|
||||
xspread(2*r1) yspread(2*r1) circle(r=r1, $fn=sides);
|
||||
if (r1<r2) {
|
||||
zflip() {
|
||||
linear_extrude(height=l, convexity=4, center=true, scale=r1/r2) {
|
||||
difference() {
|
||||
square(2*r2, center=true);
|
||||
xspread(2*r2) yspread(2*r2) circle(r=r2, $fn=sides);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
linear_extrude(height=l, convexity=4, center=true, scale=r2/r1) {
|
||||
difference() {
|
||||
square(2*r1, center=true);
|
||||
xspread(2*r1) yspread(2*r1) circle(r=r1, $fn=sides);
|
||||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
|
|
12
math.scad
12
math.scad
|
@ -14,6 +14,10 @@ PHI = (1+sqrt(5))/2; // The golden ratio phi.
|
|||
|
||||
EPSILON = 1e-9; // A really small value useful in comparing FP numbers. ie: abs(a-b)<EPSILON
|
||||
|
||||
INF = 1/0; // The value `inf`, useful for comparisons.
|
||||
|
||||
NAN = acos(2); // The value `nan`, useful for comparisons.
|
||||
|
||||
|
||||
|
||||
// Section: Simple math
|
||||
|
@ -424,14 +428,16 @@ function lcm(a,b=[]) =
|
|||
// Description:
|
||||
// Returns the sum of all entries in the given list.
|
||||
// If passed an array of vectors, returns a vector of sums of each part.
|
||||
// If passed an empty list, the value of `dflt` will be returned.
|
||||
// Arguments:
|
||||
// v = The list to get the sum of.
|
||||
// dflt = The default value to return if `v` is an empty list. Default: 0
|
||||
// Example:
|
||||
// sum([1,2,3]); // returns 6.
|
||||
// sum([[1,2,3], [3,4,5], [5,6,7]]); // returns [9, 12, 15]
|
||||
function sum(v, _i=0, _acc) =
|
||||
_i>=len(v)? _acc :
|
||||
sum(v, _i=_i+1, _acc=is_undef(_acc)? v[_i] : _acc+v[_i]);
|
||||
function sum(v, dflt=0, _i=0, _acc) =
|
||||
_i>=len(v)? (len(v)? _acc : dflt) :
|
||||
sum(v, dflt=dflt, _i=_i+1, _acc=is_undef(_acc)? v[_i] : _acc+v[_i]);
|
||||
|
||||
|
||||
// Function: cumsum()
|
||||
|
|
326
shapes2d.scad
326
shapes2d.scad
|
@ -1010,5 +1010,331 @@ module supershape(step=0.5,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=und
|
|||
polygon(supershape(step=step,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b, r=r,d=d, anchor=anchor, spin=spin));
|
||||
|
||||
|
||||
// Section: 2D Masking Shapes
|
||||
|
||||
// Function&Module: mask2d_roundover()
|
||||
// Usage:
|
||||
// mask2d_roundover(r|d, [inset], [excess]);
|
||||
// Description:
|
||||
// Creates a 2D roundover/bead mask shape that is useful for extruding into a 3D mask for a 90º edge.
|
||||
// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
|
||||
// If called as a function, this just returns a 2D path of the outline of the mask shape.
|
||||
// Arguments:
|
||||
// r = Radius of the roundover.
|
||||
// d = Diameter of the roundover.
|
||||
// inset = Optional bead inset size. Default: 0
|
||||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape.
|
||||
// Example(2D): 2D Roundover Mask
|
||||
// mask2d_roundover(r=10);
|
||||
// Example(2D): 2D Bead Mask
|
||||
// mask2d_roundover(r=10,inset=2);
|
||||
// Example: Masking by Edge Attachment
|
||||
// diff("mask")
|
||||
// cube([50,60,70],center=true)
|
||||
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
|
||||
// mask2d_roundover(r=10, inset=2);
|
||||
module mask2d_roundover(r, d, excess, inset=0) {
|
||||
polygon(mask2d_roundover(r=r,d=d,excess=excess,inset=inset));
|
||||
}
|
||||
|
||||
function mask2d_roundover(r, d, excess, inset=0) =
|
||||
assert(is_num(r)||is_num(d))
|
||||
assert(is_undef(excess)||is_num(excess))
|
||||
assert(is_num(inset)||(is_vector(inset)&&len(inset)==2))
|
||||
let(
|
||||
inset = is_list(inset)? inset : [inset,inset],
|
||||
excess = default(excess,$overlap),
|
||||
r = get_radius(r=r,d=d,dflt=1),
|
||||
steps = quantup(segs(r),4)/4,
|
||||
step = 90/steps
|
||||
) [
|
||||
[-excess,-excess], [-excess, r+inset.y],
|
||||
for (i=[0:1:steps]) [r,r] + inset + polar_to_xy(r,180+i*step),
|
||||
[r+inset.x,-excess]
|
||||
];
|
||||
|
||||
|
||||
// Function&Module: mask2d_cove()
|
||||
// Usage:
|
||||
// mask2d_cove(r|d, [inset], [excess]);
|
||||
// Description:
|
||||
// Creates a 2D cove mask shape that is useful for extruding into a 3D mask for a 90º edge.
|
||||
// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
|
||||
// If called as a function, this just returns a 2D path of the outline of the mask shape.
|
||||
// Arguments:
|
||||
// r = Radius of the cove.
|
||||
// d = Diameter of the cove.
|
||||
// inset = Optional amount to inset code from corner. Default: 0
|
||||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape.
|
||||
// Example(2D): 2D Cove Mask
|
||||
// mask2d_cove(r=10);
|
||||
// Example(2D): 2D Inset Cove Mask
|
||||
// mask2d_cove(r=10,inset=3);
|
||||
// Example: Masking by Edge Attachment
|
||||
// diff("mask")
|
||||
// cube([50,60,70],center=true)
|
||||
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
|
||||
// mask2d_cove(r=10, inset=2);
|
||||
module mask2d_cove(r, d, inset=0, excess) {
|
||||
polygon(mask2d_cove(r=r,d=d,excess=excess,inset=inset));
|
||||
}
|
||||
|
||||
function mask2d_cove(r, d, inset=0, excess) =
|
||||
assert(is_num(r)||is_num(d))
|
||||
assert(is_undef(excess)||is_num(excess))
|
||||
assert(is_num(inset)||(is_vector(inset)&&len(inset)==2))
|
||||
let(
|
||||
inset = is_list(inset)? inset : [inset,inset],
|
||||
excess = default(excess,$overlap),
|
||||
r = get_radius(r=r,d=d,dflt=1),
|
||||
steps = quantup(segs(r),4)/4,
|
||||
step = 90/steps
|
||||
) [
|
||||
[-excess,-excess], [-excess, r+inset.y],
|
||||
for (i=[0:1:steps]) inset + polar_to_xy(r,90-i*step),
|
||||
[r+inset.x,-excess]
|
||||
];
|
||||
|
||||
|
||||
// Function&Module: mask2d_chamfer()
|
||||
// Usage:
|
||||
// mask2d_chamfer(x|y|edge, [angle], [inset], [excess]);
|
||||
// Description:
|
||||
// Creates a 2D chamfer mask shape that is useful for extruding into a 3D mask for a 90º edge.
|
||||
// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
|
||||
// If called as a function, this just returns a 2D path of the outline of the mask shape.
|
||||
// Arguments:
|
||||
// x = The width of the chamfer.
|
||||
// y = The height of the chamfer.
|
||||
// edge = The length of the edge of the chamfer.
|
||||
// angle = The angle of the chamfer edge, away from vertical. Default: 45.
|
||||
// inset = Optional amount to inset code from corner. Default: 0
|
||||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape.
|
||||
// Example(2D): 2D Chamfer Mask
|
||||
// mask2d_chamfer(x=10);
|
||||
// Example(2D): 2D Chamfer Mask by Width.
|
||||
// mask2d_chamfer(x=10, angle=30);
|
||||
// Example(2D): 2D Chamfer Mask by Height.
|
||||
// mask2d_chamfer(y=10, angle=30);
|
||||
// Example(2D): 2D Inset Chamfer Mask
|
||||
// mask2d_chamfer(x=10, inset=2);
|
||||
// Example: Masking by Edge Attachment
|
||||
// diff("mask")
|
||||
// cube([50,60,70],center=true)
|
||||
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
|
||||
// mask2d_chamfer(x=10, inset=2);
|
||||
module mask2d_chamfer(x, y, edge, angle=45, excess, inset=0) {
|
||||
polygon(mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset));
|
||||
}
|
||||
|
||||
function mask2d_chamfer(x, y, edge, angle=45, excess, inset=0) =
|
||||
assert(num_defined([x,y,edge])==1)
|
||||
assert(is_num(first_defined([x,y,edge])))
|
||||
assert(is_num(angle))
|
||||
assert(is_undef(excess)||is_num(excess))
|
||||
assert(is_num(inset)||(is_vector(inset)&&len(inset)==2))
|
||||
let(
|
||||
inset = is_list(inset)? inset : [inset,inset],
|
||||
excess = default(excess,$overlap),
|
||||
x = !is_undef(x)? x :
|
||||
!is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) :
|
||||
hyp_ang_to_opp(hyp=edge,ang=angle),
|
||||
y = opp_ang_to_adj(opp=x,ang=angle)
|
||||
) [
|
||||
[-excess, -excess], [-excess, y+inset.y],
|
||||
[inset.x, y+inset.y], [x+inset.x, inset.y],
|
||||
[x+inset.x, -excess]
|
||||
];
|
||||
|
||||
|
||||
// Function&Module: mask2d_rabbet()
|
||||
// Usage:
|
||||
// mask2d_rabbet(size, [excess]);
|
||||
// Description:
|
||||
// Creates a 2D rabbet mask shape that is useful for extruding into a 3D mask for a 90º edge.
|
||||
// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
|
||||
// If called as a function, this just returns a 2D path of the outline of the mask shape.
|
||||
// Arguments:
|
||||
// size = The size of the rabbet, either as a scalar or an [X,Y] list.
|
||||
// inset = Optional amount to inset code from corner. Default: 0
|
||||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape.
|
||||
// Example(2D): 2D Rabbet Mask
|
||||
// mask2d_rabbet(size=10);
|
||||
// Example(2D): 2D Asymmetrical Rabbet Mask
|
||||
// mask2d_rabbet(size=[5,10]);
|
||||
// Example: Masking by Edge Attachment
|
||||
// diff("mask")
|
||||
// cube([50,60,70],center=true)
|
||||
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
|
||||
// mask2d_rabbet(size=10);
|
||||
module mask2d_rabbet(size, excess) {
|
||||
polygon(mask2d_rabbet(size=size, excess=excess));
|
||||
}
|
||||
|
||||
function mask2d_rabbet(size, excess) =
|
||||
assert(is_num(size)||(is_vector(size)&&len(size)==2))
|
||||
assert(is_undef(excess)||is_num(excess))
|
||||
let(
|
||||
excess = default(excess,$overlap),
|
||||
size = is_list(size)? size : [size,size]
|
||||
) [
|
||||
[-excess, -excess], [-excess, size.y], size, [size.x, -excess]
|
||||
];
|
||||
|
||||
|
||||
// Function&Module: mask2d_dovetail()
|
||||
// Usage:
|
||||
// mask2d_dovetail(x|y|edge, [angle], [inset], [shelf], [excess]);
|
||||
// Description:
|
||||
// Creates a 2D dovetail mask shape that is useful for extruding into a 3D mask for a 90º edge.
|
||||
// This 2D mask is designed to be differenced away from the edge of a shape that is in the first (X+Y+) quadrant.
|
||||
// If called as a function, this just returns a 2D path of the outline of the mask shape.
|
||||
// Arguments:
|
||||
// x = The width of the dovetail.
|
||||
// y = The height of the dovetail.
|
||||
// edge = The length of the edge of the dovetail.
|
||||
// angle = The angle of the chamfer edge, away from vertical. Default: 30.
|
||||
// inset = Optional amount to inset code from corner. Default: 0
|
||||
// shelf = The extra height to add to the inside corner of the dovetail. Default: 0
|
||||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape.
|
||||
// Example(2D): 2D Dovetail Mask
|
||||
// mask2d_dovetail(x=10);
|
||||
// Example(2D): 2D Dovetail Mask by Width.
|
||||
// mask2d_dovetail(x=10, angle=30);
|
||||
// Example(2D): 2D Dovetail Mask by Height.
|
||||
// mask2d_dovetail(y=10, angle=30);
|
||||
// Example(2D): 2D Inset Dovetail Mask
|
||||
// mask2d_dovetail(x=10, inset=2);
|
||||
// Example: Masking by Edge Attachment
|
||||
// diff("mask")
|
||||
// cube([50,60,70],center=true)
|
||||
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
|
||||
// mask2d_dovetail(x=10, inset=2);
|
||||
module mask2d_dovetail(x, y, edge, angle=30, inset=0, shelf=0, excess) {
|
||||
polygon(mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess));
|
||||
}
|
||||
|
||||
function mask2d_dovetail(x, y, edge, angle=30, inset=0, shelf=0, excess) =
|
||||
assert(num_defined([x,y,edge])==1)
|
||||
assert(is_num(first_defined([x,y,edge])))
|
||||
assert(is_num(angle))
|
||||
assert(is_undef(excess)||is_num(excess))
|
||||
assert(is_num(inset)||(is_vector(inset)&&len(inset)==2))
|
||||
let(
|
||||
inset = is_list(inset)? inset : [inset,inset],
|
||||
excess = default(excess,$overlap),
|
||||
x = !is_undef(x)? x :
|
||||
!is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) :
|
||||
hyp_ang_to_opp(hyp=edge,ang=angle),
|
||||
y = opp_ang_to_adj(opp=x,ang=angle)
|
||||
) [
|
||||
[-excess, 0], [-excess, y+inset.y+shelf],
|
||||
inset+[x,y+shelf], inset+[x,y], inset, [inset.x,0]
|
||||
];
|
||||
|
||||
|
||||
// Function&Module: mask2d_ogee()
|
||||
// Usage:
|
||||
// mask2d_ogee(x|y|edge, [angle], [inset], [shelf], [excess]);
|
||||
//
|
||||
// Description:
|
||||
// Creates a 2D Ogee mask shape that is useful for extruding into a 3D mask for a 90º edge.
|
||||
// This 2D mask is designed to be `difference()`d away from the edge of a shape that is in the first (X+Y+) quadrant.
|
||||
// Since there are a number of shapes that fall under the name ogee, the shape of this mask is given as a pattern.
|
||||
// Patterns are given as TYPE, VALUE pairs. ie: `["fillet",10, "xstep",2, "step",[5,5], ...]`. See Patterns below.
|
||||
// If called as a function, this just returns a 2D path of the outline of the mask shape.
|
||||
//
|
||||
// ### Patterns
|
||||
//
|
||||
// Type | Argument | Description
|
||||
// -------- | --------- | ----------------
|
||||
// "step" | [x,y] | Makes a line to a point `x` right and `y` down.
|
||||
// "xstep" | dist | Makes a `dist` length line towards X+.
|
||||
// "ystep" | dist | Makes a `dist` length line towards Y-.
|
||||
// "round" | radius | Makes an arc that will mask a roundover.
|
||||
// "fillet" | radius | Makes an arc that will mask a fillet.
|
||||
//
|
||||
// Arguments:
|
||||
// pattern = A list of pattern pieces to describe the Ogee.
|
||||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape.
|
||||
//
|
||||
// Example(2D): 2D Ogee Mask
|
||||
// mask2d_ogee([
|
||||
// "xstep",1, "ystep",1, // Starting shoulder.
|
||||
// "fillet",5, "round",5, // S-curve.
|
||||
// "ystep",1, "xstep",1 // Ending shoulder.
|
||||
// ]);
|
||||
module mask2d_ogee(pattern, excess) {
|
||||
polygon(mask2d_ogee(pattern, excess=excess));
|
||||
}
|
||||
|
||||
function mask2d_ogee(pattern, excess) =
|
||||
assert(is_list(pattern))
|
||||
assert(len(pattern)>0)
|
||||
assert(len(pattern)%2==0,"pattern must be a list of TYPE, VAL pairs.")
|
||||
assert(all([for (i = idx(pattern,step=2)) in_list(pattern[i],["step","xstep","ystep","round","fillet"])]))
|
||||
let(
|
||||
excess = default(excess,$overlap),
|
||||
x = concat([0], cumsum([
|
||||
for (i=idx(pattern,step=2)) let(
|
||||
type = pattern[i],
|
||||
val = pattern[i+1]
|
||||
) (
|
||||
type=="step"? val.x :
|
||||
type=="xstep"? val :
|
||||
type=="round"? val :
|
||||
type=="fillet"? val :
|
||||
0
|
||||
)
|
||||
])),
|
||||
y = concat([0], cumsum([
|
||||
for (i=idx(pattern,step=2)) let(
|
||||
type = pattern[i],
|
||||
val = pattern[i+1]
|
||||
) (
|
||||
type=="step"? val.y :
|
||||
type=="ystep"? val :
|
||||
type=="round"? val :
|
||||
type=="fillet"? val :
|
||||
0
|
||||
)
|
||||
])),
|
||||
tot_x = select(x,-1),
|
||||
tot_y = select(y,-1),
|
||||
data = [
|
||||
for (i=idx(pattern,step=2)) let(
|
||||
type = pattern[i],
|
||||
val = pattern[i+1],
|
||||
pt = [x[i/2], tot_y-y[i/2]] + (
|
||||
type=="step"? [val.x,-val.y] :
|
||||
type=="xstep"? [val,0] :
|
||||
type=="ystep"? [0,-val] :
|
||||
type=="round"? [val,0] :
|
||||
type=="fillet"? [0,-val] :
|
||||
[0,0]
|
||||
)
|
||||
) [type, val, pt]
|
||||
],
|
||||
path = [
|
||||
[tot_x,-excess],
|
||||
[-excess,-excess],
|
||||
[-excess,tot_y],
|
||||
for (pat = data) each
|
||||
pat[0]=="step"? [pat[2]] :
|
||||
pat[0]=="xstep"? [pat[2]] :
|
||||
pat[0]=="ystep"? [pat[2]] :
|
||||
let(
|
||||
r = pat[1],
|
||||
steps = segs(abs(r)),
|
||||
step = 90/steps
|
||||
) [
|
||||
for (i=[0:1:steps]) let(
|
||||
a = pat[0]=="round"? (180+i*step) : (90-i*step)
|
||||
) pat[2] + abs(r)*[cos(a),sin(a)]
|
||||
]
|
||||
]
|
||||
) deduplicate(path);
|
||||
|
||||
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
@ -293,6 +293,8 @@ test_atanh();
|
|||
|
||||
|
||||
module test_sum() {
|
||||
assert(sum([]) == 0);
|
||||
assert(sum([],dflt=undef) == undef);
|
||||
assert(sum([1,2,3]) == 6);
|
||||
assert(sum([-2,-1,0,1,2]) == 0);
|
||||
assert(sum([[1,2,3], [3,4,5], [5,6,7]]) == [9,12,15]);
|
||||
|
@ -301,6 +303,7 @@ test_sum();
|
|||
|
||||
|
||||
module test_cumsum() {
|
||||
assert(cumsum([]) == []);
|
||||
assert(cumsum([1,1,1]) == [1,2,3]);
|
||||
assert(cumsum([2,2,2]) == [2,4,6]);
|
||||
assert(cumsum([1,2,3]) == [1,3,6]);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BOSL_VERSION = [2,0,117];
|
||||
BOSL_VERSION = [2,0,124];
|
||||
|
||||
|
||||
// Section: BOSL Library Version Functions
|
||||
|
|
Loading…
Reference in a new issue