mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-04 03:09:45 +00:00
Merge pull request #1308 from BelfrySCAD/revarbat_dev
Added catenary_path().
This commit is contained in:
commit
9ef3abc04a
9 changed files with 373 additions and 155 deletions
29
.github/workflows/pr_merge.yml
vendored
29
.github/workflows/pr_merge.yml
vendored
|
@ -1,36 +1,27 @@
|
|||
name: VersionBump
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
VersionBump:
|
||||
if: github.event.pull_request.merged == true
|
||||
VersionBumpJob:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.merged == true
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Apt Update
|
||||
run: sudo apt update
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Bump Version
|
||||
id: commit
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE
|
||||
./scripts/increment_version.sh
|
||||
run: ./scripts/increment_version.sh
|
||||
|
||||
- name: Push changes
|
||||
- name: Checkin
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
branch: master
|
||||
commit_user_email: github+actions@gmail.com
|
||||
commit_message: Version Bump
|
||||
|
||||
|
||||
file_pattern: version.scad
|
||||
|
||||
|
|
|
@ -633,6 +633,7 @@ metadata directives:
|
|||
- `Anim`: Make an animation where `$t` varies from `0.0` to almost `1.0`.
|
||||
- `Frames=36`: Number of animation frames to make.
|
||||
- `FrameMS=250`: Sets the number of milliseconds per frame for spins and animation.
|
||||
- `FPS=8`: Sets the number of frames per second for spins and animation.
|
||||
- `Small`: Make the image small sized.
|
||||
- `Med`: Make the image medium sized.
|
||||
- `Big`: Make the image big sized.
|
||||
|
|
83
drawing.scad
83
drawing.scad
|
@ -864,6 +864,89 @@ module arc(n, r, angle, d, cp, points, corner, width, thickness, start, wedge=fa
|
|||
}
|
||||
|
||||
|
||||
// Function: catenary()
|
||||
// Synopsis: Returns a 2D Catenary chain or arch path.
|
||||
// SynTags: Path
|
||||
// Topics: Paths
|
||||
// See Also: circle(), stroke()
|
||||
// Usage:
|
||||
// path = catenary(width, droop=|angle=, n=);
|
||||
// Description:
|
||||
// Returns a 2D Catenary path, which is the path a chain held at both ends will take.
|
||||
// The path will have the endpoints at `[±width/2, 0]`, and the middle of the path will droop
|
||||
// towards Y- if the given droop= or angle= is positive. It will droop towards Y+ if the
|
||||
// droop= or angle= is negative. You *must* specify one of droop= or angle=.
|
||||
// Arguments:
|
||||
// width = The straight-line distance between the endpoints of the path.
|
||||
// droop = If given, specifies the height difference between the endpoints and the hanging middle of the path. If given a negative value, returns an arch *above* the Y axis.
|
||||
// n = The number of points to return in the path. Default: 100
|
||||
// ---
|
||||
// angle = If given, specifies the angle that the path will droop by at the endpoints. If given a negative value, returns an arch *above* the Y axis.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). (Module only) Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). (Module only) Default: `0`
|
||||
// Example(2D): By Droop
|
||||
// stroke(catenary(100, droop=30));
|
||||
// Example(2D): By Angle
|
||||
// stroke(catenary(100, angle=30));
|
||||
// Example(2D): Upwards Arch by Angle
|
||||
// stroke(catenary(100, angle=30));
|
||||
// Example(2D): Upwards Arch by Height Delta
|
||||
// stroke(catenary(100, droop=-30));
|
||||
// Example(2D): Specifying Vertex Count
|
||||
// stroke(catenary(100, angle=-85, n=11), dots="dot");
|
||||
// Example: Sweeping a Catenary Path
|
||||
// path = xrot(90, p=path3d(catenary(100, droop=20, n=41)));
|
||||
// path_sweep(circle(r=1.5, $fn=24), path);
|
||||
function catenary(width, droop, n=100, angle) =
|
||||
assert(one_defined([droop, angle],"droop,angle"))
|
||||
let(
|
||||
sgn = is_undef(droop)? sign(angle) : sign(droop),
|
||||
droop = droop==undef? undef : abs(droop),
|
||||
angle = angle==undef? undef : abs(angle)
|
||||
)
|
||||
assert(is_finite(width) && width>0, "Bad width= value.")
|
||||
assert(is_integer(n) && n>0, "Bad n= value. Must be a positive integer.")
|
||||
assert(is_undef(droop) || is_finite(droop), "Bad droop= value.")
|
||||
assert(is_undef(angle) || (is_finite(angle) && angle != 0 && abs(angle) < 90), "Bad angle= value.")
|
||||
let(
|
||||
catlup_fn = is_undef(droop)
|
||||
? function(x) let(
|
||||
p1 = [x-0.001, cosh(x-0.001)-1],
|
||||
p2 = [x+0.001, cosh(x+0.001)-1],
|
||||
delta = p2-p1,
|
||||
ang = atan2(delta.y, delta.x)
|
||||
) ang
|
||||
: function(x) (cosh(x)-1)/x,
|
||||
binsearch_fn = function(targ,x=0,inc=4)
|
||||
inc < 1e-9? lookup(targ,[[catlup_fn(x),x],[catlup_fn(x+inc),x+inc]]) :
|
||||
catlup_fn(x+inc) > targ? binsearch_fn(targ,x,inc/2) :
|
||||
binsearch_fn(targ,x+inc,inc),
|
||||
scx = is_undef(droop)? binsearch_fn(angle) :
|
||||
binsearch_fn(droop / (width/2)),
|
||||
sc = width/2 / scx,
|
||||
droop = !is_undef(droop)? droop : (cosh(scx)-1) * sc,
|
||||
path = [
|
||||
for (x = lerpn(-scx,scx,n))
|
||||
let(
|
||||
xval = x * sc,
|
||||
yval = approx(abs(x),scx)? 0 :
|
||||
(cosh(x)-1) * sc - droop
|
||||
)
|
||||
[xval, yval]
|
||||
],
|
||||
out = sgn>0? path : yflip(p=path)
|
||||
) out;
|
||||
|
||||
|
||||
module catenary(width, droop, n=100, angle, anchor=CTR, spin=0) {
|
||||
path = catenary(width=width, droop=droop, n=n, angle=angle);
|
||||
attachable(anchor,spin, two_d=true, path=path, extent=true) {
|
||||
polygon(path);
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Function: helix()
|
||||
// Synopsis: Creates a 2d spiral or 3d helical path.
|
||||
// SynTags: Path
|
||||
|
|
322
masks2d.scad
322
masks2d.scad
|
@ -20,14 +20,16 @@
|
|||
// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
|
||||
// See Also: corner_profile(), edge_profile(), face_profile(), fillet()
|
||||
// Usage: As module
|
||||
// mask2d_roundover(r|d=, [inset], [mask_angle], [excess]) [ATTACHMENTS];
|
||||
// mask2d_roundover(r|d=|h=|cut=|joint=, [inset], [mask_angle], [excess], [flat_top=]) [ATTACHMENTS];
|
||||
// Usage: As function
|
||||
// path = mask2d_roundover(r|d=, [inset], [mask_angle], [excess]);
|
||||
// path = mask2d_roundover(r|d=|h=|cut=|joint=, [inset], [mask_angle], [excess], [flat_top=]);
|
||||
// Description:
|
||||
// Creates a 2D roundover/bead mask shape that is useful for extruding into a 3D mask for an edge.
|
||||
// Conversely, you can use that same extruded shape to make an interior fillet between two walls.
|
||||
// As a 2D mask, this 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.
|
||||
// The roundover can be specified by radius, diameter, height, cut, or joint length.
|
||||
// ![Types of Roundovers](images/rounding/section-types-of-roundovers_fig1.png)
|
||||
// Arguments:
|
||||
// r = Radius of the roundover.
|
||||
// inset = Optional bead inset size. Default: 0
|
||||
|
@ -35,24 +37,34 @@
|
|||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
|
||||
// ---
|
||||
// d = Diameter of the roundover.
|
||||
// h = Mask height. Given instead of r or d when you want a consistent mask height, no matter what the mask angle.
|
||||
// cut = Cut distance. IE: How much of the corner to cut off. See [Types of Roundovers](rounding.scad#section-types-of-roundovers).
|
||||
// joint = Joint distance. IE: How far from the edge the roundover should start. See [Types of Roundovers](rounding.scad#section-types-of-roundovers).
|
||||
// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||
// Side Effects:
|
||||
// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
|
||||
//
|
||||
// Example(2D): 2D Roundover Mask
|
||||
// Example(2D): 2D Roundover Mask by Radius
|
||||
// mask2d_roundover(r=10);
|
||||
// Example(2D): 2D Bead Mask
|
||||
// mask2d_roundover(r=10,inset=2);
|
||||
// Example(2D): 2D Bead Mask by Height
|
||||
// mask2d_roundover(h=10,inset=2);
|
||||
// Example(2D): 2D Bead Mask for a Non-Right Edge.
|
||||
// mask2d_roundover(r=10, inset=2, mask_angle=75);
|
||||
// Example(2D): Disabling flat_top=
|
||||
// mask2d_roundover(r=10, inset=2, flat_top=false, mask_angle=75);
|
||||
// Example(2D): 2D Angled Bead Mask by Joint Length
|
||||
// mask2d_roundover(joint=10, inset=2, mask_angle=75);
|
||||
// Example(2D): Increasing the Excess
|
||||
// mask2d_roundover(r=10, inset=2, mask_angle=75, excess=2);
|
||||
// Example: Masking by Edge Attachment
|
||||
// diff()
|
||||
// cube([50,60,70],center=true)
|
||||
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
|
||||
// mask2d_roundover(r=10, inset=2);
|
||||
// mask2d_roundover(h=12, inset=2);
|
||||
// Example: Making an interior fillet
|
||||
// %render() difference() {
|
||||
// move(-[5,0,5]) cube(30, anchor=BOT+LEFT);
|
||||
|
@ -61,8 +73,8 @@
|
|||
// xrot(90)
|
||||
// linear_extrude(height=30, center=true)
|
||||
// mask2d_roundover(r=10);
|
||||
module mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER,spin=0) {
|
||||
path = mask2d_roundover(r=r, d=d, inset=inset, mask_angle=mask_angle, excess=excess);
|
||||
module mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER,spin=0) {
|
||||
path = mask2d_roundover(r=r, d=d, h=h, cut=cut, joint=joint, inset=inset, flat_top=flat_top, mask_angle=mask_angle, excess=excess);
|
||||
default_tag("remove") {
|
||||
attachable(anchor,spin, two_d=true, path=path) {
|
||||
polygon(path);
|
||||
|
@ -71,40 +83,127 @@ module mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER
|
|||
}
|
||||
}
|
||||
|
||||
function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) =
|
||||
assert(is_finite(r)||is_finite(d))
|
||||
function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER, spin=0) =
|
||||
assert(one_defined([r,d,h,cut,joint],"r,d,h,cut,joint"))
|
||||
assert(is_undef(r) || is_finite(r))
|
||||
assert(is_undef(d) || is_finite(d))
|
||||
assert(is_undef(h) || is_finite(h))
|
||||
assert(is_undef(cut) || is_finite(cut))
|
||||
assert(is_undef(joint) || is_finite(joint))
|
||||
assert(is_finite(excess))
|
||||
assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180)
|
||||
assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2))
|
||||
assert(is_bool(flat_top))
|
||||
let(
|
||||
inset = is_list(inset)? inset : [inset,inset],
|
||||
r = get_radius(r=r,d=d,dflt=1),
|
||||
avec = polar_to_xy(inset.x,mask_angle-90),
|
||||
line1 = [[0,inset.y], [100,inset.y]],
|
||||
line2 = [avec, polar_to_xy(100,mask_angle)+avec],
|
||||
corner = line_intersection(line1,line2),
|
||||
arcpts = arc(r=r, corner=[line2.y, corner, line1.y]),
|
||||
ipath = [
|
||||
arcpts[0] + polar_to_xy(inset.x+excess, mask_angle+90),
|
||||
r = is_finite(joint)? adj_ang_to_opp(joint, mask_angle/2) :
|
||||
is_finite(h)? (
|
||||
mask_angle==90? h-inset.y :
|
||||
mask_angle < 90 ? adj_ang_to_opp(opp_ang_to_hyp(h-inset.y,mask_angle), mask_angle/2) :
|
||||
adj_ang_to_opp(adj_ang_to_hyp(h-inset.y,mask_angle-90), mask_angle/2)
|
||||
) :
|
||||
is_finite(cut)
|
||||
? let(
|
||||
o = adj_ang_to_opp(cut, mask_angle/2),
|
||||
h = adj_ang_to_hyp(cut, mask_angle/2)
|
||||
) adj_ang_to_opp(o+h, mask_angle/2)
|
||||
: get_radius(r=r,d=d,dflt=undef),
|
||||
pts = _inset_isect(inset,mask_angle,flat_top,excess,-r),
|
||||
arcpts = arc(r=r, corner=[pts[4],pts[5],pts[0]]),
|
||||
path = [
|
||||
each select(pts, 1, 3),
|
||||
each arcpts,
|
||||
last(arcpts) + polar_to_xy(inset.y+excess, -90),
|
||||
[0,-excess],
|
||||
[-excess,-excess],
|
||||
[-excess,0]
|
||||
],
|
||||
path = deduplicate(ipath)
|
||||
]
|
||||
) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path);
|
||||
|
||||
|
||||
function _inset_isect(inset,mask_angle,flat_top,excess,r,size) =
|
||||
assert(one_defined([size,r],"size,r"))
|
||||
let(
|
||||
lft_n = polar_to_xy(1, mask_angle-90),
|
||||
rgt_n = [1,0],
|
||||
top_n = flat_top? [1,0] : lft_n,
|
||||
bot_n = [0,1],
|
||||
|
||||
line_lft = [[0,0], polar_to_xy(100, mask_angle)],
|
||||
line_bot = [[0,0], [100,0]],
|
||||
ex_line_lft = move(-excess*lft_n, p=line_lft),
|
||||
ex_line_bot = move(-excess*bot_n, p=line_bot),
|
||||
in_line_lft = move(inset.x*top_n, p=line_lft),
|
||||
in_line_bot = move(inset.y*bot_n, p=line_bot),
|
||||
|
||||
ex_pt = line_intersection(ex_line_lft, ex_line_bot),
|
||||
in_pt = line_intersection(in_line_lft, in_line_bot),
|
||||
|
||||
pos_r = r==undef || r >= 0,
|
||||
r = r==undef? undef : abs(r),
|
||||
x = is_undef(size)? r : size.x,
|
||||
y = is_undef(size)? r : size.y,
|
||||
base_pt = !flat_top && is_num(r)? in_pt :
|
||||
in_pt + [y*cos(mask_angle)/sin(mask_angle), 0],
|
||||
line_top = !flat_top && is_num(r)
|
||||
? let( pt = in_pt + polar_to_xy(r/(pos_r?1:tan(mask_angle/2)), mask_angle) )
|
||||
[pt, pt - top_n]
|
||||
: [base_pt + [0,y], base_pt + [0,y] - top_n],
|
||||
line_rgt = !flat_top && is_num(r)
|
||||
? pos_r
|
||||
? [in_pt + [r,0], in_pt + [r,1]]
|
||||
: [in_pt + [r/tan(mask_angle/2),0], in_pt + [r/tan(mask_angle/2),1]]
|
||||
: [base_pt + [x,0], base_pt + [x,1]],
|
||||
top_pt = line_intersection(ex_line_lft, line_top),
|
||||
|
||||
path = is_vector(size)? [
|
||||
// All size based
|
||||
base_pt + [size.x,0],
|
||||
[base_pt.x + size.x, -excess],
|
||||
ex_pt,
|
||||
top_pt,
|
||||
base_pt + [0,size.y],
|
||||
base_pt,
|
||||
base_pt + size,
|
||||
] : flat_top? [
|
||||
// flat_top radius
|
||||
base_pt + [r,0],
|
||||
[base_pt.x + r, -excess],
|
||||
ex_pt,
|
||||
top_pt,
|
||||
base_pt + [0,r],
|
||||
base_pt,
|
||||
base_pt + [r,r],
|
||||
] : let(
|
||||
cp_pt = line_intersection(line_rgt, line_top)
|
||||
) pos_r? [
|
||||
// non-flat_top radius from inside
|
||||
in_pt + [r,0],
|
||||
[in_pt.x + r, -excess],
|
||||
ex_pt,
|
||||
top_pt,
|
||||
in_pt + polar_to_xy(r,mask_angle),
|
||||
in_pt,
|
||||
cp_pt,
|
||||
] : [
|
||||
// non-flat_top radius from outside
|
||||
line_rgt[0],
|
||||
[cp_pt.x, -excess],
|
||||
ex_pt,
|
||||
top_pt,
|
||||
in_pt + polar_to_xy(r/tan(mask_angle/2),mask_angle),
|
||||
in_pt,
|
||||
cp_pt,
|
||||
]
|
||||
) path;
|
||||
|
||||
|
||||
|
||||
// Function&Module: mask2d_cove()
|
||||
// Synopsis: Creates a 2D cove (quarter-round) mask shape.
|
||||
// SynTags: Geom, Path
|
||||
// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
|
||||
// See Also: corner_profile(), edge_profile(), face_profile()
|
||||
// Usage: As module
|
||||
// mask2d_cove(r|d=, [inset], [mask_angle], [excess]) [ATTACHMENTS];
|
||||
// mask2d_cove(r|d=|h=, [inset], [mask_angle], [excess], [flat_top=]) [ATTACHMENTS];
|
||||
// Usage: As function
|
||||
// path = mask2d_cove(r|d=, [inset], [mask_angle], [excess]);
|
||||
// path = mask2d_cove(r|d=|h=, [inset], [mask_angle], [excess], [flat_top=]);
|
||||
// Description:
|
||||
// Creates a 2D cove mask shape that is useful for extruding into a 3D mask for an edge.
|
||||
// Conversely, you can use that same extruded shape to make an interior rounded shelf decoration between two walls.
|
||||
|
@ -117,23 +216,29 @@ function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENT
|
|||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
|
||||
// ---
|
||||
// d = Diameter of the cove.
|
||||
// h = Mask height. Given instead of r or d when you want a consistent mask height, no matter what the mask angle.
|
||||
// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||
// Side Effects:
|
||||
// Tags the children with "remove" (and hence sets `$tag`) if no tag is already set.
|
||||
// Example(2D): 2D Cove Mask
|
||||
// Example(2D): 2D Cove Mask by Radius
|
||||
// mask2d_cove(r=10);
|
||||
// Example(2D): 2D Inset Cove Mask
|
||||
// mask2d_cove(r=10,inset=3);
|
||||
// Example(2D): 2D Inset Cove Mask by Height
|
||||
// mask2d_cove(h=10,inset=2);
|
||||
// Example(2D): 2D Inset Cove Mask for a Non-Right Edge
|
||||
// mask2d_cove(r=10,inset=3,mask_angle=75);
|
||||
// Example(2D): Disabling flat_top=
|
||||
// mask2d_cove(r=10, inset=3, flat_top=false, mask_angle=75);
|
||||
// Example(2D): Increasing the Excess
|
||||
// mask2d_cove(r=10,inset=3,mask_angle=75, excess=2);
|
||||
// Example: Masking by Edge Attachment
|
||||
// diff()
|
||||
// cube([50,60,70],center=true)
|
||||
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
|
||||
// mask2d_cove(r=10, inset=2);
|
||||
// mask2d_cove(h=10, inset=3);
|
||||
// Example: Making an interior rounded shelf
|
||||
// %render() difference() {
|
||||
// move(-[5,0,5]) cube(30, anchor=BOT+LEFT);
|
||||
|
@ -142,8 +247,8 @@ function mask2d_roundover(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENT
|
|||
// xrot(90)
|
||||
// linear_extrude(height=30, center=true)
|
||||
// mask2d_cove(r=5, inset=5);
|
||||
module mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) {
|
||||
path = mask2d_cove(r=r, d=d, inset=inset, mask_angle=mask_angle, excess=excess);
|
||||
module mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, anchor=CENTER, spin=0) {
|
||||
path = mask2d_cove(r=r, d=d, h=h, flat_top=flat_top, inset=inset, mask_angle=mask_angle, excess=excess);
|
||||
default_tag("remove") {
|
||||
attachable(anchor,spin, two_d=true, path=path) {
|
||||
polygon(path);
|
||||
|
@ -152,26 +257,27 @@ module mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spi
|
|||
}
|
||||
}
|
||||
|
||||
function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) =
|
||||
assert(is_finite(r)||is_finite(d))
|
||||
function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, flat_top=true, d, h, anchor=CENTER, spin=0) =
|
||||
assert(one_defined([r,d,h],"r,d,h"))
|
||||
assert(is_undef(r) || is_finite(r))
|
||||
assert(is_undef(d) || is_finite(d))
|
||||
assert(is_undef(h) || is_finite(h))
|
||||
assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180)
|
||||
assert(is_finite(excess))
|
||||
assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2))
|
||||
assert(is_bool(flat_top))
|
||||
let(
|
||||
inset = is_list(inset)? inset : [inset,inset],
|
||||
r = get_radius(r=r,d=d,dflt=1),
|
||||
avec = polar_to_xy(inset.x,mask_angle-90),
|
||||
line1 = [[0,inset.y], [100,inset.y]],
|
||||
line2 = [avec, polar_to_xy(100,mask_angle)+avec],
|
||||
corner = line_intersection(line1,line2),
|
||||
arcpts = arc(r=r, cp=corner, start=mask_angle, angle=-mask_angle),
|
||||
r = is_finite(h)? (
|
||||
mask_angle==90? h-inset.y :
|
||||
mask_angle < 90 ? adj_ang_to_opp(opp_ang_to_hyp(h-inset.y,mask_angle), mask_angle/2) :
|
||||
adj_ang_to_opp(adj_ang_to_hyp(h-inset.y,mask_angle-90), mask_angle/2)
|
||||
) : get_radius(r=r,d=d,dflt=undef),
|
||||
pts = _inset_isect(inset,mask_angle,flat_top,excess,r),
|
||||
arcpts = arc(r=r, corner=[pts[4],pts[6],pts[0]]),
|
||||
ipath = [
|
||||
arcpts[0] + polar_to_xy(inset.x+excess, mask_angle+90),
|
||||
each select(pts, 1, 3),
|
||||
each arcpts,
|
||||
last(arcpts) + polar_to_xy(inset.y+excess, -90),
|
||||
[0,-excess],
|
||||
[-excess,-excess],
|
||||
[-excess,0]
|
||||
],
|
||||
path = deduplicate(ipath)
|
||||
) reorient(anchor,spin, two_d=true, path=path, p=path);
|
||||
|
@ -201,10 +307,12 @@ function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, s
|
|||
// 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
|
||||
// mask_angle = Number of degrees in the corner angle to mask. Default: 90
|
||||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
|
||||
// ---
|
||||
// x = The width of the chamfer.
|
||||
// y = The height of the chamfer.
|
||||
// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||
// Side Effects:
|
||||
|
@ -230,8 +338,8 @@ function mask2d_cove(r, inset=0, mask_angle=90, excess=0.01, d, anchor=CENTER, s
|
|||
// xrot(90)
|
||||
// linear_extrude(height=30, center=true)
|
||||
// mask2d_chamfer(edge=10);
|
||||
module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) {
|
||||
path = mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset);
|
||||
module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, mask_angle=90, flat_top=true, x, y, anchor=CENTER,spin=0) {
|
||||
path = mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset, mask_angle=mask_angle, flat_top=flat_top);
|
||||
default_tag("remove") {
|
||||
attachable(anchor,spin, two_d=true, path=path, extent=true) {
|
||||
polygon(path);
|
||||
|
@ -240,10 +348,11 @@ module mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,
|
|||
}
|
||||
}
|
||||
|
||||
function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTER,spin=0) =
|
||||
function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, mask_angle=90, flat_top=true, x, y, anchor=CENTER,spin=0) =
|
||||
let(dummy=one_defined([x,y,edge],["x","y","edge"]))
|
||||
assert(is_finite(angle))
|
||||
assert(is_finite(excess))
|
||||
assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180)
|
||||
assert(is_finite(inset)||(is_vector(inset)&&len(inset)==2))
|
||||
let(
|
||||
inset = is_list(inset)? inset : [inset,inset],
|
||||
|
@ -251,12 +360,10 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE
|
|||
is_def(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),
|
||||
pts = _inset_isect(inset,mask_angle,flat_top,excess,size=[x,y]),
|
||||
path = [
|
||||
[x+inset.x, -excess],
|
||||
[-excess, -excess],
|
||||
[-excess, y+inset.y],
|
||||
[inset.x, y+inset.y],
|
||||
[x+inset.x, inset.y]
|
||||
each select(pts, 1, 4),
|
||||
pts[0],
|
||||
]
|
||||
) reorient(anchor,spin, two_d=true, path=path, extent=true, p=path);
|
||||
|
||||
|
@ -267,9 +374,9 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE
|
|||
// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable, Masks (2D)
|
||||
// See Also: corner_profile(), edge_profile(), face_profile()
|
||||
// Usage: As Module
|
||||
// mask2d_rabbet(size, [mask_angle], [excess]) [ATTACHMENTS];
|
||||
// mask2d_rabbet(size, [mask_angle], [excess], [flat_top=]) [ATTACHMENTS];
|
||||
// Usage: As Function
|
||||
// path = mask2d_rabbet(size, [mask_angle], [excess]);
|
||||
// path = mask2d_rabbet(size, [mask_angle], [excess], [flat_top=]);
|
||||
// Description:
|
||||
// Creates a 2D rabbet mask shape that is useful for extruding into a 3D mask for an edge.
|
||||
// Conversely, you can use that same extruded shape to make an interior shelf decoration between two walls.
|
||||
|
@ -277,9 +384,11 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE
|
|||
// 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 bead inset size. Default: 0
|
||||
// mask_angle = Number of degrees in the corner angle to mask. Default: 90
|
||||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
|
||||
// ---
|
||||
// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||
// Side Effects:
|
||||
|
@ -289,7 +398,9 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE
|
|||
// Example(2D): 2D Asymmetrical Rabbet Mask
|
||||
// mask2d_rabbet(size=[5,10]);
|
||||
// Example(2D): 2D Mask for a Non-Right Edge
|
||||
// mask2d_rabbet(size=10,mask_angle=75);
|
||||
// mask2d_rabbet(size=10, mask_angle=75);
|
||||
// Example(2D): Disabling flat_top=
|
||||
// mask2d_rabbet(size=10, flat_top=false, mask_angle=75);
|
||||
// Example: Masking by Edge Attachment
|
||||
// diff()
|
||||
// cube([50,60,70],center=true)
|
||||
|
@ -303,8 +414,8 @@ function mask2d_chamfer(edge, angle=45, inset=0, excess=0.01, x, y, anchor=CENTE
|
|||
// xrot(90)
|
||||
// linear_extrude(height=30, center=true)
|
||||
// mask2d_rabbet(size=[5,10]);
|
||||
module mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) {
|
||||
path = mask2d_rabbet(size=size, mask_angle=mask_angle, excess=excess);
|
||||
module mask2d_rabbet(size, inset=[0,0], mask_angle=90, excess=0.01, flat_top=true, anchor=CTR, spin=0) {
|
||||
path = mask2d_rabbet(size=size, inset=inset, mask_angle=mask_angle, excess=excess, flat_top=flat_top);
|
||||
default_tag("remove") {
|
||||
attachable(anchor,spin, two_d=true, path=path, extent=false) {
|
||||
polygon(path);
|
||||
|
@ -313,23 +424,18 @@ module mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) {
|
|||
}
|
||||
}
|
||||
|
||||
function mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) =
|
||||
function mask2d_rabbet(size, inset=[0,0], mask_angle=90, excess=0.01, flat_top=true, anchor=CTR, spin=0) =
|
||||
assert(is_finite(size)||(is_vector(size)&&len(size)==2))
|
||||
assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180)
|
||||
assert(is_finite(excess))
|
||||
assert(is_bool(flat_top))
|
||||
let(
|
||||
size = is_list(size)? size : [size,size],
|
||||
avec = polar_to_xy(size.x,mask_angle-90),
|
||||
line1 = [[0,size.y], [100,size.y]],
|
||||
line2 = [avec, polar_to_xy(100,mask_angle)+avec],
|
||||
cp = line_intersection(line1,line2),
|
||||
pts = _inset_isect(inset,mask_angle,flat_top,excess,size=size),
|
||||
path = [
|
||||
cp + polar_to_xy(size.x+excess, mask_angle+90),
|
||||
cp,
|
||||
cp + polar_to_xy(size.y+excess, -90),
|
||||
[0,-excess],
|
||||
[-excess,-excess],
|
||||
[-excess,0]
|
||||
each select(pts, 1, 4),
|
||||
pts[6],
|
||||
pts[0],
|
||||
]
|
||||
) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path);
|
||||
|
||||
|
@ -353,12 +459,14 @@ function mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) =
|
|||
// Arguments:
|
||||
// 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
|
||||
// inset = Optional amount to inset code from corner. Default: 0
|
||||
// mask_angle = Number of degrees in the corner angle to mask. Default: 90
|
||||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
|
||||
// ---
|
||||
// x = The width of the dovetail.
|
||||
// y = The height of the dovetail.
|
||||
// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||
// Side Effects:
|
||||
|
@ -384,8 +492,8 @@ function mask2d_rabbet(size, mask_angle=90, excess=0.01, anchor=CTR, spin=0) =
|
|||
// xrot(90)
|
||||
// linear_extrude(height=30, center=true)
|
||||
// mask2d_dovetail(x=10);
|
||||
module mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) {
|
||||
path = mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess);
|
||||
module mask2d_dovetail(edge, angle=30, shelf=0, inset=0, mask_angle=90, excess=0.01, flat_top=true, x, y, anchor=CENTER, spin=0) {
|
||||
path = mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess, flat_top=flat_top, mask_angle=mask_angle);
|
||||
default_tag("remove") {
|
||||
attachable(anchor,spin, two_d=true, path=path) {
|
||||
polygon(path);
|
||||
|
@ -394,7 +502,7 @@ module mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anch
|
|||
}
|
||||
}
|
||||
|
||||
function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, anchor=CENTER, spin=0) =
|
||||
function mask2d_dovetail(edge, angle=30, shelf=0, inset=0, mask_angle=90, excess=0.01, flat_top=true, x, y, anchor=CENTER, spin=0) =
|
||||
assert(num_defined([x,y,edge])==1)
|
||||
assert(is_finite(first_defined([x,y,edge])))
|
||||
assert(is_finite(angle))
|
||||
|
@ -406,13 +514,13 @@ function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, an
|
|||
!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),
|
||||
pts = _inset_isect(inset,mask_angle,flat_top,excess,size=[x,y+shelf]),
|
||||
path = [
|
||||
[inset.x,0],
|
||||
[-excess, 0],
|
||||
[-excess, y+inset.y+shelf],
|
||||
inset+[x,y+shelf],
|
||||
inset+[x,y],
|
||||
inset
|
||||
[max(0,pts[5].x),-excess],
|
||||
each select(pts, 2, 4),
|
||||
pts[6],
|
||||
pts[6]-[0,shelf],
|
||||
pts[5],
|
||||
]
|
||||
) reorient(anchor,spin, two_d=true, path=path, p=path);
|
||||
|
||||
|
@ -432,13 +540,20 @@ function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, an
|
|||
// As a 2D mask, this 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.
|
||||
// This is particularly useful to make partially rounded bottoms, that don't need support to print.
|
||||
// The roundover can be specified by radius, diameter, height, cut, or joint length.
|
||||
// ![Types of Roundovers](images/rounding/section-types-of-roundovers_fig1.png)
|
||||
// Arguments:
|
||||
// r = Radius of the rounding.
|
||||
// angle = The maximum angle from vertical.
|
||||
// inset = Optional bead inset size. Default: 0
|
||||
// mask_angle = Number of degrees in the corner angle to mask. Default: 90
|
||||
// excess = Extra amount of mask shape to creates on the X- and Y- sides of the shape. Default: 0.01
|
||||
// ---
|
||||
// d = Diameter of the rounding.
|
||||
// h = Mask height. Given instead of r or d when you want a consistent mask height, no matter what the mask angle.
|
||||
// cut = Cut distance. IE: How much of the corner to cut off. See [Types of Roundovers](rounding.scad#section-types-of-roundovers).
|
||||
// joint = Joint distance. IE: How far from the edge the roundover should start. See [Types of Roundovers](rounding.scad#section-types-of-roundovers).
|
||||
// flat_top = If true, the top inset of the mask will be horizontal instead of angled by the mask_angle. Default: true.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||
// Side Effects:
|
||||
|
@ -464,34 +579,50 @@ function mask2d_dovetail(edge, angle=30, inset=0, shelf=0, excess=0.01, x, y, an
|
|||
// xrot(90)
|
||||
// linear_extrude(height=30, center=true)
|
||||
// mask2d_teardrop(r=10);
|
||||
function mask2d_teardrop(r, angle=45, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) =
|
||||
function mask2d_teardrop(r, angle=45, inset=[0,0], mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER, spin=0) =
|
||||
assert(one_defined([r,d,h,cut,joint],"r,d,h,cut,joint"))
|
||||
assert(is_undef(r) || is_finite(r))
|
||||
assert(is_undef(d) || is_finite(d))
|
||||
assert(is_undef(h) || is_finite(h))
|
||||
assert(is_undef(cut) || is_finite(cut))
|
||||
assert(is_undef(joint) || is_finite(joint))
|
||||
assert(is_finite(angle))
|
||||
assert(angle>0 && angle<90)
|
||||
assert(is_finite(mask_angle) && mask_angle>0 && mask_angle<180)
|
||||
assert(is_finite(excess))
|
||||
let(
|
||||
r = get_radius(r=r, d=d, dflt=1),
|
||||
avec = polar_to_xy(r,mask_angle-90),
|
||||
line1 = [[0,r], [100,r]],
|
||||
line2 = [avec, polar_to_xy(100,mask_angle)+avec],
|
||||
cp = line_intersection(line1,line2),
|
||||
tp = cp + polar_to_xy(r,180+angle),
|
||||
bp = [tp.x+adj_ang_to_opp(tp.y,angle), 0],
|
||||
arcpts = arc(r=r, cp=cp, angle=[mask_angle+90,180+angle]),
|
||||
ipath = [
|
||||
arcpts[0] + polar_to_xy(excess, mask_angle+90),
|
||||
each arcpts,
|
||||
bp,
|
||||
bp + [0,-excess],
|
||||
[0,-excess],
|
||||
[-excess,-excess],
|
||||
[-excess,0]
|
||||
r = is_finite(joint)? adj_ang_to_opp(joint, mask_angle/2) :
|
||||
is_finite(h)? (
|
||||
mask_angle==90? h :
|
||||
mask_angle < 90 ? adj_ang_to_opp(opp_ang_to_hyp(h,mask_angle), mask_angle/2) :
|
||||
adj_ang_to_opp(adj_ang_to_hyp(h,mask_angle-90), mask_angle/2)
|
||||
) :
|
||||
is_finite(cut)
|
||||
? let(
|
||||
o = adj_ang_to_opp(cut, mask_angle/2),
|
||||
h = adj_ang_to_hyp(cut, mask_angle/2)
|
||||
) adj_ang_to_opp(o+h, mask_angle/2)
|
||||
: get_radius(r=r,d=d,dflt=undef),
|
||||
pts = _inset_isect(inset,mask_angle,flat_top,excess,-r),
|
||||
arcpts = arc(r=r, corner=[pts[4],pts[5],pts[0]]),
|
||||
arcpts2 = [
|
||||
for (i = idx(arcpts))
|
||||
if(i==0 || v_theta(arcpts[i]-arcpts[i-1]) <= angle-90)
|
||||
arcpts[i]
|
||||
],
|
||||
path = deduplicate(ipath)
|
||||
line1 = [last(arcpts2), last(arcpts2) + polar_to_xy(1, angle-90)],
|
||||
line2 = [[0,inset.y], [100,inset.y]],
|
||||
ipt = line_intersection(line1,line2),
|
||||
path = [
|
||||
[ipt.x, -excess],
|
||||
each select(pts, 2, 3),
|
||||
each arcpts2,
|
||||
ipt,
|
||||
]
|
||||
) reorient(anchor,spin, two_d=true, path=path, p=path);
|
||||
|
||||
module mask2d_teardrop(r, angle=45, mask_angle=90, excess=0.01, d, anchor=CENTER, spin=0) {
|
||||
path = mask2d_teardrop(r=r, d=d, angle=angle, mask_angle=mask_angle, excess=excess);
|
||||
module mask2d_teardrop(r, angle=45, mask_angle=90, excess=0.01, flat_top=true, d, h, cut, joint, anchor=CENTER, spin=0) {
|
||||
path = mask2d_teardrop(r=r, d=d, h=h, cut=cut, joint=joint, angle=angle, mask_angle=mask_angle, excess=excess);
|
||||
default_tag("remove") {
|
||||
attachable(anchor,spin, two_d=true, path=path) {
|
||||
polygon(path);
|
||||
|
@ -500,6 +631,7 @@ module mask2d_teardrop(r, angle=45, mask_angle=90, excess=0.01, d, anchor=CENTER
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// Function&Module: mask2d_ogee()
|
||||
// Synopsis: Creates a 2D ogee mask shape.
|
||||
// SynTags: Geom, Path
|
||||
|
|
|
@ -574,7 +574,7 @@ function cuboid(
|
|||
// Synopsis: Creates a rectangular prismoid shape with optional roundovers and chamfering.
|
||||
// SynTags: Geom, VNF
|
||||
// Topics: Shapes (3D), Attachable, VNF Generators
|
||||
// See Also: cuboid(), rounded_prism(), trapezoid()
|
||||
// See Also: cuboid(), rounded_prism(), trapezoid(), edge_profile()
|
||||
// Usage:
|
||||
// prismoid(size1, size2, [h|l|height|length], [shift], [xang=], [yang=], ...) [ATTACHMENTS];
|
||||
// Usage: Chamfered and/or Rounded Prismoids
|
||||
|
@ -659,6 +659,13 @@ function cuboid(
|
|||
// chamfer1=[0,5,0,10], chamfer2=[5,0,10,0],
|
||||
// rounding1=[5,0,10,0], rounding2=[0,5,0,10]
|
||||
// );
|
||||
// Example: How to Round a Top or Bottom Edge
|
||||
// diff()
|
||||
// prismoid([50,30], [30,20], shift=[3,6], h=15, rounding=[5,0,5,0]) {
|
||||
// edge_profile([TOP+RIGHT, BOT+FRONT], excess=10, convexity=20) {
|
||||
// mask2d_roundover(h=5,mask_angle=$edge_angle);
|
||||
// }
|
||||
// }
|
||||
// Example(Spin,VPD=160,VPT=[0,0,10]): Standard Connectors
|
||||
// prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5])
|
||||
// show_anchors();
|
||||
|
|
|
@ -1258,9 +1258,10 @@ function spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fa
|
|||
points, col_wrap=true, caps=true, reverse=dir>0,
|
||||
// style=higbee1>0 || higbee2>0 ? "quincunx" : "alt"
|
||||
style="convex"
|
||||
)
|
||||
),
|
||||
vnf2 = vnf_triangulate(vnf)
|
||||
)
|
||||
reorient(anchor,spin,orient, vnf=vnf, r1=r1, r2=r2, l=h, p=vnf);
|
||||
reorient(anchor,spin,orient, vnf=vnf2, r1=r1, r2=r2, l=h, p=vnf2);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -19,60 +19,60 @@ test_mask2d_chamfer();
|
|||
|
||||
module test_mask2d_cove() {
|
||||
$fn = 24;
|
||||
assert_approx(mask2d_cove(r=10),[[-0.01,10],[0,10],[3.09016994375,9.51056516295],[5.87785252292,8.09016994375],[8.09016994375,5.87785252292],[9.51056516295,3.09016994375],[10,0],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_cove(d=20),[[-0.01,10],[0,10],[3.09016994375,9.51056516295],[5.87785252292,8.09016994375],[8.09016994375,5.87785252292],[9.51056516295,3.09016994375],[10,0],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_cove(r=10,inset=1),[[-0.01,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1],[11,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_cove(d=20,inset=1),[[-0.01,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1],[11,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_cove(r=10,inset=1,excess=1),[[-1,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1],[11,-1],[0,-1],[-1,-1],[-1,0]]);
|
||||
assert_approx(mask2d_cove(d=20,inset=1,excess=1),[[-1,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1],[11,-1],[0,-1],[-1,-1],[-1,0]]);
|
||||
assert_approx(mask2d_cove(r=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[1.7763568394e-15,10],[3.09016994375,9.51056516295],[5.87785252292,8.09016994375],[8.09016994375,5.87785252292],[9.51056516295,3.09016994375],[10,1.7763568394e-15]]);
|
||||
assert_approx(mask2d_cove(d=20),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[1.7763568394e-15,10],[3.09016994375,9.51056516295],[5.87785252292,8.09016994375],[8.09016994375,5.87785252292],[9.51056516295,3.09016994375],[10,1.7763568394e-15]]);
|
||||
assert_approx(mask2d_cove(r=10,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]);
|
||||
assert_approx(mask2d_cove(d=20,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]);
|
||||
assert_approx(mask2d_cove(r=10,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]);
|
||||
assert_approx(mask2d_cove(d=20,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[4.09016994375,10.510565163],[6.87785252292,9.09016994375],[9.09016994375,6.87785252292],[10.510565163,4.09016994375],[11,1]]);
|
||||
}
|
||||
test_mask2d_cove();
|
||||
|
||||
|
||||
module test_mask2d_roundover() {
|
||||
$fn = 24;
|
||||
assert_approx(mask2d_roundover(r=10),[[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.90983005625,0.489434837048],[10,-1.7763568394e-15],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_roundover(d=20),[[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.90983005625,0.489434837048],[10,-1.7763568394e-15],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_roundover(r=10,inset=1),[[-0.01,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1],[11,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_roundover(d=20,inset=1),[[-0.01,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1],[11,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_roundover(r=10,inset=1,excess=1),[[-1,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1],[11,-1],[0,-1],[-1,-1],[-1,0]]);
|
||||
assert_approx(mask2d_roundover(d=20,inset=1,excess=1),[[-1,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1],[11,-1],[0,-1],[-1,-1],[-1,0]]);
|
||||
assert_approx(mask2d_roundover(r=10),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.90983005625,0.489434837048],[10,-1.7763568394e-15]]);
|
||||
assert_approx(mask2d_roundover(d=20),[[10,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.90983005625,0.489434837048],[10,-1.7763568394e-15]]);
|
||||
assert_approx(mask2d_roundover(r=10,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]);
|
||||
assert_approx(mask2d_roundover(d=20,inset=1),[[11,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]);
|
||||
assert_approx(mask2d_roundover(r=10,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]);
|
||||
assert_approx(mask2d_roundover(d=20,inset=1,excess=1),[[11,-1],[-1,-1],[-1,11],[1,11],[1.48943483705,7.90983005625],[2.90983005625,5.12214747708],[5.12214747708,2.90983005625],[7.90983005625,1.48943483705],[11,1]]);
|
||||
}
|
||||
test_mask2d_roundover();
|
||||
|
||||
|
||||
module test_mask2d_dovetail() {
|
||||
assert_approx(mask2d_dovetail(x=10),[[0,0],[-0.01,0],[-0.01,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(y=10),[[0,0],[-0.01,0],[-0.01,10],[5.7735026919,10],[5.7735026919,10],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(edge=10),[[0,0],[-0.01,0],[-0.01,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(x=10,angle=30),[[0,0],[-0.01,0],[-0.01,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(y=10,angle=30),[[0,0],[-0.01,0],[-0.01,10],[5.7735026919,10],[5.7735026919,10],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(edge=10,angle=30),[[0,0],[-0.01,0],[-0.01,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(x=10,angle=30,inset=1),[[1,0],[-0.01,0],[-0.01,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]);
|
||||
assert_approx(mask2d_dovetail(y=10,angle=30,inset=1),[[1,0],[-0.01,0],[-0.01,11],[6.7735026919,11],[6.7735026919,11],[1,1]]);
|
||||
assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1),[[1,0],[-0.01,0],[-0.01,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]);
|
||||
assert_approx(mask2d_dovetail(x=10,angle=30,inset=1,excess=1),[[1,0],[-1,0],[-1,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]);
|
||||
assert_approx(mask2d_dovetail(y=10,angle=30,inset=1,excess=1),[[1,0],[-1,0],[-1,11],[6.7735026919,11],[6.7735026919,11],[1,1]]);
|
||||
assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1,excess=1),[[1,0],[-1,0],[-1,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]);
|
||||
assert_approx(mask2d_dovetail(x=10),[[0,-0.01],[-0.01,-0.01],[-0.01,17.3205080757],[0,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(y=10),[[0,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[5.7735026919,10],[5.7735026919,10],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(edge=10),[[0,-0.01],[-0.01,-0.01],[-0.01,8.66025403784],[0,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(x=10,angle=30),[[0,-0.01],[-0.01,-0.01],[-0.01,17.3205080757],[0,17.3205080757],[10,17.3205080757],[10,17.3205080757],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(y=10,angle=30),[[0,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[5.7735026919,10],[5.7735026919,10],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(edge=10,angle=30),[[0,-0.01],[-0.01,-0.01],[-0.01,8.66025403784],[0,8.66025403784],[5,8.66025403784],[5,8.66025403784],[0,0]]);
|
||||
assert_approx(mask2d_dovetail(x=10,angle=30,inset=1),[[1,-0.01],[-0.01,-0.01],[-0.01,18.3205080757],[1,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]);
|
||||
assert_approx(mask2d_dovetail(y=10,angle=30,inset=1),[[1,-0.01],[-0.01,-0.01],[-0.01,11],[1,11],[6.7735026919,11],[6.7735026919,11],[1,1]]);
|
||||
assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1),[[1,-0.01],[-0.01,-0.01],[-0.01,9.66025403784],[1,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]);
|
||||
assert_approx(mask2d_dovetail(x=10,angle=30,inset=1,excess=1),[[1,-1],[-1,-1],[-1,18.3205080757],[1,18.3205080757],[11,18.3205080757],[11,18.3205080757],[1,1]]);
|
||||
assert_approx(mask2d_dovetail(y=10,angle=30,inset=1,excess=1),[[1,-1],[-1,-1],[-1,11],[1,11],[6.7735026919,11],[6.7735026919,11],[1,1]]);
|
||||
assert_approx(mask2d_dovetail(edge=10,angle=30,inset=1,excess=1),[[1,-1],[-1,-1],[-1,9.66025403784],[1,9.66025403784],[6,9.66025403784],[6,9.66025403784],[1,1]]);
|
||||
}
|
||||
test_mask2d_dovetail();
|
||||
|
||||
|
||||
module test_mask2d_rabbet() {
|
||||
assert_approx(mask2d_rabbet(10), [[-0.01,10],[10,10],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_rabbet(size=10), [[-0.01,10],[10,10],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_rabbet(size=[10,15]), [[-0.01,15],[10,15],[10,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_rabbet(size=[10,15],excess=1), [[-1,15],[10,15],[10,-1],[0,-1],[-1,-1],[-1,0]]);
|
||||
assert_approx(mask2d_rabbet(10), [[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[10,10],[10,0]]);
|
||||
assert_approx(mask2d_rabbet(size=10), [[10,-0.01],[-0.01,-0.01],[-0.01,10],[0,10],[10,10],[10,0]]);
|
||||
assert_approx(mask2d_rabbet(size=[10,15]), [[10,-0.01],[-0.01,-0.01],[-0.01,15],[0,15],[10,15],[10,0]]);
|
||||
assert_approx(mask2d_rabbet(size=[10,15],excess=1), [[10,-1],[-1,-1],[-1,15],[0,15],[10,15],[10,0]]);
|
||||
}
|
||||
test_mask2d_rabbet();
|
||||
|
||||
|
||||
module test_mask2d_teardrop() {
|
||||
$fn=24;
|
||||
assert_approx(mask2d_teardrop(r=10), [[-0.01,10],[0,10],[0.761204674887,6.17316567635],[2.92893218813,2.92893218813],[5.85786437627,0],[5.85786437627,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_teardrop(d=20), [[-0.01,10],[0,10],[0.761204674887,6.17316567635],[2.92893218813,2.92893218813],[5.85786437627,0],[5.85786437627,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_teardrop(r=10,angle=30), [[-0.01,10],[0,10],[0.340741737109,7.41180954897],[1.33974596216,5],[4.2264973081,0],[4.2264973081,-0.01],[0,-0.01],[-0.01,-0.01],[-0.01,0]]);
|
||||
assert_approx(mask2d_teardrop(r=10,angle=30,excess=1), [[-1,10],[0,10],[0.340741737109,7.41180954897],[1.33974596216,5],[4.2264973081,0],[4.2264973081,-1],[0,-1],[-1,-1],[-1,0]]);
|
||||
assert_approx(mask2d_teardrop(r=10), [[6.03197753333,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.03197753333,-4.4408920985e-16]]);
|
||||
assert_approx(mask2d_teardrop(d=20), [[6.03197753333,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.12214747708,1.90983005625],[6.03197753333,-4.4408920985e-16]]);
|
||||
assert_approx(mask2d_teardrop(r=10,angle=30), [[4.28975301178,-0.01],[-0.01,-0.01],[-0.01,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.28975301178,0]]);
|
||||
assert_approx(mask2d_teardrop(r=10,angle=30,excess=1), [[4.28975301178,-1],[-1,-1],[-1,10],[-1.7763568394e-15,10],[0.489434837048,6.90983005625],[1.90983005625,4.12214747708],[4.28975301178,0]]);
|
||||
}
|
||||
test_mask2d_teardrop();
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BOSL_VERSION = [2,0,654];
|
||||
BOSL_VERSION = [2,0,655];
|
||||
|
||||
|
||||
// Section: BOSL Library Version Functions
|
||||
|
|
13
vnf.scad
13
vnf.scad
|
@ -33,7 +33,7 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
|
|||
// Topics: VNF Generators, Lists
|
||||
// See Also: vnf_tri_array(), vnf_join(), vnf_from_polygons(), vnf_from_region()
|
||||
// Usage:
|
||||
// vnf = vnf_vertex_array(points, [caps=], [cap1=], [cap2=], [style=], [reverse=], [col_wrap=], [row_wrap=]);
|
||||
// vnf = vnf_vertex_array(points, [caps=], [cap1=], [cap2=], [style=], [reverse=], [col_wrap=], [row_wrap=], [triangulate=]);
|
||||
// Description:
|
||||
// Creates a VNF structure from a rectangular vertex list, by dividing the vertices into columns and rows,
|
||||
// adding faces to tile the surface. You can optionally have faces added to wrap the last column
|
||||
|
@ -55,6 +55,7 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
|
|||
// row_wrap = If true, add faces to connect the last row to the first.
|
||||
// reverse = If true, reverse all face normals.
|
||||
// style = The style of subdividing the quads into faces. Valid options are "default", "alt", "min_edge", "quincunx", "convex" and "concave".
|
||||
// triangulate = If true, triangulates endcaps to resolve possible CGAL issues. This can be an expensive operation if the endcaps are complex. Default: false
|
||||
// Example(3D):
|
||||
// vnf = vnf_vertex_array(
|
||||
// points=[
|
||||
|
@ -131,13 +132,15 @@ function vnf_vertex_array(
|
|||
col_wrap=false,
|
||||
row_wrap=false,
|
||||
reverse=false,
|
||||
style="default"
|
||||
style="default",
|
||||
triangulate = false
|
||||
) =
|
||||
assert(!(any([caps,cap1,cap2]) && !col_wrap), "col_wrap must be true if caps are requested")
|
||||
assert(!(any([caps,cap1,cap2]) && row_wrap), "Cannot combine caps with row_wrap")
|
||||
assert(in_list(style,["default","alt","quincunx", "convex","concave", "min_edge","min_area"]))
|
||||
assert(is_matrix(points[0], n=3),"Point array has the wrong shape or points are not 3d")
|
||||
assert(is_consistent(points), "Non-rectangular or invalid point array")
|
||||
assert(is_bool(triangulate))
|
||||
let(
|
||||
pts = flatten(points),
|
||||
pcnt = len(pts),
|
||||
|
@ -226,9 +229,9 @@ function vnf_vertex_array(
|
|||
rfaces = reverse? [for (face=culled_faces) reverse(face)] : culled_faces
|
||||
)
|
||||
rfaces,
|
||||
]
|
||||
)
|
||||
[verts,allfaces];
|
||||
],
|
||||
vnf = [verts, allfaces]
|
||||
) triangulate? vnf_triangulate(vnf) : vnf;
|
||||
|
||||
|
||||
// Function: vnf_tri_array()
|
||||
|
|
Loading…
Reference in a new issue