mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-03-09 02:39:47 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
3e356c7c34
5 changed files with 178 additions and 233 deletions
46
.github/workflows/main.yml
vendored
46
.github/workflows/main.yml
vendored
|
@ -21,7 +21,7 @@ jobs:
|
||||||
export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE)
|
export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE)
|
||||||
./scripts/run_tests.sh
|
./scripts/run_tests.sh
|
||||||
|
|
||||||
CheckDocs:
|
CheckTutorials:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
@ -55,17 +55,45 @@ jobs:
|
||||||
echo "::add-matcher::.github/check_for_tabs.json"
|
echo "::add-matcher::.github/check_for_tabs.json"
|
||||||
./scripts/check_for_tabs.sh
|
./scripts/check_for_tabs.sh
|
||||||
|
|
||||||
- name: Checking Docs
|
|
||||||
run: |
|
|
||||||
cd $GITHUB_WORKSPACE
|
|
||||||
echo "::add-matcher::.github/openscad_docsgen.json"
|
|
||||||
export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE)
|
|
||||||
openscad-docsgen -Tmf *.scad
|
|
||||||
|
|
||||||
- name: Checking Tutorials
|
- name: Checking Tutorials
|
||||||
run: |
|
run: |
|
||||||
cd $GITHUB_WORKSPACE
|
cd $GITHUB_WORKSPACE
|
||||||
echo "::add-matcher::.github/openscad_docsgen.json"
|
echo "::add-matcher::.github/openscad_docsgen.json"
|
||||||
export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE)
|
export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE)
|
||||||
openscad-mdimggen -T *.scad
|
openscad-mdimggen -T
|
||||||
|
|
||||||
|
CheckDocs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Clone Wiki
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: revarbat/BOSL2.wiki
|
||||||
|
path: BOSL2.wiki
|
||||||
|
|
||||||
|
- name: Apt Update
|
||||||
|
run: sudo apt update
|
||||||
|
|
||||||
|
- name: Install Python dev
|
||||||
|
run: sudo apt-get install python3-pip python3-dev python3-setuptools python3-pil
|
||||||
|
|
||||||
|
- name: Install OpenSCAD-DocsGen package.
|
||||||
|
run: sudo pip3 install openscad-docsgen
|
||||||
|
|
||||||
|
- name: Install OpenSCAD
|
||||||
|
run: |
|
||||||
|
cd $GITHUB_WORKSPACE
|
||||||
|
wget https://files.openscad.org/OpenSCAD-2021.01-x86_64.AppImage
|
||||||
|
sudo mv OpenSCAD-2021.01*-x86_64.AppImage /usr/local/bin/openscad
|
||||||
|
sudo chmod +x /usr/local/bin/openscad
|
||||||
|
|
||||||
|
- name: Checking Docs
|
||||||
|
run: |
|
||||||
|
cd $GITHUB_WORKSPACE
|
||||||
|
echo "::add-matcher::.github/openscad_docsgen.json"
|
||||||
|
export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE)
|
||||||
|
openscad-docsgen -Tmf
|
||||||
|
|
||||||
|
|
161
paths.scad
161
paths.scad
|
@ -689,169 +689,8 @@ function path_torsion(path, closed=false) =
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
// Section: Modifying paths
|
|
||||||
|
|
||||||
// Function: path_chamfer_and_rounding()
|
|
||||||
// Usage:
|
|
||||||
// path2 = path_chamfer_and_rounding(path, [closed], [chamfer], [rounding]);
|
|
||||||
// Description:
|
|
||||||
// Rounds or chamfers corners in the given path.
|
|
||||||
// Arguments:
|
|
||||||
// path = The path to chamfer and/or round.
|
|
||||||
// closed = If true, treat path like a closed polygon. Default: true
|
|
||||||
// chamfer = The length of the chamfer faces at the corners. If given as a list of numbers, gives individual chamfers for each corner, from first to last. Default: 0 (no chamfer)
|
|
||||||
// rounding = The rounding radius for the corners. If given as a list of numbers, gives individual radii for each corner, from first to last. Default: 0 (no rounding)
|
|
||||||
// Example(2D): Chamfering a Path
|
|
||||||
// path = star(5, step=2, d=100);
|
|
||||||
// path2 = path_chamfer_and_rounding(path, closed=true, chamfer=5);
|
|
||||||
// stroke(path2, closed=true);
|
|
||||||
// Example(2D): Per-Corner Chamfering
|
|
||||||
// path = star(5, step=2, d=100);
|
|
||||||
// chamfs = [for (i=[0:1:4]) each 3*[i,i]];
|
|
||||||
// path2 = path_chamfer_and_rounding(path, closed=true, chamfer=chamfs);
|
|
||||||
// stroke(path2, closed=true);
|
|
||||||
// Example(2D): Rounding a Path
|
|
||||||
// path = star(5, step=2, d=100);
|
|
||||||
// path2 = path_chamfer_and_rounding(path, closed=true, rounding=5);
|
|
||||||
// stroke(path2, closed=true);
|
|
||||||
// Example(2D): Per-Corner Chamfering
|
|
||||||
// path = star(5, step=2, d=100);
|
|
||||||
// rs = [for (i=[0:1:4]) each 2*[i,i]];
|
|
||||||
// path2 = path_chamfer_and_rounding(path, closed=true, rounding=rs);
|
|
||||||
// stroke(path2, closed=true);
|
|
||||||
// Example(2D): Mixing Chamfers and Roundings
|
|
||||||
// path = star(5, step=2, d=100);
|
|
||||||
// chamfs = [for (i=[0:4]) each [5,0]];
|
|
||||||
// rs = [for (i=[0:4]) each [0,10]];
|
|
||||||
// path2 = path_chamfer_and_rounding(path, closed=true, chamfer=chamfs, rounding=rs);
|
|
||||||
// stroke(path2, closed=true);
|
|
||||||
function path_chamfer_and_rounding(path, closed=true, chamfer, rounding) =
|
|
||||||
let (
|
|
||||||
p = force_path(path)
|
|
||||||
)
|
|
||||||
assert(is_path(p),"Input 'path' is not a path")
|
|
||||||
let(
|
|
||||||
path = deduplicate(p,closed=true),
|
|
||||||
lp = len(path),
|
|
||||||
chamfer = is_undef(chamfer)? repeat(0,lp) :
|
|
||||||
is_vector(chamfer)? list_pad(chamfer,lp,0) :
|
|
||||||
is_num(chamfer)? repeat(chamfer,lp) :
|
|
||||||
assert(false, "Bad chamfer value."),
|
|
||||||
rounding = is_undef(rounding)? repeat(0,lp) :
|
|
||||||
is_vector(rounding)? list_pad(rounding,lp,0) :
|
|
||||||
is_num(rounding)? repeat(rounding,lp) :
|
|
||||||
assert(false, "Bad rounding value."),
|
|
||||||
|
|
||||||
corner_paths = [
|
|
||||||
for (i=(closed? [0:1:lp-1] : [1:1:lp-2])) let(
|
|
||||||
p1 = select(path,i-1),
|
|
||||||
p2 = select(path,i),
|
|
||||||
p3 = select(path,i+1)
|
|
||||||
)
|
|
||||||
chamfer[i] > 0? _corner_chamfer_path(p1, p2, p3, side=chamfer[i]) :
|
|
||||||
rounding[i] > 0? _corner_roundover_path(p1, p2, p3, r=rounding[i]) :
|
|
||||||
[p2]
|
|
||||||
],
|
|
||||||
out = [
|
|
||||||
if (!closed) path[0],
|
|
||||||
for (i=(closed? [0:1:lp-1] : [1:1:lp-2])) let(
|
|
||||||
p1 = select(path,i-1),
|
|
||||||
p2 = select(path,i),
|
|
||||||
crn1 = select(corner_paths,i-1),
|
|
||||||
crn2 = corner_paths[i],
|
|
||||||
l1 = norm(last(crn1)-p1),
|
|
||||||
l2 = norm(crn2[0]-p2),
|
|
||||||
needed = l1 + l2,
|
|
||||||
seglen = norm(p2-p1),
|
|
||||||
check = assert(seglen >= needed, str("Path segment ",i," is too short to fulfill rounding/chamfering for the adjacent corners."))
|
|
||||||
) each crn2,
|
|
||||||
if (!closed) last(path)
|
|
||||||
]
|
|
||||||
) deduplicate(out);
|
|
||||||
|
|
||||||
|
|
||||||
function _corner_chamfer_path(p1, p2, p3, dist1, dist2, side, angle) =
|
|
||||||
let(
|
|
||||||
v1 = unit(p1 - p2),
|
|
||||||
v2 = unit(p3 - p2),
|
|
||||||
n = vector_axis(v1,v2),
|
|
||||||
ang = vector_angle(v1,v2),
|
|
||||||
path = (is_num(dist1) && is_undef(dist2) && is_undef(side))? (
|
|
||||||
// dist1 & optional angle
|
|
||||||
assert(dist1 > 0)
|
|
||||||
let(angle = default(angle,(180-ang)/2))
|
|
||||||
assert(is_num(angle))
|
|
||||||
assert(angle > 0 && angle < 180)
|
|
||||||
let(
|
|
||||||
pta = p2 + dist1*v1,
|
|
||||||
a3 = 180 - angle - ang
|
|
||||||
) assert(a3>0, "Angle too extreme.")
|
|
||||||
let(
|
|
||||||
side = sin(angle) * dist1/sin(a3),
|
|
||||||
ptb = p2 + side*v2
|
|
||||||
) [pta, ptb]
|
|
||||||
) : (is_undef(dist1) && is_num(dist2) && is_undef(side))? (
|
|
||||||
// dist2 & optional angle
|
|
||||||
assert(dist2 > 0)
|
|
||||||
let(angle = default(angle,(180-ang)/2))
|
|
||||||
assert(is_num(angle))
|
|
||||||
assert(angle > 0 && angle < 180)
|
|
||||||
let(
|
|
||||||
ptb = p2 + dist2*v2,
|
|
||||||
a3 = 180 - angle - ang
|
|
||||||
) assert(a3>0, "Angle too extreme.")
|
|
||||||
let(
|
|
||||||
side = sin(angle) * dist2/sin(a3),
|
|
||||||
pta = p2 + side*v1
|
|
||||||
) [pta, ptb]
|
|
||||||
) : (is_undef(dist1) && is_undef(dist2) && is_num(side))? (
|
|
||||||
// side & optional angle
|
|
||||||
assert(side > 0)
|
|
||||||
let(angle = default(angle,(180-ang)/2))
|
|
||||||
assert(is_num(angle))
|
|
||||||
assert(angle > 0 && angle < 180)
|
|
||||||
let(
|
|
||||||
a3 = 180 - angle - ang
|
|
||||||
) assert(a3>0, "Angle too extreme.")
|
|
||||||
let(
|
|
||||||
dist1 = sin(a3) * side/sin(ang),
|
|
||||||
dist2 = sin(angle) * side/sin(ang),
|
|
||||||
pta = p2 + dist1*v1,
|
|
||||||
ptb = p2 + dist2*v2
|
|
||||||
) [pta, ptb]
|
|
||||||
) : (is_num(dist1) && is_num(dist2) && is_undef(side) && is_undef(side))? (
|
|
||||||
// dist1 & dist2
|
|
||||||
assert(dist1 > 0)
|
|
||||||
assert(dist2 > 0)
|
|
||||||
let(
|
|
||||||
pta = p2 + dist1*v1,
|
|
||||||
ptb = p2 + dist2*v2
|
|
||||||
) [pta, ptb]
|
|
||||||
) : (
|
|
||||||
assert(false,"Bad arguments.")
|
|
||||||
)
|
|
||||||
) path;
|
|
||||||
|
|
||||||
|
|
||||||
function _corner_roundover_path(p1, p2, p3, r, d) =
|
|
||||||
let(
|
|
||||||
r = get_radius(r=r,d=d,dflt=undef),
|
|
||||||
res = circle_2tangents(p1, p2, p3, r=r, tangents=true),
|
|
||||||
cp = res[0],
|
|
||||||
n = res[1],
|
|
||||||
tp1 = res[2],
|
|
||||||
ang = res[4]+res[5],
|
|
||||||
steps = floor(segs(r)*ang/360+0.5),
|
|
||||||
step = ang / steps,
|
|
||||||
path = [for (i=[0:1:steps]) move(cp, p=rot(a=-i*step, v=n, p=tp1-cp))]
|
|
||||||
) path;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Section: Breaking paths up into subpaths
|
// Section: Breaking paths up into subpaths
|
||||||
|
|
||||||
|
|
||||||
/// Internal Function: _path_cut_points()
|
/// Internal Function: _path_cut_points()
|
||||||
///
|
///
|
||||||
/// Usage:
|
/// Usage:
|
||||||
|
|
143
shapes2d.scad
143
shapes2d.scad
|
@ -84,8 +84,8 @@ module square(size=1, center, anchor, spin) {
|
||||||
// When called as a function, returns a 2D path/list of points for a square/rectangle of the given size.
|
// When called as a function, returns a 2D path/list of points for a square/rectangle of the given size.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// size = The size of the rectangle to create. If given as a scalar, both X and Y will be the same size.
|
// size = The size of the rectangle to create. If given as a scalar, both X and Y will be the same size.
|
||||||
// rounding = The rounding radius for the corners. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no rounding)
|
// rounding = The rounding radius for the corners. If negative, produces external roundover spikes on the X axis. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no rounding)
|
||||||
// chamfer = The chamfer size for the corners. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no chamfer)
|
// chamfer = The chamfer size for the corners. If negative, produces external chamfer spikes on the X axis. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no chamfer)
|
||||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
// 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`
|
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||||
// Example(2D):
|
// Example(2D):
|
||||||
|
@ -98,6 +98,10 @@ module square(size=1, center, anchor, spin) {
|
||||||
// rect([40,30], chamfer=5);
|
// rect([40,30], chamfer=5);
|
||||||
// Example(2D): Rounded Rect
|
// Example(2D): Rounded Rect
|
||||||
// rect([40,30], rounding=5);
|
// rect([40,30], rounding=5);
|
||||||
|
// Example(2D): Negative-Chamferred Rect
|
||||||
|
// rect([40,30], chamfer=-5);
|
||||||
|
// Example(2D): Negative-Rounded Rect
|
||||||
|
// rect([40,30], rounding=-5);
|
||||||
// Example(2D): Mixed Chamferring and Rounding
|
// Example(2D): Mixed Chamferring and Rounding
|
||||||
// rect([40,30],rounding=[5,0,10,0],chamfer=[0,8,0,15],$fa=1,$fs=1);
|
// rect([40,30],rounding=[5,0,10,0],chamfer=[0,8,0,15],$fa=1,$fs=1);
|
||||||
// Example(2D): Called as Function
|
// Example(2D): Called as Function
|
||||||
|
@ -145,7 +149,8 @@ function rect(size=1, rounding=0, chamfer=0, anchor=CENTER, spin=0) =
|
||||||
rounding = is_list(rounding)? rounding : [for (i=[0:3]) rounding],
|
rounding = is_list(rounding)? rounding : [for (i=[0:3]) rounding],
|
||||||
quadorder = [3,2,1,0],
|
quadorder = [3,2,1,0],
|
||||||
quadpos = [[1,1],[-1,1],[-1,-1],[1,-1]],
|
quadpos = [[1,1],[-1,1],[-1,-1],[1,-1]],
|
||||||
insets = [for (i=[0:3]) chamfer[i]>0? chamfer[i] : rounding[i]>0? rounding[i] : 0],
|
eps = 1e-9,
|
||||||
|
insets = [for (i=[0:3]) abs(chamfer[i])>=eps? chamfer[i] : abs(rounding[i])>=eps? rounding[i] : 0],
|
||||||
insets_x = max(insets[0]+insets[1],insets[2]+insets[3]),
|
insets_x = max(insets[0]+insets[1],insets[2]+insets[3]),
|
||||||
insets_y = max(insets[0]+insets[3],insets[1]+insets[2])
|
insets_y = max(insets[0]+insets[3],insets[1]+insets[2])
|
||||||
)
|
)
|
||||||
|
@ -156,16 +161,20 @@ function rect(size=1, rounding=0, chamfer=0, anchor=CENTER, spin=0) =
|
||||||
for(i = [0:3])
|
for(i = [0:3])
|
||||||
let(
|
let(
|
||||||
quad = quadorder[i],
|
quad = quadorder[i],
|
||||||
inset = insets[quad],
|
qinset = insets[quad],
|
||||||
cverts = quant(segs(inset),4)/4,
|
qpos = quadpos[quad],
|
||||||
cp = v_mul(size/2-[inset,inset], quadpos[quad]),
|
qchamf = chamfer[quad],
|
||||||
|
qround = rounding[quad],
|
||||||
|
cverts = quant(segs(abs(qinset)),4)/4,
|
||||||
step = 90/cverts,
|
step = 90/cverts,
|
||||||
angs =
|
cp = v_mul(size/2-[qinset,abs(qinset)], qpos),
|
||||||
chamfer[quad] > 0? [0,-90]-90*[i,i] :
|
qpts = abs(qchamf) >= eps? [[0,abs(qinset)], [qinset,0]] :
|
||||||
rounding[quad] > 0? [for (j=[0:1:cverts]) 360-j*step-i*90] :
|
abs(qround) >= eps? [for (j=[0:1:cverts]) let(a=90-j*step) v_mul(polar_to_xy(abs(qinset),a),[sign(qinset),1])] :
|
||||||
[0]
|
[[0,0]],
|
||||||
|
qfpts = [for (p=qpts) v_mul(p,qpos)],
|
||||||
|
qrpts = qpos.x*qpos.y < 0? reverse(qfpts) : qfpts
|
||||||
)
|
)
|
||||||
each [for (a = angs) cp + inset*[cos(a),sin(a)]]
|
each move(cp, p=qrpts)
|
||||||
]
|
]
|
||||||
) complex?
|
) complex?
|
||||||
reorient(anchor,spin, two_d=true, path=path, p=path) :
|
reorient(anchor,spin, two_d=true, path=path, p=path) :
|
||||||
|
@ -785,6 +794,7 @@ module right_triangle(size=[1,1], center, anchor, spin=0) {
|
||||||
// shift = Scalar value to shift the back of the trapezoid along the X axis by. Default: 0
|
// shift = Scalar value to shift the back of the trapezoid along the X axis by. Default: 0
|
||||||
// rounding = The rounding radius for the corners. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no rounding)
|
// rounding = The rounding radius for the corners. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no rounding)
|
||||||
// chamfer = The Length of the chamfer faces at the corners. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no chamfer)
|
// chamfer = The Length of the chamfer faces at the corners. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no chamfer)
|
||||||
|
// flip = If true, negative roundings and chamfers will point forward and back instead of left and right. Default: `false`.
|
||||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
// 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`
|
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||||
// Examples(2D):
|
// Examples(2D):
|
||||||
|
@ -796,15 +806,23 @@ module right_triangle(size=[1,1], center, anchor, spin=0) {
|
||||||
// trapezoid(h=20, w2=10, angle=30);
|
// trapezoid(h=20, w2=10, angle=30);
|
||||||
// trapezoid(h=20, w2=30, angle=-30);
|
// trapezoid(h=20, w2=30, angle=-30);
|
||||||
// trapezoid(w1=30, w2=10, angle=30);
|
// trapezoid(w1=30, w2=10, angle=30);
|
||||||
// Example(2D): Chamferred Trapezoid
|
// Example(2D): Chamfered Trapezoid
|
||||||
// trapezoid(h=30, w1=60, w2=40, chamfer=5);
|
// trapezoid(h=30, w1=60, w2=40, chamfer=5);
|
||||||
|
// Example(2D): Negative Chamfered Trapezoid
|
||||||
|
// trapezoid(h=30, w1=60, w2=40, chamfer=-5);
|
||||||
|
// Example(2D): Flipped Negative Chamfered Trapezoid
|
||||||
|
// trapezoid(h=30, w1=60, w2=40, chamfer=-5, flip=true);
|
||||||
// Example(2D): Rounded Trapezoid
|
// Example(2D): Rounded Trapezoid
|
||||||
// trapezoid(h=30, w1=60, w2=40, rounding=5);
|
// trapezoid(h=30, w1=60, w2=40, rounding=5);
|
||||||
|
// Example(2D): Negative Rounded Trapezoid
|
||||||
|
// trapezoid(h=30, w1=60, w2=40, rounding=-5);
|
||||||
|
// Example(2D): Flipped Negative Rounded Trapezoid
|
||||||
|
// trapezoid(h=30, w1=60, w2=40, rounding=-5, flip=true);
|
||||||
// Example(2D): Mixed Chamfering and Rounding
|
// Example(2D): Mixed Chamfering and Rounding
|
||||||
// trapezoid(h=30, w1=60, w2=40, rounding=[5,0,10,0],chamfer=[0,8,0,15],$fa=1,$fs=1);
|
// trapezoid(h=30, w1=60, w2=40, rounding=[5,0,-10,0],chamfer=[0,8,0,-15],$fa=1,$fs=1);
|
||||||
// Example(2D): Called as Function
|
// Example(2D): Called as Function
|
||||||
// stroke(closed=true, trapezoid(h=30, w1=40, w2=20));
|
// stroke(closed=true, trapezoid(h=30, w1=40, w2=20));
|
||||||
function trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER, spin=0) =
|
function trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, flip=false, anchor=CENTER, spin=0) =
|
||||||
assert(is_undef(h) || is_finite(h))
|
assert(is_undef(h) || is_finite(h))
|
||||||
assert(is_undef(w1) || is_finite(w1))
|
assert(is_undef(w1) || is_finite(w1))
|
||||||
assert(is_undef(w2) || is_finite(w2))
|
assert(is_undef(w2) || is_finite(w2))
|
||||||
|
@ -817,23 +835,65 @@ function trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENT
|
||||||
simple = chamfer==0 && rounding==0,
|
simple = chamfer==0 && rounding==0,
|
||||||
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle)),
|
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle)),
|
||||||
w1 = !is_undef(w1)? w1 : w2 + 2*(adj_ang_to_opp(h, angle) + shift),
|
w1 = !is_undef(w1)? w1 : w2 + 2*(adj_ang_to_opp(h, angle) + shift),
|
||||||
w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift)
|
w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift),
|
||||||
|
chamfs = is_num(chamfer)? [for (i=[0:3]) chamfer] :
|
||||||
|
assert(len(chamfer)==4) chamfer,
|
||||||
|
rounds = is_num(rounding)? [for (i=[0:3]) rounding] :
|
||||||
|
assert(len(rounding)==4) rounding,
|
||||||
|
srads = [for (i=[0:3]) rounds[i]? rounds[i] : chamfs[i]],
|
||||||
|
rads = v_abs(srads)
|
||||||
)
|
)
|
||||||
assert(w1>=0 && w2>=0 && h>0, "Degenerate trapezoid geometry.")
|
assert(w1>=0 && w2>=0 && h>0, "Degenerate trapezoid geometry.")
|
||||||
assert(w1+w2>0, "Degenerate trapezoid geometry.")
|
assert(w1+w2>0, "Degenerate trapezoid geometry.")
|
||||||
let(
|
let(
|
||||||
base_path = [
|
base = [
|
||||||
[w2/2+shift,h/2],
|
[ w2/2+shift, h/2],
|
||||||
[-w2/2+shift,h/2],
|
[-w2/2+shift, h/2],
|
||||||
[-w1/2,-h/2],
|
[-w1/2,-h/2],
|
||||||
[w1/2,-h/2],
|
[ w1/2,-h/2],
|
||||||
],
|
],
|
||||||
cpath = simple? base_path :
|
ang1 = v_theta(base[0]-base[3])-90,
|
||||||
path_chamfer_and_rounding(
|
ang2 = v_theta(base[1]-base[2])-90,
|
||||||
base_path, closed=true,
|
angs = [ang1, ang2, ang2, ang1],
|
||||||
chamfer=chamfer,
|
qdirs = [[1,1], [-1,1], [-1,-1], [1,-1]],
|
||||||
rounding=rounding
|
hyps = [for (i=[0:3]) adj_ang_to_hyp(rads[i],angs[i])],
|
||||||
|
offs = [
|
||||||
|
for (i=[0:3]) let(
|
||||||
|
xoff = adj_ang_to_opp(rads[i],angs[i]),
|
||||||
|
a = [xoff, -rads[i]] * qdirs[i].y * (srads[i]<0 && flip? -1 : 1),
|
||||||
|
b = a + [hyps[i] * qdirs[i].x * (srads[i]<0 && !flip? 1 : -1), 0]
|
||||||
|
) b
|
||||||
|
],
|
||||||
|
cpath = [
|
||||||
|
each (
|
||||||
|
let(i = 0)
|
||||||
|
rads[i] == 0? [base[i]] :
|
||||||
|
srads[i] > 0? arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[angs[i], 90], r=rads[i]) :
|
||||||
|
flip? arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[angs[i],-90], r=rads[i]) :
|
||||||
|
arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[180+angs[i],90], r=rads[i])
|
||||||
),
|
),
|
||||||
|
each (
|
||||||
|
let(i = 1)
|
||||||
|
rads[i] == 0? [base[i]] :
|
||||||
|
srads[i] > 0? arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[90,180+angs[i]], r=rads[i]) :
|
||||||
|
flip? arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[270,180+angs[i]], r=rads[i]) :
|
||||||
|
arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[90,angs[i]], r=rads[i])
|
||||||
|
),
|
||||||
|
each (
|
||||||
|
let(i = 2)
|
||||||
|
rads[i] == 0? [base[i]] :
|
||||||
|
srads[i] > 0? arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[180+angs[i],270], r=rads[i]) :
|
||||||
|
flip? arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[180+angs[i],90], r=rads[i]) :
|
||||||
|
arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[angs[i],-90], r=rads[i])
|
||||||
|
),
|
||||||
|
each (
|
||||||
|
let(i = 3)
|
||||||
|
rads[i] == 0? [base[i]] :
|
||||||
|
srads[i] > 0? arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[-90,angs[i]], r=rads[i]) :
|
||||||
|
flip? arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[90,angs[i]], r=rads[i]) :
|
||||||
|
arc(N=rounds[i]?undef:2, cp=base[i]+offs[i], angle=[270,180+angs[i]], r=rads[i])
|
||||||
|
),
|
||||||
|
],
|
||||||
path = reverse(cpath)
|
path = reverse(cpath)
|
||||||
) simple
|
) simple
|
||||||
? reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift, p=path)
|
? reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift, p=path)
|
||||||
|
@ -841,8 +901,8 @@ function trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENT
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER, spin=0) {
|
module trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, flip=false, anchor=CENTER, spin=0) {
|
||||||
path = trapezoid(h=h, w1=w1, w2=w2, angle=angle, shift=shift, chamfer=chamfer, rounding=rounding);
|
path = trapezoid(h=h, w1=w1, w2=w2, angle=angle, shift=shift, chamfer=chamfer, rounding=rounding, flip=flip);
|
||||||
union() {
|
union() {
|
||||||
simple = chamfer==0 && rounding==0;
|
simple = chamfer==0 && rounding==0;
|
||||||
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle));
|
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle));
|
||||||
|
@ -1103,26 +1163,17 @@ module teardrop2d(r, ang=45, cap_h, d, anchor=CENTER, spin=0)
|
||||||
function teardrop2d(r, ang=45, cap_h, d, anchor=CENTER, spin=0) =
|
function teardrop2d(r, ang=45, cap_h, d, anchor=CENTER, spin=0) =
|
||||||
let(
|
let(
|
||||||
r = get_radius(r=r, d=d, dflt=1),
|
r = get_radius(r=r, d=d, dflt=1),
|
||||||
tanpt = polar_to_xy(r, ang),
|
ang2 = 90-ang,
|
||||||
tip_y = adj_ang_to_hyp(r, 90-ang),
|
prepath = zrot(90, p=circle(r=r)),
|
||||||
cap_h = min(default(cap_h,tip_y), tip_y),
|
eps=1e-9,
|
||||||
cap_w = tanpt.y >= cap_h
|
prepath2 = [for (p=prepath) let(a=atan2(p.y,p.x)) if(a<=90-ang2+eps || a>=90+ang2-eps) p],
|
||||||
? hyp_opp_to_adj(r, cap_h)
|
hyp = is_undef(cap_h)
|
||||||
: adj_ang_to_opp(tip_y-cap_h, ang),
|
? opp_ang_to_hyp(abs(prepath2[0].x), ang)
|
||||||
ang2 = min(ang,atan2(cap_h,cap_w)),
|
: adj_ang_to_hyp(cap_h-prepath2[0].y, ang),
|
||||||
sa = 180 - ang2,
|
p1 = prepath2[0] + polar_to_xy(hyp, 90+ang),
|
||||||
ea = 360 + ang2,
|
p2 = last(prepath2) + polar_to_xy(hyp, 90-ang),
|
||||||
steps = ceil(segs(r)*(ea-sa)/360),
|
path = deduplicate([p1, each prepath2, p2], closed=true)
|
||||||
path = deduplicate(
|
) reorient(anchor,spin, two_d=true, path=path, p=path, extent=false);
|
||||||
[
|
|
||||||
[ cap_w,cap_h],
|
|
||||||
for (a=lerpn(ea,sa,steps+1)) r*[cos(a),sin(a)],
|
|
||||||
[-cap_w,cap_h]
|
|
||||||
], closed=true
|
|
||||||
),
|
|
||||||
maxx_idx = max_index(column(path,0)),
|
|
||||||
path2 = list_rotate(path,maxx_idx)
|
|
||||||
) reorient(anchor,spin, two_d=true, path=path2, p=path2, extent=false);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -94,7 +94,7 @@ function cube(size=1, center, anchor, spin=0, orient=UP) =
|
||||||
// Usage: Chamfered Cubes
|
// Usage: Chamfered Cubes
|
||||||
// cuboid(size, [chamfer=], [edges=], [except=], [trimcorners=], ...);
|
// cuboid(size, [chamfer=], [edges=], [except=], [trimcorners=], ...);
|
||||||
// Usage: Rounded Cubes
|
// Usage: Rounded Cubes
|
||||||
// cuboid(size, [rounding=], [edges=], [except=], [trimcorners=], ...);
|
// cuboid(size, [rounding=], [teardrop=], [edges=], [except=], [trimcorners=], ...);
|
||||||
// Usage: Attaching children
|
// Usage: Attaching children
|
||||||
// cuboid(size, [anchor=], ...) [attachments];
|
// cuboid(size, [anchor=], ...) [attachments];
|
||||||
//
|
//
|
||||||
|
@ -113,6 +113,7 @@ function cube(size=1, center, anchor, spin=0, orient=UP) =
|
||||||
// edges = Edges to mask. See [Specifying Edges](attachments.scad#section-specifying-edges). Default: all edges.
|
// edges = Edges to mask. See [Specifying Edges](attachments.scad#section-specifying-edges). Default: all edges.
|
||||||
// except = Edges to explicitly NOT mask. See [Specifying Edges](attachments.scad#section-specifying-edges). Default: No edges.
|
// except = Edges to explicitly NOT mask. See [Specifying Edges](attachments.scad#section-specifying-edges). Default: No edges.
|
||||||
// trimcorners = If true, rounds or chamfers corners where three chamfered/rounded edges meet. Default: `true`
|
// trimcorners = If true, rounds or chamfers corners where three chamfered/rounded edges meet. Default: `true`
|
||||||
|
// teardrop = If given as a number, rounding around the bottom edge of the cuboid won't exceed this many degrees from vertical. If true, the limit angle is 45 degrees. Default: `false`
|
||||||
// p1 = Align the cuboid's corner at `p1`, if given. Forces `anchor=FRONT+LEFT+BOTTOM`.
|
// p1 = Align the cuboid's corner at `p1`, if given. Forces `anchor=FRONT+LEFT+BOTTOM`.
|
||||||
// p2 = If given with `p1`, defines the cornerpoints of the cuboid.
|
// p2 = If given with `p1`, defines the cornerpoints of the cuboid.
|
||||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
|
||||||
|
@ -132,6 +133,8 @@ function cube(size=1, center, anchor, spin=0, orient=UP) =
|
||||||
// cuboid([30,40,50], chamfer=5, trimcorners=false);
|
// cuboid([30,40,50], chamfer=5, trimcorners=false);
|
||||||
// Example: Rounded Edges and Corners
|
// Example: Rounded Edges and Corners
|
||||||
// cuboid([30,40,50], rounding=10);
|
// cuboid([30,40,50], rounding=10);
|
||||||
|
// Example(VPR=[100,0,25],VPD=180): Rounded Edges and Corners with Teardrop Bottoms
|
||||||
|
// cuboid([30,40,50], rounding=10, teardrop=true);
|
||||||
// Example: Rounded Edges, Untrimmed Corners
|
// Example: Rounded Edges, Untrimmed Corners
|
||||||
// cuboid([30,40,50], rounding=10, trimcorners=false);
|
// cuboid([30,40,50], rounding=10, trimcorners=false);
|
||||||
// Example: Chamferring Selected Edges
|
// Example: Chamferring Selected Edges
|
||||||
|
@ -181,10 +184,32 @@ module cuboid(
|
||||||
except=[],
|
except=[],
|
||||||
except_edges,
|
except_edges,
|
||||||
trimcorners=true,
|
trimcorners=true,
|
||||||
|
teardrop=false,
|
||||||
anchor=CENTER,
|
anchor=CENTER,
|
||||||
spin=0,
|
spin=0,
|
||||||
orient=UP
|
orient=UP
|
||||||
) {
|
) {
|
||||||
|
module xtcyl(l,r) {
|
||||||
|
if (teardrop) {
|
||||||
|
teardrop(r=r, l=l, cap_h=r, ang=teardrop, spin=90, orient=DOWN);
|
||||||
|
} else {
|
||||||
|
yrot(90) cyl(l=l, r=r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module ytcyl(l,r) {
|
||||||
|
if (teardrop) {
|
||||||
|
teardrop(r=r, l=l, cap_h=r, ang=teardrop, spin=0, orient=DOWN);
|
||||||
|
} else {
|
||||||
|
zrot(90) yrot(90) cyl(l=l, r=r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
module tsphere(r) {
|
||||||
|
if (teardrop) {
|
||||||
|
onion(r=r, cap_h=r, ang=teardrop, orient=DOWN);
|
||||||
|
} else {
|
||||||
|
spheroid(r=r, style="octa", orient=DOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
module corner_shape(corner) {
|
module corner_shape(corner) {
|
||||||
e = _corner_edges(edges, corner);
|
e = _corner_edges(edges, corner);
|
||||||
cnt = sum(e);
|
cnt = sum(e);
|
||||||
|
@ -197,33 +222,33 @@ module cuboid(
|
||||||
if (cnt == 0 || approx(r,0)) {
|
if (cnt == 0 || approx(r,0)) {
|
||||||
translate(c2) cube(c, center=true);
|
translate(c2) cube(c, center=true);
|
||||||
} else if (cnt == 1) {
|
} else if (cnt == 1) {
|
||||||
if (e.x) right(c2.x) xcyl(l=c.x, r=r);
|
if (e.x) right(c2.x) xtcyl(l=c.x, r=r);
|
||||||
if (e.y) back (c2.y) ycyl(l=c.y, r=r);
|
if (e.y) back (c2.y) ytcyl(l=c.y, r=r);
|
||||||
if (e.z) up (c2.z) zcyl(l=c.z, r=r);
|
if (e.z) up (c2.z) zcyl(l=c.z, r=r);
|
||||||
} else if (cnt == 2) {
|
} else if (cnt == 2) {
|
||||||
if (!e.x) {
|
if (!e.x) {
|
||||||
intersection() {
|
intersection() {
|
||||||
ycyl(l=c.y*2, r=r);
|
ytcyl(l=c.y*2, r=r);
|
||||||
zcyl(l=c.z*2, r=r);
|
zcyl(l=c.z*2, r=r);
|
||||||
}
|
}
|
||||||
} else if (!e.y) {
|
} else if (!e.y) {
|
||||||
intersection() {
|
intersection() {
|
||||||
xcyl(l=c.x*2, r=r);
|
xtcyl(l=c.x*2, r=r);
|
||||||
zcyl(l=c.z*2, r=r);
|
zcyl(l=c.z*2, r=r);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
intersection() {
|
intersection() {
|
||||||
xcyl(l=c.x*2, r=r);
|
xtcyl(l=c.x*2, r=r);
|
||||||
ycyl(l=c.y*2, r=r);
|
ytcyl(l=c.y*2, r=r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (trimcorners) {
|
if (trimcorners) {
|
||||||
spheroid(r=r, style="octa");
|
tsphere(r=r);
|
||||||
} else {
|
} else {
|
||||||
intersection() {
|
intersection() {
|
||||||
xcyl(l=c.x*2, r=r);
|
xtcyl(l=c.x*2, r=r);
|
||||||
ycyl(l=c.y*2, r=r);
|
ytcyl(l=c.y*2, r=r);
|
||||||
zcyl(l=c.z*2, r=r);
|
zcyl(l=c.z*2, r=r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,6 +258,7 @@ module cuboid(
|
||||||
|
|
||||||
size = scalar_vec3(size);
|
size = scalar_vec3(size);
|
||||||
edges = _edges(edges, except=first_defined([except_edges,except]));
|
edges = _edges(edges, except=first_defined([except_edges,except]));
|
||||||
|
teardrop = is_bool(teardrop)&&teardrop? 45 : teardrop;
|
||||||
chamfer = approx(chamfer,0) ? undef : chamfer;
|
chamfer = approx(chamfer,0) ? undef : chamfer;
|
||||||
rounding = approx(rounding,0) ? undef : rounding;
|
rounding = approx(rounding,0) ? undef : rounding;
|
||||||
assert(is_vector(size,3));
|
assert(is_vector(size,3));
|
||||||
|
@ -240,6 +266,7 @@ module cuboid(
|
||||||
assert(is_undef(chamfer) || is_finite(chamfer),"chamfer must be a finite value");
|
assert(is_undef(chamfer) || is_finite(chamfer),"chamfer must be a finite value");
|
||||||
assert(is_undef(rounding) || is_finite(rounding),"rounding must be a finite value");
|
assert(is_undef(rounding) || is_finite(rounding),"rounding must be a finite value");
|
||||||
assert(is_undef(rounding) || is_undef(chamfer), "Cannot specify nonzero value for both chamfer and rounding");
|
assert(is_undef(rounding) || is_undef(chamfer), "Cannot specify nonzero value for both chamfer and rounding");
|
||||||
|
assert(teardrop==false || (is_finite(teardrop) && teardrop>0 && teardrop<90), "teardrop must be either false or an angle number between 0 and 90")
|
||||||
assert(is_undef(p1) || is_vector(p1));
|
assert(is_undef(p1) || is_vector(p1));
|
||||||
assert(is_undef(p2) || is_vector(p2));
|
assert(is_undef(p2) || is_vector(p2));
|
||||||
assert(is_bool(trimcorners));
|
assert(is_bool(trimcorners));
|
||||||
|
@ -358,12 +385,12 @@ module cuboid(
|
||||||
minkowski() {
|
minkowski() {
|
||||||
cube(isize, center=true);
|
cube(isize, center=true);
|
||||||
if (trimcorners) {
|
if (trimcorners) {
|
||||||
spheroid(r=rounding, style="octa", $fn=sides);
|
tsphere(r=rounding, $fn=sides);
|
||||||
} else {
|
} else {
|
||||||
intersection() {
|
intersection() {
|
||||||
|
xtcyl(r=rounding, l=rounding*2, $fn=sides);
|
||||||
|
ytcyl(r=rounding, l=rounding*2, $fn=sides);
|
||||||
cyl(r=rounding, h=rounding*2, $fn=sides);
|
cyl(r=rounding, h=rounding*2, $fn=sides);
|
||||||
rotate([90,0,0]) cyl(r=rounding, h=rounding*2, $fn=sides);
|
|
||||||
rotate([0,90,0]) cyl(r=rounding, h=rounding*2, $fn=sides);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,10 +90,10 @@ test_octagon();
|
||||||
|
|
||||||
module test_teardrop2d() {
|
module test_teardrop2d() {
|
||||||
$fn=24;
|
$fn=24;
|
||||||
assert_approx(teardrop2d(r=50), [[50,0],[48.2962913145,-12.9409522551],[43.3012701892,-25],[35.3553390593,-35.3553390593],[25,-43.3012701892],[12.9409522551,-48.2962913145],[0,-50],[-12.9409522551,-48.2962913145],[-25,-43.3012701892],[-35.3553390593,-35.3553390593],[-43.3012701892,-25],[-48.2962913145,-12.9409522551],[-50,0],[-48.2962913145,12.9409522551],[-43.3012701892,25],[-35.3553390593,35.3553390593],[0,70.7106781187],[35.3553390593,35.3553390593],[43.3012701892,25],[48.2962913145,12.9409522551]]);
|
assert_approx(teardrop2d(r=50), [[0,70.7106781187],[35.3553390593,35.3553390593],[43.3012701892,25],[48.2962913145,12.9409522551],[50,0],[48.2962913145,-12.9409522551],[43.3012701892,-25],[35.3553390593,-35.3553390593],[25,-43.3012701892],[12.9409522551,-48.2962913145],[0,-50],[-12.9409522551,-48.2962913145],[-25,-43.3012701892],[-35.3553390593,-35.3553390593],[-43.3012701892,-25],[-48.2962913145,-12.9409522551],[-50,0],[-48.2962913145,12.9409522551],[-43.3012701892,25],[-35.3553390593,35.3553390593]]);
|
||||||
assert_approx(teardrop2d(d=100), [[50,0],[48.2962913145,-12.9409522551],[43.3012701892,-25],[35.3553390593,-35.3553390593],[25,-43.3012701892],[12.9409522551,-48.2962913145],[0,-50],[-12.9409522551,-48.2962913145],[-25,-43.3012701892],[-35.3553390593,-35.3553390593],[-43.3012701892,-25],[-48.2962913145,-12.9409522551],[-50,0],[-48.2962913145,12.9409522551],[-43.3012701892,25],[-35.3553390593,35.3553390593],[0,70.7106781187],[35.3553390593,35.3553390593],[43.3012701892,25],[48.2962913145,12.9409522551]]);
|
assert_approx(teardrop2d(d=100), [[0,70.7106781187],[35.3553390593,35.3553390593],[43.3012701892,25],[48.2962913145,12.9409522551],[50,0],[48.2962913145,-12.9409522551],[43.3012701892,-25],[35.3553390593,-35.3553390593],[25,-43.3012701892],[12.9409522551,-48.2962913145],[0,-50],[-12.9409522551,-48.2962913145],[-25,-43.3012701892],[-35.3553390593,-35.3553390593],[-43.3012701892,-25],[-48.2962913145,-12.9409522551],[-50,0],[-48.2962913145,12.9409522551],[-43.3012701892,25],[-35.3553390593,35.3553390593]]);
|
||||||
assert_approx(teardrop2d(r=50,cap_h=50), [[50,0],[48.2962913145,-12.9409522551],[43.3012701892,-25],[35.3553390593,-35.3553390593],[25,-43.3012701892],[12.9409522551,-48.2962913145],[0,-50],[-12.9409522551,-48.2962913145],[-25,-43.3012701892],[-35.3553390593,-35.3553390593],[-43.3012701892,-25],[-48.2962913145,-12.9409522551],[-50,0],[-48.2962913145,12.9409522551],[-43.3012701892,25],[-35.3553390593,35.3553390593],[-20.7106781187,50],[20.7106781187,50],[35.3553390593,35.3553390593],[43.3012701892,25],[48.2962913145,12.9409522551]]);
|
assert_approx(teardrop2d(r=50,cap_h=50), [[20.7106781187,50],[35.3553390593,35.3553390593],[43.3012701892,25],[48.2962913145,12.9409522551],[50,0],[48.2962913145,-12.9409522551],[43.3012701892,-25],[35.3553390593,-35.3553390593],[25,-43.3012701892],[12.9409522551,-48.2962913145],[0,-50],[-12.9409522551,-48.2962913145],[-25,-43.3012701892],[-35.3553390593,-35.3553390593],[-43.3012701892,-25],[-48.2962913145,-12.9409522551],[-50,0],[-48.2962913145,12.9409522551],[-43.3012701892,25],[-35.3553390593,35.3553390593],[-20.7106781187,50]]);
|
||||||
assert_approx(teardrop2d(r=50,cap_h=50,ang=30), [[50,0],[48.2962913145,-12.9409522551],[43.3012701892,-25],[35.3553390593,-35.3553390593],[25,-43.3012701892],[12.9409522551,-48.2962913145],[0,-50],[-12.9409522551,-48.2962913145],[-25,-43.3012701892],[-35.3553390593,-35.3553390593],[-43.3012701892,-25],[-48.2962913145,-12.9409522551],[-50,0],[-48.2962913145,12.9409522551],[-43.3012701892,25],[-28.8675134595,50],[28.8675134595,50],[43.3012701892,25],[48.2962913145,12.9409522551]]);
|
assert_approx(teardrop2d(r=50,cap_h=50,ang=30), [[28.8675134595,50],[43.3012701892,25],[48.2962913145,12.9409522551],[50,0],[48.2962913145,-12.9409522551],[43.3012701892,-25],[35.3553390593,-35.3553390593],[25,-43.3012701892],[12.9409522551,-48.2962913145],[0,-50],[-12.9409522551,-48.2962913145],[-25,-43.3012701892],[-35.3553390593,-35.3553390593],[-43.3012701892,-25],[-48.2962913145,-12.9409522551],[-50,0],[-48.2962913145,12.9409522551],[-43.3012701892,25],[-28.8675134595,50]]);
|
||||||
}
|
}
|
||||||
test_teardrop2d();
|
test_teardrop2d();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue