mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Merge branch 'master' of https://github.com/RonaldoCMP/BOSL2
This commit is contained in:
commit
c010626bc9
40 changed files with 1978 additions and 930 deletions
43
.github/workflows/docsgen.yml
vendored
43
.github/workflows/docsgen.yml
vendored
|
@ -1,12 +1,13 @@
|
|||
name: CI
|
||||
name: Docs
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches: [master]
|
||||
types: [closed]
|
||||
|
||||
jobs:
|
||||
GenerateDocs:
|
||||
runs-on: macos-10.15
|
||||
if: github.event.pull_request.merged == true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
@ -17,28 +18,32 @@ jobs:
|
|||
repository: revarbat/BOSL2.wiki
|
||||
path: BOSL2.wiki
|
||||
|
||||
- name: Install gifsicle
|
||||
run: brew install gifsicle
|
||||
- name: Apt Update
|
||||
run: sudo apt update
|
||||
|
||||
- name: Install Pillow
|
||||
run: sudo pip3 install Pillow
|
||||
- name: Install Packages
|
||||
run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle
|
||||
|
||||
- name: Install Docsgen
|
||||
- name: Install openscad-docsgen
|
||||
run: sudo pip3 install openscad_docsgen
|
||||
|
||||
- name: Install OpenSCAD
|
||||
run: |
|
||||
curl -L -o OpenSCAD.dmg https://files.openscad.org/snapshots/OpenSCAD-2021.05.07.dmg
|
||||
hdiutil attach OpenSCAD.dmg
|
||||
cp -a /Volumes/OpenSCAD/OpenSCAD.app /Applications/
|
||||
|
||||
- name: Generating Docs
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE
|
||||
export OPENSCADPATH=$(dirname $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
|
||||
echo "::add-matcher::.github/openscad_docsgen.json"
|
||||
openscad-docsgen -m -i -t -c -I *.scad
|
||||
cd BOSL2.wiki
|
||||
|
||||
- name: Generating Docs Headless
|
||||
run: |
|
||||
export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE)
|
||||
xvfb-run --server-args="-screen 0, 1280x720x24" -a \
|
||||
openscad-docsgen -ticmI *.scad
|
||||
|
||||
- name: Commit Wiki Docs
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/BOSL2.wiki
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
git add --all
|
||||
|
|
49
.github/workflows/forced_docsgen.yml
vendored
Normal file
49
.github/workflows/forced_docsgen.yml
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
name: FDocs
|
||||
on:
|
||||
workflow_dispatch:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
GenerateDocs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Checkout Wiki
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: revarbat/BOSL2.wiki
|
||||
path: BOSL2.wiki
|
||||
|
||||
- name: Apt Update
|
||||
run: sudo apt update
|
||||
|
||||
- name: Install Packages
|
||||
run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle
|
||||
|
||||
- name: Install openscad-docsgen
|
||||
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
|
||||
echo "::add-matcher::.github/openscad_docsgen.json"
|
||||
|
||||
- name: Generating Docs Headless
|
||||
run: |
|
||||
export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE)
|
||||
xvfb-run --server-args="-screen 0, 1280x720x24" -a \
|
||||
openscad-docsgen -ticmIf *.scad
|
||||
|
||||
- name: Commit Wiki Docs
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/BOSL2.wiki
|
||||
git config user.name github-actions
|
||||
git config user.email github-actions@github.com
|
||||
git add --all
|
||||
git commit -m "Wiki docs auto-regen." && git push || true
|
||||
|
49
.github/workflows/testwf.yml
vendored
Normal file
49
.github/workflows/testwf.yml
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
name: TestWorkflow
|
||||
on:
|
||||
workflow_dispatch:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
TestJob:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Checkout Wiki
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: revarbat/BOSL2.wiki
|
||||
path: BOSL2.wiki
|
||||
|
||||
- name: Setup OpenGL
|
||||
uses: openrndr/setup-opengl@v1.1
|
||||
|
||||
- name: Apt Update
|
||||
run: sudo apt update
|
||||
|
||||
- name: Install Packages
|
||||
run: sudo apt-get install -y python3-pip python3-dev python3-setuptools python3-pil gifsicle
|
||||
|
||||
- name: Install openscad-docsgen
|
||||
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
|
||||
echo "::add-matcher::.github/openscad_docsgen.json"
|
||||
|
||||
- name: Make SCAD File
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE
|
||||
echo "cube(50, center=100);" > testwf.scad
|
||||
|
||||
- name: TestScript
|
||||
run: |
|
||||
export OPENSCADPATH=$(dirname $GITHUB_WORKSPACE)
|
||||
xvfb-run --server-args="-screen 0, 1280x720x24" -a \
|
||||
glxinfo
|
||||
|
|
@ -46,4 +46,5 @@ PrioritizeFiles:
|
|||
DefineHeader(BulletList): Side Effects
|
||||
DefineHeader(Table:Anchor Name|Position): Extra Anchors
|
||||
DefineHeader(Table:Name|Definition): Terminology
|
||||
DefineHeader(BulletList): Requirements
|
||||
|
||||
|
|
165
attachments.scad
165
attachments.scad
|
@ -328,8 +328,11 @@ function attach_geom_size(geom) =
|
|||
[2*maxxr, 2*maxyr,l]
|
||||
) : type == "spheroid"? ( //r
|
||||
let( r=geom[1] )
|
||||
is_num(r)? [2,2,2]*r : vmul([2,2,2],point3d(r))
|
||||
is_num(r)? [2,2,2]*r : v_mul([2,2,2],point3d(r))
|
||||
) : type == "vnf_extent" || type=="vnf_isect"? ( //vnf
|
||||
let(
|
||||
vnf = geom[1]
|
||||
) vnf==EMPTY_VNF? [0,0,0] :
|
||||
let(
|
||||
mm = pointlist_bounds(geom[1][0]),
|
||||
delt = mm[1]-mm[0]
|
||||
|
@ -341,7 +344,7 @@ function attach_geom_size(geom) =
|
|||
) [maxx, size.y]
|
||||
) : type == "circle"? ( //r
|
||||
let( r=geom[1] )
|
||||
is_num(r)? [2,2]*r : vmul([2,2],point2d(r))
|
||||
is_num(r)? [2,2]*r : v_mul([2,2],point2d(r))
|
||||
) : type == "path_isect" || type == "path_extent"? ( //path
|
||||
let(
|
||||
mm = pointlist_bounds(geom[1]),
|
||||
|
@ -425,7 +428,7 @@ function attach_transform(anchor, spin, orient, geom, p) =
|
|||
)
|
||||
)
|
||||
) is_undef(p)? m :
|
||||
is_vnf(p)? [apply(m, p[0]), p[1]] :
|
||||
is_vnf(p)? [(p==EMPTY_VNF? p : apply(m, p[0])), p[1]] :
|
||||
apply(m, p);
|
||||
|
||||
|
||||
|
@ -445,7 +448,8 @@ function attach_transform(anchor, spin, orient, geom, p) =
|
|||
function find_anchor(anchor, geom) =
|
||||
let(
|
||||
cp = select(geom,-3),
|
||||
offset = anchor==CENTER? CENTER : select(geom,-2),
|
||||
offset_raw = select(geom,-2),
|
||||
offset = [for (i=[0:2]) anchor[i]==0? 0 : offset_raw[i]], // prevents bad centering.
|
||||
anchors = last(geom),
|
||||
type = geom[0]
|
||||
)
|
||||
|
@ -472,8 +476,8 @@ function find_anchor(anchor, geom) =
|
|||
h = size.z,
|
||||
u = (anch.z+1)/2,
|
||||
axy = point2d(anch),
|
||||
bot = point3d(vmul(point2d(size)/2,axy),-h/2),
|
||||
top = point3d(vmul(point2d(size2)/2,axy)+shift,h/2),
|
||||
bot = point3d(v_mul(point2d(size)/2,axy),-h/2),
|
||||
top = point3d(v_mul(point2d(size2)/2,axy)+shift,h/2),
|
||||
pos = point3d(cp) + lerp(bot,top,u) + offset,
|
||||
sidevec = unit(rot(from=UP, to=top-bot, p=point3d(axy)),UP),
|
||||
vvec = anch==CENTER? UP : unit([0,0,anch.z],UP),
|
||||
|
@ -493,8 +497,8 @@ function find_anchor(anchor, geom) =
|
|||
anch = rot(from=axis, to=UP, p=anchor),
|
||||
u = (anch.z+1)/2,
|
||||
axy = unit(point2d(anch),[0,0]),
|
||||
bot = point3d(vmul(r1,axy), -l/2),
|
||||
top = point3d(vmul(r2,axy)+shift, l/2),
|
||||
bot = point3d(v_mul(r1,axy), -l/2),
|
||||
top = point3d(v_mul(r2,axy)+shift, l/2),
|
||||
pos = point3d(cp) + lerp(bot,top,u) + offset,
|
||||
sidevec = rot(from=UP, to=top-bot, p=point3d(axy)),
|
||||
vvec = anch==CENTER? UP : unit([0,0,anch.z],UP),
|
||||
|
@ -510,12 +514,14 @@ function find_anchor(anchor, geom) =
|
|||
rr = geom[1],
|
||||
r = is_num(rr)? [rr,rr,rr] : point3d(rr),
|
||||
anchor = unit(point3d(anchor),CENTER),
|
||||
pos = point3d(cp) + vmul(r,anchor) + point3d(offset),
|
||||
vec = unit(vmul(r,anchor),UP)
|
||||
pos = point3d(cp) + v_mul(r,anchor) + point3d(offset),
|
||||
vec = unit(v_mul(r,anchor),UP)
|
||||
) [anchor, pos, vec, oang]
|
||||
) : type == "vnf_isect"? ( //vnf
|
||||
let(
|
||||
vnf=geom[1],
|
||||
vnf=geom[1]
|
||||
) vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] :
|
||||
let(
|
||||
eps = 1/2048,
|
||||
points = vnf[0],
|
||||
faces = vnf[1],
|
||||
|
@ -565,7 +571,9 @@ function find_anchor(anchor, geom) =
|
|||
[anchor, pos, n, oang]
|
||||
) : type == "vnf_extent"? ( //vnf
|
||||
let(
|
||||
vnf=geom[1],
|
||||
vnf=geom[1]
|
||||
) vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] :
|
||||
let(
|
||||
rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), vnf[0]),
|
||||
maxx = max(subindex(rpts,0)),
|
||||
idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i],
|
||||
|
@ -589,8 +597,8 @@ function find_anchor(anchor, geom) =
|
|||
rr = geom[1],
|
||||
r = is_num(rr)? [rr,rr] : point2d(rr),
|
||||
anchor = unit(point2d(anchor),[0,0]),
|
||||
pos = point2d(cp) + vmul(r,anchor) + point2d(offset),
|
||||
vec = unit(vmul(r,anchor),[0,1])
|
||||
pos = point2d(cp) + v_mul(r,anchor) + point2d(offset),
|
||||
vec = unit(v_mul(r,anchor),[0,1])
|
||||
) [anchor, pos, vec, 0]
|
||||
) : type == "path_isect"? ( //path
|
||||
let(
|
||||
|
@ -986,6 +994,92 @@ module attachable(
|
|||
}
|
||||
|
||||
|
||||
// Module: atext()
|
||||
// Topics: Attachments, Text
|
||||
// Usage:
|
||||
// atext(text, <h>, <size>, <font>);
|
||||
// Description:
|
||||
// Creates a 3D text block that can be attached to other attachable objects.
|
||||
// NOTE: This cannot have children attached to it.
|
||||
// Arguments:
|
||||
// text = The text string to instantiate as an object.
|
||||
// h = The height to which the text should be extruded. Default: 1
|
||||
// size = The font size used to create the text block. Default: 10
|
||||
// font = The name of the font used to create the text block. Default: "Courier"
|
||||
// ---
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"baseline"`
|
||||
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP`
|
||||
// See Also: attachable()
|
||||
// Extra Anchors:
|
||||
// "baseline" = Anchors at the baseline of the text, at the start of the string.
|
||||
// str("baseline",VECTOR) = Anchors at the baseline of the text, modified by the X and Z components of the appended vector.
|
||||
// Examples:
|
||||
// atext("Foobar", h=3, size=10);
|
||||
// atext("Foobar", h=2, size=12, font="Helvetica");
|
||||
// atext("Foobar", h=2, anchor=CENTER);
|
||||
// atext("Foobar", h=2, anchor=str("baseline",CENTER));
|
||||
// atext("Foobar", h=2, anchor=str("baseline",BOTTOM+RIGHT));
|
||||
// Example: Using line_of() distributor
|
||||
// txt = "This is the string.";
|
||||
// line_of(spacing=[10,-5],n=len(txt))
|
||||
// atext(txt[$idx], size=10, anchor=CENTER);
|
||||
// Example: Using arc_of() distributor
|
||||
// txt = "This is the string";
|
||||
// arc_of(r=50, n=len(txt), sa=0, ea=180)
|
||||
// atext(select(txt,-1-$idx), size=10, anchor=str("baseline",CENTER), spin=-90);
|
||||
module atext(text, h=1, size=9, font="Courier", anchor="baseline", spin=0, orient=UP) {
|
||||
no_children($children);
|
||||
dummy1 =
|
||||
assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Got: ",anchor))
|
||||
assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Got: ",spin))
|
||||
assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient));
|
||||
anchor = default(anchor, CENTER);
|
||||
spin = default(spin, 0);
|
||||
orient = default(orient, UP);
|
||||
geom = attach_geom(size=[size,size,h]);
|
||||
anch = !any([for (c=anchor) c=="["])? anchor :
|
||||
let(
|
||||
parts = str_split(str_split(str_split(anchor,"]")[0],"[")[1],","),
|
||||
vec = [for (p=parts) str_float(str_strip_leading(p," "))]
|
||||
) vec;
|
||||
ha = anchor=="baseline"? "left" :
|
||||
anchor==anch && is_string(anchor)? "center" :
|
||||
anch.x<0? "left" :
|
||||
anch.x>0? "right" :
|
||||
"center";
|
||||
va = starts_with(anchor,"baseline")? "baseline" :
|
||||
anchor==anch && is_string(anchor)? "center" :
|
||||
anch.y<0? "bottom" :
|
||||
anch.y>0? "top" :
|
||||
"center";
|
||||
base = anchor=="baseline"? CENTER :
|
||||
anchor==anch && is_string(anchor)? CENTER :
|
||||
anch.z<0? BOTTOM :
|
||||
anch.z>0? TOP :
|
||||
CENTER;
|
||||
m = attach_transform(base,spin,orient,geom);
|
||||
multmatrix(m) {
|
||||
$parent_anchor = anchor;
|
||||
$parent_spin = spin;
|
||||
$parent_orient = orient;
|
||||
$parent_geom = geom;
|
||||
$parent_size = attach_geom_size(geom);
|
||||
$attach_to = undef;
|
||||
do_show = attachment_is_shown($tags);
|
||||
if (do_show) {
|
||||
if (is_undef($color)) {
|
||||
linear_extrude(height=h, center=true)
|
||||
text(text=text, size=size, halign=ha, valign=va);
|
||||
} else color($color) {
|
||||
$color = undef;
|
||||
linear_extrude(height=h, center=true)
|
||||
text(text=text, size=size, halign=ha, valign=va);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Section: Attachment Positioning
|
||||
|
||||
|
@ -1057,11 +1151,12 @@ module attach(from, to, overlap, norot=false)
|
|||
$attach_to = to;
|
||||
$attach_anchor = anch;
|
||||
$attach_norot = norot;
|
||||
olap = two_d? [0,-overlap,0] : [0,0,-overlap];
|
||||
if (norot || (norm(anch[2]-UP)<1e-9 && anch[3]==0)) {
|
||||
translate(anch[1]) translate([0,0,-overlap]) children();
|
||||
translate(anch[1]) translate(olap) children();
|
||||
} else {
|
||||
fromvec = two_d? BACK : UP;
|
||||
translate(anch[1]) rot(anch[3],from=fromvec,to=anch[2]) translate([0,0,-overlap]) children();
|
||||
translate(anch[1]) rot(anch[3],from=fromvec,to=anch[2]) translate(olap) children();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1137,10 +1232,10 @@ module edge_profile(edges=EDGES_ALL, except=[], convexity=10) {
|
|||
psize = point3d($parent_size);
|
||||
length = [for (i=[0:2]) if(!vec[i]) psize[i]][0]+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))];
|
||||
vec.z<0? [90,0,180+v_theta(vec)] :
|
||||
vec.z==0 && sign(vec.x)==sign(vec.y)? 135+v_theta(vec) :
|
||||
vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+v_theta(vec)] :
|
||||
[-90,0,180+v_theta(vec)];
|
||||
translate(anch[1]) {
|
||||
rot(rotang) {
|
||||
linear_extrude(height=length, center=true, convexity=convexity) {
|
||||
|
@ -1191,8 +1286,8 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) {
|
|||
$attach_norot = true;
|
||||
$tags = "mask";
|
||||
rotang = vec.z<0?
|
||||
[ 0,0,180+vang(point2d(vec))-45] :
|
||||
[180,0,-90+vang(point2d(vec))-45];
|
||||
[ 0,0,180+v_theta(vec)-45] :
|
||||
[180,0,-90+v_theta(vec)-45];
|
||||
translate(anch[1]) {
|
||||
rot(rotang) {
|
||||
render(convexity=convexity)
|
||||
|
@ -1223,8 +1318,18 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) {
|
|||
// Topics: Attachments
|
||||
// See Also: attachable(), position(), attach(), face_profile(), edge_profile(), corner_mask()
|
||||
// Description:
|
||||
// Takes a 3D mask shape, and attaches it to the given edges, with the appropriate orientation to be `diff()`ed away.
|
||||
// For a more step-by-step explanation of attachments, see the [[Attachments Tutorial|Tutorial-Attachments]].
|
||||
// Takes a 3D mask shape, and attaches it to the given edges, with the appropriate orientation to be
|
||||
// `diff()`ed away. The mask shape should be vertically oriented (Z-aligned) with the back-right
|
||||
// quadrant (X+Y+) shaped to be diffed away from the edge of parent attachable shape. For a more
|
||||
// step-by-step explanation of attachments, see the [[Attachments Tutorial|Tutorial-Attachments]].
|
||||
// Figure: A Typical Edge Rounding Mask
|
||||
// module roundit(l,r) difference() {
|
||||
// translate([-1,-1,-l/2])
|
||||
// cube([r+1,r+1,l]);
|
||||
// translate([r,r])
|
||||
// cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
|
||||
// }
|
||||
// roundit(l=30,r=10);
|
||||
// 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.
|
||||
|
@ -1252,10 +1357,10 @@ module edge_mask(edges=EDGES_ALL, except=[]) {
|
|||
$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))];
|
||||
vec.z<0? [90,0,180+v_theta(vec)] :
|
||||
vec.z==0 && sign(vec.x)==sign(vec.y)? 135+v_theta(vec) :
|
||||
vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+v_theta(vec)] :
|
||||
[-90,0,180+v_theta(vec)];
|
||||
translate(anch[1]) rot(rotang) children();
|
||||
}
|
||||
}
|
||||
|
@ -1296,8 +1401,8 @@ module corner_mask(corners=CORNERS_ALL, except=[]) {
|
|||
$attach_norot = true;
|
||||
$tags = "mask";
|
||||
rotang = vec.z<0?
|
||||
[ 0,0,180+vang(point2d(vec))-45] :
|
||||
[180,0,-90+vang(point2d(vec))-45];
|
||||
[ 0,0,180+v_theta(vec)-45] :
|
||||
[180,0,-90+v_theta(vec)-45];
|
||||
translate(anch[1]) rot(rotang) children();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1461,7 +1461,7 @@ function bezier_patch_flat(size=[100,100], N=4, spin=0, orient=UP, trans=[0,0,0]
|
|||
patch = [
|
||||
for (x=[0:1:N]) [
|
||||
for (y=[0:1:N])
|
||||
vmul(point3d(size), [x/N-0.5, 0.5-y/N, 0])
|
||||
v_mul(point3d(size), [x/N-0.5, 0.5-y/N, 0])
|
||||
]
|
||||
],
|
||||
m = move(trans) * rot(a=spin, from=UP, to=orient)
|
||||
|
|
|
@ -603,9 +603,9 @@ function generic_bottle_cap(
|
|||
) = no_function("generic_bottle_cap");
|
||||
|
||||
|
||||
// Module: thread_adapter_NC()
|
||||
// Module: bottle_adapter_neck_to_cap()
|
||||
// Usage:
|
||||
// thread_adapter_NC(wall, <texture>);
|
||||
// bottle_adapter_neck_to_cap(wall, <texture>);
|
||||
// Description:
|
||||
// Creates a threaded neck to cap adapter
|
||||
// Arguments:
|
||||
|
@ -628,8 +628,8 @@ function generic_bottle_cap(
|
|||
// d = Distance between bottom of neck and top of cap
|
||||
// taper_lead_in = Length to leave straight before tapering on tube between neck and cap if exists.
|
||||
// Examples:
|
||||
// thread_adapter_NC();
|
||||
module thread_adapter_NC(
|
||||
// bottle_adapter_neck_to_cap();
|
||||
module bottle_adapter_neck_to_cap(
|
||||
wall,
|
||||
texture = "none",
|
||||
cap_wall = 2,
|
||||
|
@ -708,17 +708,17 @@ module thread_adapter_NC(
|
|||
}
|
||||
}
|
||||
|
||||
function thread_adapter_NC(
|
||||
function bottle_adapter_neck_to_cap(
|
||||
wall, texture, cap_wall, cap_h, cap_thread_od,
|
||||
tolerance, cap_neck_od, cap_neck_id, cap_thread_taper,
|
||||
cap_thread_pitch, neck_d, neck_id, neck_thread_od,
|
||||
neck_h, neck_thread_pitch, neck_support_od, d, taper_lead_in
|
||||
) = no_fuction("thread_adapter_NC");
|
||||
) = no_fuction("bottle_adapter_neck_to_cap");
|
||||
|
||||
|
||||
// Module: thread_adapter_CC()
|
||||
// Module: bottle_adapter_cap_to_cap()
|
||||
// Usage:
|
||||
// thread_adapter_CC(wall, <texture>);
|
||||
// bottle_adapter_cap_to_cap(wall, <texture>);
|
||||
// Description:
|
||||
// Creates a threaded cap to cap adapter.
|
||||
// Arguments:
|
||||
|
@ -738,8 +738,8 @@ function thread_adapter_NC(
|
|||
// neck_id2 = Inner diameter of cutout in bottom cap.
|
||||
// taper_lead_in = Length to leave straight before tapering on tube between caps if exists.
|
||||
// Examples:
|
||||
// thread_adapter_CC();
|
||||
module thread_adapter_CC(
|
||||
// bottle_adapter_cap_to_cap();
|
||||
module bottle_adapter_cap_to_cap(
|
||||
wall = 2,
|
||||
texture = "none",
|
||||
cap_h1 = 11.2,
|
||||
|
@ -822,16 +822,16 @@ module thread_adapter_CC(
|
|||
}
|
||||
}
|
||||
|
||||
function thread_adapter_CC(
|
||||
function bottle_adapter_cap_to_cap(
|
||||
wall, texture, cap_h1, cap_thread_od1, tolerance,
|
||||
cap_neck_od1, cap_thread_pitch1, cap_h2, cap_thread_od2,
|
||||
cap_neck_od2, cap_thread_pitch2, d, neck_id1, neck_id2, taper_lead_in
|
||||
) = no_function("thread_adapter_CC");
|
||||
) = no_function("bottle_adapter_cap_to_cap");
|
||||
|
||||
|
||||
// Module: thread_adapter_NN()
|
||||
// Module: bottle_adapter_neck_to_neck()
|
||||
// Usage:
|
||||
// thread_adapter_NN();
|
||||
// bottle_adapter_neck_to_neck();
|
||||
// Description:
|
||||
// Creates a threaded neck to neck adapter.
|
||||
// Arguments:
|
||||
|
@ -851,8 +851,8 @@ function thread_adapter_CC(
|
|||
// taper_lead_in = Length to leave straight before tapering on tube between necks if exists.
|
||||
// wall = Thickness of tube wall between necks. Leave undefined to match outer diameters with the neckODs/supportODs.
|
||||
// Examples:
|
||||
// thread_adapter_NN();
|
||||
module thread_adapter_NN(
|
||||
// bottle_adapter_neck_to_neck();
|
||||
module bottle_adapter_neck_to_neck(
|
||||
d = 0,
|
||||
neck_od1 = 25,
|
||||
neck_id1 = 21.4,
|
||||
|
@ -939,12 +939,12 @@ module thread_adapter_NN(
|
|||
}
|
||||
}
|
||||
|
||||
function thread_adapter_NN(
|
||||
function bottle_adapter_neck_to_neck(
|
||||
d, neck_od1, neck_id1, thread_od1, height1,
|
||||
support_od1, thread_pitch1, neck_od2, neck_id2,
|
||||
thread_od2, height2, support_od2,
|
||||
pitch2, taper_lead_in, wall
|
||||
) = no_fuction("thread_adapter_NN");
|
||||
) = no_fuction("bottle_adapter_neck_to_neck");
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -101,6 +101,66 @@ module cubetruss_segment(size, strut, bracing, anchor=CENTER, spin=0, orient=UP)
|
|||
}
|
||||
|
||||
|
||||
// Module: cubetruss_support()
|
||||
// Usage:
|
||||
// cubetruss_support(<size>, <strut>);
|
||||
// Description:
|
||||
// Creates a single cubetruss support.
|
||||
// Arguments:
|
||||
// size = The length of each side of the cubetruss cubes. Default: `$cubetruss_size` (usually 30)
|
||||
// strut = The width of the struts on the cubetruss cubes. Default: `$cubetruss_strut_size` (usually 3)
|
||||
// extents = If given as an integer, specifies the number of vertical segments for the support. If given as a list of 3 integers, specifies the number of segments in the X, Y, and Z directions. Default: 1.
|
||||
// ---
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP`
|
||||
// Topics: Attachable, Trusses
|
||||
// Example(VPT=[0,0,0],VPD=150):
|
||||
// cubetruss_support();
|
||||
// Example(VPT=[0,0,0],VPD=200):
|
||||
// cubetruss_support(extents=2);
|
||||
// Example(VPT=[0,0,0],VPD=250):
|
||||
// cubetruss_support(extents=3);
|
||||
// Example(VPT=[0,0,0],VPD=350):
|
||||
// cubetruss_support(extents=[2,2,3]);
|
||||
// Example(VPT=[0,0,0],VPD=150):
|
||||
// cubetruss_support(strut=4);
|
||||
// Example(VPT=[0,0,0],VPD=260):
|
||||
// cubetruss_support(extents=2) show_anchors();
|
||||
module cubetruss_support(size, strut, extents=1, anchor=CENTER, spin=0, orient=UP) {
|
||||
extents = is_num(extents)? [1,1,extents] : extents;
|
||||
size = is_undef(size)? $cubetruss_size : size;
|
||||
strut = is_undef(strut)? $cubetruss_strut_size : strut;
|
||||
assert(is_int(extents.x) && extents.x > 0);
|
||||
assert(is_int(extents.y) && extents.y > 0);
|
||||
assert(is_int(extents.z) && extents.z > 0);
|
||||
w = (size-strut) * extents.x + strut;
|
||||
l = (size-strut) * extents.y + strut;
|
||||
h = (size-strut) * extents.z + strut;
|
||||
attachable(anchor,spin,orient, size=[w,l,h], size2=[l,0], shift=[0,l/2], axis=DOWN) {
|
||||
xcopies(size-strut, n=extents.x) {
|
||||
difference() {
|
||||
half_of(BACK/extents.y + UP/extents.z, s=size*(max(extents)+1))
|
||||
cube([size,l,h], center=true);
|
||||
half_of(BACK/extents.y + UP/extents.z, cp=strut, s=size*(max(extents)+1)) {
|
||||
ycopies(size-strut, n=extents.y) {
|
||||
zcopies(size-strut, n=extents.z) {
|
||||
cyl(h=size+1, d=size-2*strut, circum=true, realign=true, orient=RIGHT, $fn=8);
|
||||
cyl(h=size+1, d=size-2*strut, circum=true, realign=true, $fn=8);
|
||||
cube(size-2*strut, center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
zcopies(size-strut, n=extents.z) {
|
||||
cyl(h=extents.y*size+1, d=size-2*strut, circum=true, realign=true, orient=BACK, $fn=8);
|
||||
}
|
||||
}
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: cubetruss_clip()
|
||||
// Usage:
|
||||
// cubetruss_clip(extents, <size>, <strut>, <clipthick>);
|
||||
|
@ -370,7 +430,7 @@ module cubetruss_uclip(dual=true, size, strut, clipthick, anchor=CENTER, spin=0,
|
|||
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP`
|
||||
// Topics: Attachable, Trusses
|
||||
// Examples(FlatSpin,VPD=444):
|
||||
// Examples:
|
||||
// cubetruss(extents=3);
|
||||
// cubetruss(extents=3, clips=FRONT);
|
||||
// cubetruss(extents=3, clips=[FRONT,BACK]);
|
||||
|
@ -405,7 +465,7 @@ module cubetruss(extents=6, clips=[], bracing, size, strut, clipthick, anchor=CE
|
|||
}
|
||||
if (clipthick > 0) {
|
||||
for (vec = clips) {
|
||||
exts = vabs(rot(from=FWD, to=vec, p=extents));
|
||||
exts = v_abs(rot(from=FWD, to=vec, p=extents));
|
||||
rot(from=FWD,to=vec) {
|
||||
for (zrow = [0:1:exts.z-1]) {
|
||||
up((zrow-(exts.z-1)/2)*(size-strut)) {
|
||||
|
@ -440,7 +500,7 @@ module cubetruss(extents=6, clips=[], bracing, size, strut, clipthick, anchor=CE
|
|||
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP`
|
||||
// Topics: Attachable, Trusses
|
||||
// Examples(FlatSpin):
|
||||
// Examples:
|
||||
// cubetruss_corner(extents=2);
|
||||
// cubetruss_corner(extents=2, h=2);
|
||||
// cubetruss_corner(extents=[3,3,0,0,2]);
|
||||
|
@ -452,12 +512,12 @@ module cubetruss_corner(h=1, extents=[1,1,0,0,1], bracing, size, strut, clipthic
|
|||
bracing = is_undef(bracing)? $cubetruss_bracing : bracing;
|
||||
clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick;
|
||||
exts = is_vector(extents)? list_fit(extents,5,fill=0) : [extents, extents, 0, 0, extents];
|
||||
s = [cubetruss_dist(1+exts[0]+exts[2],1), cubetruss_dist(1+exts[1]+exts[3],1), cubetruss_dist(h+exts[4],1)];
|
||||
offset = [cubetruss_dist(exts[0]-exts[2],0), cubetruss_dist(exts[1]-exts[3],0), cubetruss_dist(exts[4],0)]/2;
|
||||
s = [cubetruss_dist(exts[0]+1+exts[2],1), cubetruss_dist(exts[1]+1+exts[3],1), cubetruss_dist(h+exts[4],1)];
|
||||
offset = [cubetruss_dist(exts[0]-exts[2],0), cubetruss_dist(exts[1]-exts[3],0), cubetruss_dist(h+exts[4]-1,0)]/2;
|
||||
attachable(anchor,spin,orient, size=s, offset=offset) {
|
||||
union() {
|
||||
for (zcol = [0:h-1]) {
|
||||
up((size-strut+0.01)*zcol) {
|
||||
up((size-strut)*zcol) {
|
||||
cubetruss_segment(size=size, strut=strut, bracing=bracing);
|
||||
}
|
||||
}
|
||||
|
|
17
debug.scad
17
debug.scad
|
@ -387,13 +387,24 @@ module show_anchors(s=10, std=true, custom=true) {
|
|||
color("black")
|
||||
noop($tags="anchor-arrow") {
|
||||
xrot(two_d? 0 : 90) {
|
||||
up(s/10) {
|
||||
linear_extrude(height=0.01, convexity=12, center=true) {
|
||||
text(text=anchor[0], size=s/4, halign="center", valign="center");
|
||||
back(s/3) {
|
||||
yrot_copies(n=2)
|
||||
up(s/30) {
|
||||
linear_extrude(height=0.01, convexity=12, center=true) {
|
||||
text(text=anchor[0], size=s/4, halign="center", valign="center");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
color([1, 1, 1, 0.4])
|
||||
noop($tags="anchor-arrow") {
|
||||
xrot(two_d? 0 : 90) {
|
||||
back(s/3) {
|
||||
zcopies(s/21) cube([s/4.5*len(anchor[0]), s/3, 0.01], center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -520,20 +520,20 @@ module grid2d(spacing, n, size, stagger=false, inside=undef)
|
|||
) :
|
||||
is_vector(spacing)? assert(len(spacing)==2) spacing :
|
||||
size!=undef? (
|
||||
is_num(n)? vdiv(size,(n-1)*[1,1]) :
|
||||
is_vector(n)? assert(len(n)==2) vdiv(size,n-[1,1]) :
|
||||
vdiv(size,(stagger==false? [1,1] : [2,2]))
|
||||
is_num(n)? v_div(size,(n-1)*[1,1]) :
|
||||
is_vector(n)? assert(len(n)==2) v_div(size,n-[1,1]) :
|
||||
v_div(size,(stagger==false? [1,1] : [2,2]))
|
||||
) :
|
||||
undef;
|
||||
n = is_num(n)? [n,n] :
|
||||
is_vector(n)? assert(len(n)==2) n :
|
||||
size!=undef && spacing!=undef? vfloor(vdiv(size,spacing))+[1,1] :
|
||||
size!=undef && spacing!=undef? v_floor(v_div(size,spacing))+[1,1] :
|
||||
[2,2];
|
||||
offset = vmul(spacing, n-[1,1])/2;
|
||||
offset = v_mul(spacing, n-[1,1])/2;
|
||||
if (stagger == false) {
|
||||
for (row = [0:1:n.y-1]) {
|
||||
for (col = [0:1:n.x-1]) {
|
||||
pos = vmul([col,row],spacing) - offset;
|
||||
pos = v_mul([col,row],spacing) - offset;
|
||||
if (
|
||||
is_undef(inside) ||
|
||||
(is_path(inside) && point_in_polygon(pos, inside)>=0) ||
|
||||
|
@ -556,7 +556,7 @@ module grid2d(spacing, n, size, stagger=false, inside=undef)
|
|||
if (rowcols > 0) {
|
||||
for (col = [0:1:rowcols-1]) {
|
||||
rowdx = (row%2 != staggermod)? spacing.x : 0;
|
||||
pos = vmul([2*col,row],spacing) + [rowdx,0] - offset;
|
||||
pos = v_mul([2*col,row],spacing) + [rowdx,0] - offset;
|
||||
if (
|
||||
is_undef(inside) ||
|
||||
(is_path(inside) && point_in_polygon(pos, inside)>=0) ||
|
||||
|
@ -616,7 +616,7 @@ module grid3d(xa=[0], ya=[0], za=[0], n=undef, spacing=undef)
|
|||
for (yi = [0:1:n.y-1]) {
|
||||
for (zi = [0:1:n.z-1]) {
|
||||
$idx = [xi,yi,zi];
|
||||
$pos = vmul(spacing, $idx - (n-[1,1,1])/2);
|
||||
$pos = v_mul(spacing, $idx - (n-[1,1,1])/2);
|
||||
translate($pos) children();
|
||||
}
|
||||
}
|
||||
|
@ -989,7 +989,7 @@ module arc_of(
|
|||
//
|
||||
// Example:
|
||||
// ovoid_spread(n=500, d=100, cone_ang=180)
|
||||
// color(unit(point3d(vabs($pos))))
|
||||
// color(unit(point3d(v_abs($pos))))
|
||||
// cylinder(d=8, h=10, center=false);
|
||||
module ovoid_spread(r=undef, d=undef, n=100, cone_ang=90, scale=[1,1,1], perp=true)
|
||||
{
|
||||
|
@ -1004,7 +1004,7 @@ module ovoid_spread(r=undef, d=undef, n=100, cone_ang=90, scale=[1,1,1], perp=tr
|
|||
for ($idx = idx(theta_phis)) {
|
||||
tp = theta_phis[$idx];
|
||||
xyz = spherical_to_xyz(r, tp[0], tp[1]);
|
||||
$pos = vmul(xyz,point3d(scale,1));
|
||||
$pos = v_mul(xyz,point3d(scale,1));
|
||||
$theta = tp[0];
|
||||
$phi = tp[1];
|
||||
$rad = r;
|
||||
|
|
|
@ -139,7 +139,7 @@ function _edge_set(v) =
|
|||
str(v, " must be a vector, edge array, or one of ", valid_values)
|
||||
) v
|
||||
) :
|
||||
let(nonz = sum(vabs(v)))
|
||||
let(nonz = sum(v_abs(v)))
|
||||
nonz==2? (v==v2) : // Edge: return matching edge.
|
||||
let(
|
||||
matches = count_true([
|
||||
|
|
|
@ -800,7 +800,6 @@ module spur_gear(
|
|||
backlash = backlash,
|
||||
interior = interior
|
||||
);
|
||||
circle(d=shaft_diam+4);
|
||||
}
|
||||
if (shaft_diam > 0) {
|
||||
cylinder(h=2*thickness+1, r=shaft_diam/2, center=true, $fn=max(12,segs(shaft_diam/2)));
|
||||
|
@ -929,7 +928,7 @@ function bevel_gear(
|
|||
radcp = [0, midpr] + polar_to_xy(cutter_radius, 180+spiral_angle),
|
||||
angC1 = law_of_cosines(a=cutter_radius, b=norm(radcp), c=ocone_rad),
|
||||
angC2 = law_of_cosines(a=cutter_radius, b=norm(radcp), c=icone_rad),
|
||||
radcpang = vang(radcp),
|
||||
radcpang = v_theta(radcp),
|
||||
sang = radcpang - (180-angC1),
|
||||
eang = radcpang - (180-angC2),
|
||||
profile = gear_tooth_profile(
|
||||
|
@ -945,7 +944,7 @@ function bevel_gear(
|
|||
verts1 = [
|
||||
for (v = lerpn(0,1,slices+1)) let(
|
||||
p = radcp + polar_to_xy(cutter_radius, lerp(sang,eang,v)),
|
||||
ang = vang(p)-90,
|
||||
ang = v_theta(p)-90,
|
||||
dist = norm(p)
|
||||
) [
|
||||
let(
|
||||
|
|
252
geometry.scad
252
geometry.scad
|
@ -1,4 +1,4 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// LibFile: geometry.scad
|
||||
// Geometry helpers.
|
||||
// Includes:
|
||||
|
@ -2237,4 +2237,254 @@ function split_polygons_at_each_z(polys, zs, _i=0) =
|
|||
);
|
||||
|
||||
|
||||
// Section: Convex Sets
|
||||
|
||||
// Function: is_convex_polygon()
|
||||
// Usage:
|
||||
// is_convex_polygon(poly);
|
||||
// Description:
|
||||
// Returns true if the given 2D or 3D polygon is convex.
|
||||
// The result is meaningless if the polygon is not simple (self-intersecting) or non coplanar.
|
||||
// If the points are collinear an error is generated.
|
||||
// Arguments:
|
||||
// poly = Polygon to check.
|
||||
// eps = Tolerance for the collinearity test. Default: EPSILON.
|
||||
// Example:
|
||||
// is_convex_polygon(circle(d=50)); // Returns: true
|
||||
// is_convex_polygon(rot([50,120,30], p=path3d(circle(1,$fn=50)))); // Returns: true
|
||||
// Example:
|
||||
// spiral = [for (i=[0:36]) let(a=-i*10) (10+i)*[cos(a),sin(a)]];
|
||||
// is_convex_polygon(spiral); // Returns: false
|
||||
function is_convex_polygon(poly,eps=EPSILON) =
|
||||
assert(is_path(poly), "The input should be a 2D or 3D polygon." )
|
||||
let( lp = len(poly) )
|
||||
assert( lp>=3 , "A polygon must have at least 3 points" )
|
||||
let( crosses = [for(i=[0:1:lp-1]) cross(poly[(i+1)%lp]-poly[i], poly[(i+2)%lp]-poly[(i+1)%lp]) ] )
|
||||
len(poly[0])==2
|
||||
? assert( max(max(crosses),-min(crosses))>eps, "The points are collinear" )
|
||||
min(crosses) >=0 || max(crosses)<=0
|
||||
: let( prod = crosses*sum(crosses),
|
||||
minc = min(prod),
|
||||
maxc = max(prod) )
|
||||
assert( max(maxc,-minc)>eps, "The points are collinear" )
|
||||
minc>=0 || maxc<=0;
|
||||
|
||||
|
||||
// Function: convex_distance()
|
||||
// Usage:
|
||||
// convex_distance(pts1, pts2,<eps=>);
|
||||
// See also:
|
||||
// convex_collision
|
||||
// Description:
|
||||
// Returns the smallest distance between a point in convex hull of `points1`
|
||||
// and a point in the convex hull of `points2`. All the points in the lists
|
||||
// should have the same dimension, either 2D or 3D.
|
||||
// A zero result means the hulls intercept whithin a tolerance `eps`.
|
||||
// Arguments:
|
||||
// points1 - first list of 2d or 3d points.
|
||||
// points2 - second list of 2d or 3d points.
|
||||
// eps - tolerance in distance evaluations. Default: EPSILON.
|
||||
// Example(2D):
|
||||
// pts1 = move([-3,0], p=square(3,center=true));
|
||||
// pts2 = rot(a=45, p=square(2,center=true));
|
||||
// pts3 = [ [2,0], [1,2],[3,2], [3,-2], [1,-2] ];
|
||||
// polygon(pts1);
|
||||
// polygon(pts2);
|
||||
// polygon(pts3);
|
||||
// echo(convex_distance(pts1,pts2)); // Returns: 0.0857864
|
||||
// echo(convex_distance(pts2,pts3)); // Returns: 0
|
||||
// Example(3D):
|
||||
// sphr1 = sphere(2,$fn=10);
|
||||
// sphr2 = move([4,0,0], p=sphr1);
|
||||
// sphr3 = move([4.5,0,0], p=sphr1);
|
||||
// vnf_polyhedron(sphr1);
|
||||
// vnf_polyhedron(sphr2);
|
||||
// echo(convex_distance(sphr1[0], sphr2[0])); // Returns: 0
|
||||
// echo(convex_distance(sphr1[0], sphr3[0])); // Returns: 0.5
|
||||
function convex_distance(points1, points2, eps=EPSILON) =
|
||||
assert(is_matrix(points1) && is_matrix(points2,undef,len(points1[0])),
|
||||
"The input list should be a consistent non empty list of points of same dimension.")
|
||||
assert(len(points1[0])==2 || len(points1[0])==3 ,
|
||||
"The input points should be 2d or 3d points.")
|
||||
let( d = points1[0]-points2[0] )
|
||||
norm(d)<eps ? 0 :
|
||||
let( v = _support_diff(points1,points2,-d) )
|
||||
norm(_GJK_distance(points1, points2, eps, 0, v, [v]));
|
||||
|
||||
|
||||
// Finds the vector difference between the hulls of the two pointsets by the GJK algorithm
|
||||
// Based on:
|
||||
// http://www.dtecta.com/papers/jgt98convex.pdf
|
||||
function _GJK_distance(points1, points2, eps=EPSILON, lbd, d, simplex=[]) =
|
||||
let( nrd = norm(d) ) // distance upper bound
|
||||
nrd<eps ? d :
|
||||
let(
|
||||
v = _support_diff(points1,points2,-d),
|
||||
lbd = max(lbd, d*v/nrd), // distance lower bound
|
||||
close = (nrd-lbd <= eps*nrd)
|
||||
)
|
||||
// v already in the simplex is a degenerence due to numerical errors
|
||||
// and may produce a non-stopping loop
|
||||
close || [for(nv=norm(v), s=simplex) if(norm(s-v)<=eps*nv) 1]!=[] ? d :
|
||||
let( newsplx = _closest_simplex(concat(simplex,[v]),eps) )
|
||||
_GJK_distance(points1, points2, eps, lbd, newsplx[0], newsplx[1]);
|
||||
|
||||
|
||||
// Function: convex_collision()
|
||||
// Usage:
|
||||
// convex_collision(pts1, pts2,<eps=>);
|
||||
// See also:
|
||||
// convex_distance
|
||||
// Description:
|
||||
// Returns `true` if the convex hull of `points1` intercepts the convex hull of `points2`
|
||||
// otherwise, `false`.
|
||||
// All the points in the lists should have the same dimension, either 2D or 3D.
|
||||
// This function is tipically faster than `convex_distance` to find a non-collision.
|
||||
// Arguments:
|
||||
// points1 - first list of 2d or 3d points.
|
||||
// points2 - second list of 2d or 3d points.
|
||||
// eps - tolerance for the intersection tests. Default: EPSILON.
|
||||
// Example(2D):
|
||||
// pts1 = move([-3,0], p=square(3,center=true));
|
||||
// pts2 = rot(a=45, p=square(2,center=true));
|
||||
// pts3 = [ [2,0], [1,2],[3,2], [3,-2], [1,-2] ];
|
||||
// polygon(pts1);
|
||||
// polygon(pts2);
|
||||
// polygon(pts3);
|
||||
// echo(convex_collision(pts1,pts2)); // Returns: false
|
||||
// echo(convex_collision(pts2,pts3)); // Returns: true
|
||||
// Example(3D):
|
||||
// sphr1 = sphere(2,$fn=10);
|
||||
// sphr2 = move([4,0,0], p=sphr1);
|
||||
// sphr3 = move([4.5,0,0], p=sphr1);
|
||||
// vnf_polyhedron(sphr1);
|
||||
// vnf_polyhedron(sphr2);
|
||||
// echo(convex_collision(sphr1[0], sphr2[0])); // Returns: true
|
||||
// echo(convex_collision(sphr1[0], sphr3[0])); // Returns: false
|
||||
//
|
||||
function convex_collision(points1, points2, eps=EPSILON) =
|
||||
assert(is_matrix(points1) && is_matrix(points2,undef,len(points1[0])),
|
||||
"The input list should be a consistent non empty list of points of same dimension.")
|
||||
assert(len(points1[0])==2 || len(points1[0])==3 ,
|
||||
"The input points should be 2d or 3d points.")
|
||||
let( d = points1[0]-points2[0] )
|
||||
norm(d)<eps ? true :
|
||||
let( v = _support_diff(points1,points2,-d) )
|
||||
_GJK_collide(points1, points2, v, [v], eps);
|
||||
|
||||
|
||||
// Based on the GJK collision algorithms found in:
|
||||
// http://uu.diva-portal.org/smash/get/diva2/FFULLTEXT01.pdf
|
||||
// or
|
||||
// http://www.dtecta.com/papers/jgt98convex.pdf
|
||||
function _GJK_collide(points1, points2, d, simplex, eps=EPSILON) =
|
||||
norm(d) < eps ? true : // does collide
|
||||
let( v = _support_diff(points1,points2,-d) )
|
||||
v*d > eps ? false : // no collision
|
||||
let( newsplx = _closest_simplex(concat(simplex,[v]),eps) )
|
||||
_GJK_collide(points1, points2, newsplx[0], newsplx[1], eps);
|
||||
|
||||
|
||||
// given a simplex s, returns a pair:
|
||||
// - the point of the s closest to the origin
|
||||
// - the smallest sub-simplex of s that contains that point
|
||||
function _closest_simplex(s,eps=EPSILON) =
|
||||
assert(len(s)>=2 && len(s)<=4, "Internal error.")
|
||||
len(s)==2 ? _closest_s1(s,eps) :
|
||||
len(s)==3 ? _closest_s2(s,eps)
|
||||
: _closest_s3(s,eps);
|
||||
|
||||
|
||||
// find the closest to a 1-simplex
|
||||
// Based on: http://uu.diva-portal.org/smash/get/diva2/FFULLTEXT01.pdf
|
||||
function _closest_s1(s,eps=EPSILON) =
|
||||
norm(s[1]-s[0])<eps*(norm(s[0])+norm(s[1]))/2 ? [ s[0], [s[0]] ] :
|
||||
let(
|
||||
c = s[1]-s[0],
|
||||
t = -s[0]*c/(c*c)
|
||||
)
|
||||
t<0 ? [ s[0], [s[0]] ] :
|
||||
t>1 ? [ s[1], [s[1]] ] :
|
||||
[ s[0]+t*c, s ];
|
||||
|
||||
|
||||
// find the closest to a 2-simplex
|
||||
// Based on: http://uu.diva-portal.org/smash/get/diva2/FFULLTEXT01.pdf
|
||||
function _closest_s2(s,eps=EPSILON) =
|
||||
let(
|
||||
dim = len(s[0]),
|
||||
a = dim==3 ? s[0]: [ each s[0], 0] ,
|
||||
b = dim==3 ? s[1]: [ each s[1], 0] ,
|
||||
c = dim==3 ? s[2]: [ each s[2], 0] ,
|
||||
ab = norm(a-b),
|
||||
bc = norm(b-c),
|
||||
ca = norm(c-a),
|
||||
nr = cross(b-a,c-a)
|
||||
)
|
||||
norm(nr) <= eps*max(ab,bc,ca) // degenerate case
|
||||
? let( i = max_index([ab, bc, ca]) )
|
||||
_closest_s1([s[i],s[(i+1)%3]],eps)
|
||||
// considering that s[2] was the last inserted vertex in s,
|
||||
// the only possible outcomes are :
|
||||
// s, [s[0],s[2]] and [s[1],s[2]]
|
||||
: let(
|
||||
class = (cross(nr,a-b)*a<0 ? 1 : 0 )
|
||||
+ (cross(nr,c-a)*a<0 ? 2 : 0 )
|
||||
+ (cross(nr,b-c)*b<0 ? 4 : 0 )
|
||||
)
|
||||
assert( class!=1, "Internal error" )
|
||||
class==0 ? [ nr*(nr*a)/(nr*nr), s] : // origin projects (or is) on the tri
|
||||
// class==1 ? _closest_s1([s[0],s[1]]) :
|
||||
class==2 ? _closest_s1([s[0],s[2]],eps) :
|
||||
class==4 ? _closest_s1([s[1],s[2]],eps) :
|
||||
// class==3 ? a*(a-b)> 0 ? _closest_s1([s[0],s[1]]) : _closest_s1([s[0],s[2]]) :
|
||||
class==3 ? _closest_s1([s[0],s[2]],eps) :
|
||||
// class==5 ? b*(b-c)<=0 ? _closest_s1([s[0],s[1]]) : _closest_s1([s[1],s[2]]) :
|
||||
class==5 ? _closest_s1([s[1],s[2]],eps) :
|
||||
c*(c-a)>0 ? _closest_s1([s[0],s[2]],eps) : _closest_s1([s[1],s[2]],eps);
|
||||
|
||||
|
||||
// find the closest to a 3-simplex
|
||||
// it seems that degenerate 3-simplices are correctly manage without extra code
|
||||
function _closest_s3(s,eps=EPSILON) =
|
||||
assert( len(s[0])==3 && len(s)==4, "Internal error." )
|
||||
let( nr = cross(s[1]-s[0],s[2]-s[0]),
|
||||
sz = [ norm(s[1]-s[0]), norm(s[1]-s[2]), norm(s[2]-s[0]) ] )
|
||||
norm(nr)<eps*max(sz)
|
||||
? let( i = max_index(sz) )
|
||||
_closest_s2([ s[i], s[(i+1)%3], s[3] ], eps) // degenerate case
|
||||
// considering that s[3] was the last inserted vertex in s,
|
||||
// the only possible outcomes will be:
|
||||
// s or some of the 3 triangles of s containing s[3]
|
||||
: let(
|
||||
tris = [ [s[0], s[1], s[3]],
|
||||
[s[1], s[2], s[3]],
|
||||
[s[2], s[0], s[3]] ],
|
||||
cntr = sum(s)/4,
|
||||
// indicator of the tris facing the origin
|
||||
facing = [for(i=[0:2])
|
||||
let( nrm = _tri_normal(tris[i]) )
|
||||
if( ((nrm*(s[i]-cntr))>0)==(nrm*s[i]<0) ) i ]
|
||||
)
|
||||
len(facing)==0 ? [ [0,0,0], s ] : // origin is inside the simplex
|
||||
len(facing)==1 ? _closest_s2(tris[facing[0]], eps) :
|
||||
let( // look for the origin-facing tri closest to the origin
|
||||
closest = [for(i=facing) _closest_s2(tris[i], eps) ],
|
||||
dist = [for(cl=closest) norm(cl[0]) ],
|
||||
nearest = min_index(dist)
|
||||
)
|
||||
closest[nearest];
|
||||
|
||||
|
||||
function _tri_normal(tri) = cross(tri[1]-tri[0],tri[2]-tri[0]);
|
||||
|
||||
|
||||
function _support_diff(p1,p2,d) =
|
||||
let( p1d = p1*d, p2d = p2*d )
|
||||
p1[search(max(p1d),p1d,1)[0]] - p2[search(min(p2d),p2d,1)[0]];
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
109
math.scad
109
math.scad
|
@ -237,7 +237,7 @@ function u_sub(a,b) = is_undef(a) || is_undef(b)? undef : a - b;
|
|||
// b = Second value.
|
||||
function u_mul(a,b) =
|
||||
is_undef(a) || is_undef(b)? undef :
|
||||
is_vector(a) && is_vector(b)? vmul(a,b) :
|
||||
is_vector(a) && is_vector(b)? v_mul(a,b) :
|
||||
a * b;
|
||||
|
||||
|
||||
|
@ -252,7 +252,7 @@ function u_mul(a,b) =
|
|||
// b = Second value.
|
||||
function u_div(a,b) =
|
||||
is_undef(a) || is_undef(b)? undef :
|
||||
is_vector(a) && is_vector(b)? vdiv(a,b) :
|
||||
is_vector(a) && is_vector(b)? v_div(a,b) :
|
||||
a / b;
|
||||
|
||||
|
||||
|
@ -674,7 +674,7 @@ function _product(v, i=0, _tot) =
|
|||
i>=len(v) ? _tot :
|
||||
_product( v,
|
||||
i+1,
|
||||
( is_vector(v[i])? vmul(_tot,v[i]) : _tot*v[i] ) );
|
||||
( is_vector(v[i])? v_mul(_tot,v[i]) : _tot*v[i] ) );
|
||||
|
||||
|
||||
|
||||
|
@ -711,7 +711,7 @@ function _cumprod_vec(v,_i=0,_acc=[]) =
|
|||
v, _i+1,
|
||||
concat(
|
||||
_acc,
|
||||
[_i==0 ? v[_i] : vmul(_acc[len(_acc)-1],v[_i])]
|
||||
[_i==0 ? v[_i] : v_mul(_acc[len(_acc)-1],v[_i])]
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -1261,8 +1261,10 @@ function compare_lists(a, b) =
|
|||
|
||||
// Function: any()
|
||||
// Usage:
|
||||
// b = any(l);
|
||||
// b = any(l,func);
|
||||
// bool = any(l);
|
||||
// bool = any(l,func); // Requires OpenSCAD 2021.01 or later.
|
||||
// Requirements:
|
||||
// Requires OpenSCAD 2021.01 or later to use the `func=` argument.
|
||||
// Description:
|
||||
// Returns true if any item in list `l` evaluates as true.
|
||||
// Arguments:
|
||||
|
@ -1292,8 +1294,10 @@ function _any_bool(l, i=0, out=false) =
|
|||
|
||||
// Function: all()
|
||||
// Usage:
|
||||
// b = all(l);
|
||||
// b = all(l,func);
|
||||
// bool = all(l);
|
||||
// bool = all(l,func); // Requires OpenSCAD 2021.01 or later.
|
||||
// Requirements:
|
||||
// Requires OpenSCAD 2021.01 or later to use the `func=` argument.
|
||||
// Description:
|
||||
// Returns true if all items in list `l` evaluate as true. If `func` is given a function liteal
|
||||
// of signature (x), returning bool, then that function literal is evaluated for each list item.
|
||||
|
@ -1325,8 +1329,10 @@ function _all_bool(l, i=0, out=true) =
|
|||
|
||||
// Function: count_true()
|
||||
// Usage:
|
||||
// n = count_true(l,<nmax=>)
|
||||
// n = count_true(l,func,<nmax=>)
|
||||
// n = count_true(l,<nmax=>);
|
||||
// n = count_true(l,func,<nmax=>); // Requires OpenSCAD 2021.01 or later.
|
||||
// Requirements:
|
||||
// Requires OpenSCAD 2021.01 or later to use the `func=` argument.
|
||||
// Description:
|
||||
// Returns the number of items in `l` that evaluate as true.
|
||||
// If `l` is a lists of lists, this is applied recursively to each
|
||||
|
@ -1631,8 +1637,6 @@ function c_ident(n) = [for (i = [0:1:n-1]) [for (j = [0:1:n-1]) (i==j)?[1,0]:[0,
|
|||
// Compute the norm of a complex number or vector.
|
||||
function c_norm(z) = norm_fro(z);
|
||||
|
||||
|
||||
|
||||
// Section: Polynomials
|
||||
|
||||
// Function: quadratic_roots()
|
||||
|
@ -1840,12 +1844,19 @@ function _poly_roots(p, pderiv, s, z, tol, i=0) =
|
|||
// parts are zero. You can specify eps, in which case the test is
|
||||
// z.y/(1+norm(z)) < eps. Because
|
||||
// of poor convergence and higher error for repeated roots, such roots may
|
||||
// be missed by the algorithm because their imaginary part is large.
|
||||
// be missed by the algorithm because their imaginary part is large.
|
||||
// Arguments:
|
||||
// p = polynomial to solve as coefficient list, highest power term first
|
||||
// eps = used to determine whether imaginary parts of roots are zero
|
||||
// tol = tolerance for the complex polynomial root finder
|
||||
|
||||
// The algorithm is based on Brent's method and is a combination of
|
||||
// bisection and inverse quadratic approximation, where bisection occurs
|
||||
// at every step, with refinement using inverse quadratic approximation
|
||||
// only when that approximation gives a good result. The detail
|
||||
// of how to decide when to use the quadratic came from an article
|
||||
// by Crenshaw on "The World's Best Root Finder".
|
||||
// https://www.embedded.com/worlds-best-root-finder/
|
||||
function real_roots(p,eps=undef,tol=1e-14) =
|
||||
assert( is_vector(p), "Invalid polynomial." )
|
||||
let( p = _poly_trim(p,eps=0) )
|
||||
|
@ -1859,4 +1870,76 @@ function real_roots(p,eps=undef,tol=1e-14) =
|
|||
? [for(z=roots) if (abs(z.y)/(1+norm(z))<eps) z.x]
|
||||
: [for(i=idx(roots)) if (abs(roots[i].y)<=err[i]) roots[i].x];
|
||||
|
||||
|
||||
// Section: Operations on Functions
|
||||
|
||||
// Function: root_find()
|
||||
// Usage:
|
||||
// x = root_find(f, x0, x1, [tol])
|
||||
// Description:
|
||||
// Find a root of the continuous function f where the sign of f(x0) is different
|
||||
// from the sign of f(x1). The function f is a function literal accepting one
|
||||
// argument. You must have a version of OpenSCAD that supports function literals
|
||||
// (2021.01 or newer). The tolerance (tol) specifies the accuracy of the solution:
|
||||
// abs(f(x)) < tol * yrange, where yrange is the range of observed function values.
|
||||
// This function can only find roots that cross the x axis: it cannot find the
|
||||
// the root of x^2.
|
||||
// Arguments:
|
||||
// f = function literal for a single variable function
|
||||
// x0 = endpoint of interval to search for root
|
||||
// x1 = second endpoint of interval to search for root
|
||||
// tol = tolerance for solution. Default: 1e-15
|
||||
function root_find(f,x0,x1,tol=1e-15) =
|
||||
let(
|
||||
y0 = f(x0),
|
||||
y1 = f(x1),
|
||||
yrange = y0<y1 ? [y0,y1] : [y1,y0]
|
||||
)
|
||||
// Check endpoints
|
||||
y0==0 || _rfcheck(x0, y0,yrange,tol) ? x0 :
|
||||
y1==0 || _rfcheck(x1, y1,yrange,tol) ? x1 :
|
||||
assert(y0*y1<0, "Sign of function must be different at the interval endpoints")
|
||||
_rootfind(f,[x0,x1],[y0,y1],yrange,tol);
|
||||
|
||||
function _rfcheck(x,y,range,tol) =
|
||||
assert(is_finite(y), str("Function not finite at ",x))
|
||||
abs(y) < tol*(range[1]-range[0]);
|
||||
|
||||
// xpts and ypts are arrays whose first two entries contain the
|
||||
// interval bracketing the root. Extra entries are ignored.
|
||||
// yrange is the total observed range of y values (used for the
|
||||
// tolerance test).
|
||||
function _rootfind(f, xpts, ypts, yrange, tol, i=0) =
|
||||
assert(i<100, "root_find did not converge to a solution")
|
||||
let(
|
||||
xmid = (xpts[0]+xpts[1])/2,
|
||||
ymid = f(xmid),
|
||||
yrange = [min(ymid, yrange[0]), max(ymid, yrange[1])]
|
||||
)
|
||||
_rfcheck(xmid, ymid, yrange, tol) ? xmid :
|
||||
let(
|
||||
// Force root to be between x0 and midpoint
|
||||
y = ymid * ypts[0] < 0 ? [ypts[0], ymid, ypts[1]]
|
||||
: [ypts[1], ymid, ypts[0]],
|
||||
x = ymid * ypts[0] < 0 ? [xpts[0], xmid, xpts[1]]
|
||||
: [xpts[1], xmid, xpts[0]],
|
||||
v = y[2]*(y[2]-y[0]) - 2*y[1]*(y[1]-y[0])
|
||||
)
|
||||
v <= 0 ? _rootfind(f,x,y,yrange,tol,i+1) // Root is between first two points, extra 3rd point doesn't hurt
|
||||
:
|
||||
let( // Do quadratic approximation
|
||||
B = (x[1]-x[0]) / (y[1]-y[0]),
|
||||
C = y*[-1,2,-1] / (y[2]-y[1]) / (y[2]-y[0]),
|
||||
newx = x[0] - B * y[0] *(1-C*y[1]),
|
||||
newy = f(newx),
|
||||
new_yrange = [min(yrange[0],newy), max(yrange[1], newy)],
|
||||
// select interval that contains the root by checking sign
|
||||
yinterval = newy*y[0] < 0 ? [y[0],newy] : [newy,y[1]],
|
||||
xinterval = newy*y[0] < 0 ? [x[0],newx] : [newx,x[1]]
|
||||
)
|
||||
_rfcheck(newx, newy, new_yrange, tol)
|
||||
? newx
|
||||
: _rootfind(f, xinterval, yinterval, new_yrange, tol, i+1);
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
@ -515,7 +515,7 @@ module path_extrude2d(path, caps=true) {
|
|||
}
|
||||
}
|
||||
for (t=triplet(path)) {
|
||||
ang = vang(t[2]-t[1]) - vang(t[1]-t[0]);
|
||||
ang = v_theta(t[2]-t[1]) - v_theta(t[1]-t[0]);
|
||||
delt = t[2] - t[1];
|
||||
translate(t[1]) {
|
||||
minkowski() {
|
||||
|
|
|
@ -44,7 +44,7 @@ function _partition_cutpath(l, h, cutsize, cutpath, gap) =
|
|||
cplen = (cutsize.x+gap) * reps,
|
||||
path = deduplicate(concat(
|
||||
[[-l/2, cutpath[0].y*cutsize.y]],
|
||||
[for (i=[0:1:reps-1], pt=cutpath) vmul(pt,cutsize)+[i*(cutsize.x+gap)+gap/2-cplen/2,0]],
|
||||
[for (i=[0:1:reps-1], pt=cutpath) v_mul(pt,cutsize)+[i*(cutsize.x+gap)+gap/2-cplen/2,0]],
|
||||
[[ l/2, cutpath[len(cutpath)-1].y*cutsize.y]]
|
||||
))
|
||||
) path;
|
||||
|
@ -164,7 +164,7 @@ module partition(size=100, spread=10, cutsize=10, cutpath=undef, gap=0, spin=0)
|
|||
{
|
||||
size = is_vector(size)? size : [size,size,size];
|
||||
cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize];
|
||||
rsize = vabs(rot(spin,p=size));
|
||||
rsize = v_abs(rot(spin,p=size));
|
||||
vec = rot(spin,p=BACK)*spread/2;
|
||||
move(vec) {
|
||||
intersection() {
|
||||
|
|
24
paths.scad
24
paths.scad
|
@ -661,7 +661,7 @@ function path_self_intersections(path, closed=true, eps=EPSILON) =
|
|||
path = cleanup_path(path, eps=eps),
|
||||
plen = len(path)
|
||||
) [
|
||||
for (i = [0:1:plen-(closed?2:3)], j=[i+1:1:plen-(closed?1:2)]) let(
|
||||
for (i = [0:1:plen-(closed?2:3)], j=[i+2:1:plen-(closed?1:2)]) let(
|
||||
a1 = path[i],
|
||||
a2 = path[(i+1)%plen],
|
||||
b1 = path[j],
|
||||
|
@ -675,15 +675,17 @@ function path_self_intersections(path, closed=true, eps=EPSILON) =
|
|||
c = a1-a2,
|
||||
d = b1-b2,
|
||||
denom = (c.x*d.y)-(c.y*d.x)
|
||||
) abs(denom)<eps? undef : let(
|
||||
) abs(denom)<eps? undef :
|
||||
let(
|
||||
e = a1-b1,
|
||||
t = ((e.x*d.y)-(e.y*d.x)) / denom,
|
||||
u = ((e.x*c.y)-(e.y*c.x)) / denom
|
||||
) [a1+t*(a2-a1), t, u]
|
||||
) if (
|
||||
(!closed || i!=0 || j!=plen-1) &&
|
||||
isect != undef &&
|
||||
isect[1]>eps && isect[1]<=1+eps &&
|
||||
isect[2]>eps && isect[2]<=1+eps
|
||||
isect[1]>=-eps && isect[1]<=1+eps &&
|
||||
isect[2]>=-eps && isect[2]<=1+eps
|
||||
) [isect[0], i, isect[1], j, isect[2]]
|
||||
];
|
||||
|
||||
|
@ -1109,11 +1111,11 @@ module spiral_sweep(poly, h, r, twist=360, higbee, center, r1, r2, d, d1, d2, hi
|
|||
// path = [ [0, 0, 0], [33, 33, 33], [66, 33, 40], [100, 0, 0], [150,0,0] ];
|
||||
// path_extrude(path) circle(r=10, $fn=6);
|
||||
module path_extrude(path, convexity=10, clipsize=100) {
|
||||
function polyquats(path, q=Q_Ident(), v=[0,0,1], i=0) = let(
|
||||
function polyquats(path, q=q_ident(), v=[0,0,1], i=0) = let(
|
||||
v2 = path[i+1] - path[i],
|
||||
ang = vector_angle(v,v2),
|
||||
axis = ang>0.001? unit(cross(v,v2)) : [0,0,1],
|
||||
newq = Q_Mul(Quat(axis, ang), q),
|
||||
newq = q_mul(quat(axis, ang), q),
|
||||
dist = norm(v2)
|
||||
) i < (len(path)-2)?
|
||||
concat([[dist, newq, ang]], polyquats(path, newq, v2, i+1)) :
|
||||
|
@ -1129,7 +1131,7 @@ module path_extrude(path, convexity=10, clipsize=100) {
|
|||
q = pquats[i][1];
|
||||
difference() {
|
||||
translate(pt1) {
|
||||
Qrot(q) {
|
||||
q_rot(q) {
|
||||
down(clipsize/2/2) {
|
||||
if ((dist+clipsize/2) > 0) {
|
||||
linear_extrude(height=dist+clipsize/2, convexity=convexity) {
|
||||
|
@ -1140,12 +1142,12 @@ module path_extrude(path, convexity=10, clipsize=100) {
|
|||
}
|
||||
}
|
||||
translate(pt1) {
|
||||
hq = (i > 0)? Q_Slerp(q, pquats[i-1][1], 0.5) : q;
|
||||
Qrot(hq) down(clipsize/2+epsilon) cube(clipsize, center=true);
|
||||
hq = (i > 0)? q_slerp(q, pquats[i-1][1], 0.5) : q;
|
||||
q_rot(hq) down(clipsize/2+epsilon) cube(clipsize, center=true);
|
||||
}
|
||||
translate(pt2) {
|
||||
hq = (i < ptcount-2)? Q_Slerp(q, pquats[i+1][1], 0.5) : q;
|
||||
Qrot(hq) up(clipsize/2+epsilon) cube(clipsize, center=true);
|
||||
hq = (i < ptcount-2)? q_slerp(q, pquats[i+1][1], 0.5) : q;
|
||||
q_rot(hq) up(clipsize/2+epsilon) cube(clipsize, center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -377,7 +377,7 @@ function _point_ref(points, sign="both") =
|
|||
unique([
|
||||
for(i=[-1,1],j=[-1,1],k=[-1,1])
|
||||
if (sign=="both" || sign=="even" && i*j*k>0 || sign=="odd" && i*j*k<0)
|
||||
each [for(point=points) vmul(point,[i,j,k])]
|
||||
each [for(point=points) v_mul(point,[i,j,k])]
|
||||
]);
|
||||
//
|
||||
_tribonacci=(1+4*cosh(acosh(2+3/8)/3))/3;
|
||||
|
|
|
@ -139,7 +139,7 @@ function cube(size=1, center, anchor, spin=0, orient=UP) =
|
|||
[-1,-1, 1],[1,-1, 1],[1,1, 1],[-1,1, 1],
|
||||
]/2,
|
||||
verts = is_num(size)? unscaled * size :
|
||||
is_vector(size,3)? [for (p=unscaled) vmul(p,size)] :
|
||||
is_vector(size,3)? [for (p=unscaled) v_mul(p,size)] :
|
||||
assert(is_num(size) || is_vector(size,3)),
|
||||
faces = [
|
||||
[0,1,2], [0,2,3], //BOTTOM
|
||||
|
|
483
quaternions.scad
483
quaternions.scad
|
@ -16,190 +16,190 @@
|
|||
|
||||
|
||||
// Internal
|
||||
function _Quat(a,s,w) = [a[0]*s, a[1]*s, a[2]*s, w];
|
||||
function _quat(a,s,w) = [a[0]*s, a[1]*s, a[2]*s, w];
|
||||
|
||||
function _Qvec(q) = [q.x,q.y,q.z];
|
||||
function _qvec(q) = [q.x,q.y,q.z];
|
||||
|
||||
function _Qreal(q) = q[3];
|
||||
function _qreal(q) = q[3];
|
||||
|
||||
function _Qset(v,r) = concat( v, r );
|
||||
function _qset(v,r) = concat( v, r );
|
||||
|
||||
// normalizes without checking
|
||||
function _Qnorm(q) = q/norm(q);
|
||||
function _qnorm(q) = q/norm(q);
|
||||
|
||||
|
||||
// Function: Q_is_quat()
|
||||
// Function: is_quaternion()
|
||||
// Usage:
|
||||
// if(Q_is_quat(q)) a=0;
|
||||
// if(is_quaternion(q)) a=0;
|
||||
// Description: Return true if q is a valid non-zero quaternion.
|
||||
// Arguments:
|
||||
// q = object to check.
|
||||
function Q_is_quat(q) = is_vector(q,4) && ! approx(norm(q),0) ;
|
||||
function is_quaternion(q) = is_vector(q,4) && ! approx(norm(q),0) ;
|
||||
|
||||
|
||||
// Function: Quat()
|
||||
// Function: quat()
|
||||
// Usage:
|
||||
// Quat(ax, ang);
|
||||
// quat(ax, ang);
|
||||
// Description: Create a normalized Quaternion from axis and angle of rotation.
|
||||
// Arguments:
|
||||
// ax = Vector of axis of rotation.
|
||||
// ang = Number of degrees to rotate around the axis counter-clockwise, when facing the origin.
|
||||
function Quat(ax=[0,0,1], ang=0) =
|
||||
function quat(ax=[0,0,1], ang=0) =
|
||||
assert( is_vector(ax,3) && is_finite(ang), "Invalid input")
|
||||
let( n = norm(ax) )
|
||||
approx(n,0)
|
||||
? _Quat([0,0,0], sin(ang/2), cos(ang/2))
|
||||
: _Quat(ax/n, sin(ang/2), cos(ang/2));
|
||||
? _quat([0,0,0], sin(ang/2), cos(ang/2))
|
||||
: _quat(ax/n, sin(ang/2), cos(ang/2));
|
||||
|
||||
|
||||
// Function: QuatX()
|
||||
// Function: quat_x()
|
||||
// Usage:
|
||||
// QuatX(a);
|
||||
// quat_x(a);
|
||||
// Description: Create a normalized Quaternion for rotating around the X axis [1,0,0].
|
||||
// Arguments:
|
||||
// a = Number of degrees to rotate around the axis counter-clockwise, when facing the origin.
|
||||
function QuatX(a=0) =
|
||||
function quat_x(a=0) =
|
||||
assert( is_finite(a), "Invalid angle" )
|
||||
Quat([1,0,0],a);
|
||||
quat([1,0,0],a);
|
||||
|
||||
|
||||
// Function: QuatY()
|
||||
// Function: quat_y()
|
||||
// Usage:
|
||||
// QuatY(a);
|
||||
// quat_y(a);
|
||||
// Description: Create a normalized Quaternion for rotating around the Y axis [0,1,0].
|
||||
// Arguments:
|
||||
// a = Number of degrees to rotate around the axis counter-clockwise, when facing the origin.
|
||||
function QuatY(a=0) =
|
||||
function quat_y(a=0) =
|
||||
assert( is_finite(a), "Invalid angle" )
|
||||
Quat([0,1,0],a);
|
||||
quat([0,1,0],a);
|
||||
|
||||
|
||||
// Function: QuatZ()
|
||||
// Function: quat_z()
|
||||
// Usage:
|
||||
// QuatZ(a);
|
||||
// quat_z(a);
|
||||
// Description: Create a normalized Quaternion for rotating around the Z axis [0,0,1].
|
||||
// Arguments:
|
||||
// a = Number of degrees to rotate around the axis counter-clockwise, when facing the origin.
|
||||
function QuatZ(a=0) =
|
||||
function quat_z(a=0) =
|
||||
assert( is_finite(a), "Invalid angle" )
|
||||
Quat([0,0,1],a);
|
||||
quat([0,0,1],a);
|
||||
|
||||
|
||||
// Function: QuatXYZ()
|
||||
// Function: quat_xyz()
|
||||
// Usage:
|
||||
// QuatXYZ([X,Y,Z])
|
||||
// quat_xyz([X,Y,Z])
|
||||
// Description:
|
||||
// Creates a normalized quaternion from standard [X,Y,Z] rotation angles in degrees.
|
||||
// Arguments:
|
||||
// a = The triplet of rotation angles, [X,Y,Z]
|
||||
function QuatXYZ(a=[0,0,0]) =
|
||||
function quat_xyz(a=[0,0,0]) =
|
||||
assert( is_vector(a,3), "Invalid angles")
|
||||
let(
|
||||
qx = QuatX(a[0]),
|
||||
qy = QuatY(a[1]),
|
||||
qz = QuatZ(a[2])
|
||||
qx = quat_x(a[0]),
|
||||
qy = quat_y(a[1]),
|
||||
qz = quat_z(a[2])
|
||||
)
|
||||
Q_Mul(qz, Q_Mul(qy, qx));
|
||||
q_mul(qz, q_mul(qy, qx));
|
||||
|
||||
|
||||
// Function: Q_From_to()
|
||||
// Function: q_from_to()
|
||||
// Usage:
|
||||
// q = Q_From_to(v1, v2);
|
||||
// q = q_from_to(v1, v2);
|
||||
// Description:
|
||||
// Returns the normalized quaternion that rotates the non zero 3D vector v1
|
||||
// to the non zero 3D vector v2.
|
||||
function Q_From_to(v1, v2) =
|
||||
function q_from_to(v1, v2) =
|
||||
assert( is_vector(v1,3) && is_vector(v2,3)
|
||||
&& ! approx(norm(v1),0) && ! approx(norm(v2),0)
|
||||
, "Invalid vector(s)")
|
||||
let( ax = cross(v1,v2),
|
||||
n = norm(ax) )
|
||||
approx(n, 0)
|
||||
? v1*v2>0 ? Q_Ident() : Quat([ v1.y, -v1.x, 0], 180)
|
||||
: Quat(ax, atan2( n , v1*v2 ));
|
||||
? v1*v2>0 ? q_ident() : quat([ v1.y, -v1.x, 0], 180)
|
||||
: quat(ax, atan2( n , v1*v2 ));
|
||||
|
||||
|
||||
// Function: Q_Ident()
|
||||
// Function: q_ident()
|
||||
// Description: Returns the "Identity" zero-rotation Quaternion.
|
||||
function Q_Ident() = [0, 0, 0, 1];
|
||||
function q_ident() = [0, 0, 0, 1];
|
||||
|
||||
|
||||
// Function: Q_Add_S()
|
||||
// Function: q_add_s()
|
||||
// Usage:
|
||||
// Q_Add_S(q, s)
|
||||
// q_add_s(q, s)
|
||||
// Description:
|
||||
// Adds a scalar value `s` to the W part of a quaternion `q`.
|
||||
// The returned quaternion is usually not normalized.
|
||||
function Q_Add_S(q, s) =
|
||||
function q_add_s(q, s) =
|
||||
assert( is_finite(s), "Invalid scalar" )
|
||||
q+[0,0,0,s];
|
||||
|
||||
|
||||
// Function: Q_Sub_S()
|
||||
// Function: q_sub_s()
|
||||
// Usage:
|
||||
// Q_Sub_S(q, s)
|
||||
// q_sub_s(q, s)
|
||||
// Description:
|
||||
// Subtracts a scalar value `s` from the W part of a quaternion `q`.
|
||||
// The returned quaternion is usually not normalized.
|
||||
function Q_Sub_S(q, s) =
|
||||
function q_sub_s(q, s) =
|
||||
assert( is_finite(s), "Invalid scalar" )
|
||||
q-[0,0,0,s];
|
||||
|
||||
|
||||
// Function: Q_Mul_S()
|
||||
// Function: q_mul_s()
|
||||
// Usage:
|
||||
// Q_Mul_S(q, s)
|
||||
// q_mul_s(q, s)
|
||||
// Description:
|
||||
// Multiplies each part of a quaternion `q` by a scalar value `s`.
|
||||
// The returned quaternion is usually not normalized.
|
||||
function Q_Mul_S(q, s) =
|
||||
function q_mul_s(q, s) =
|
||||
assert( is_finite(s), "Invalid scalar" )
|
||||
q*s;
|
||||
|
||||
|
||||
// Function: Q_Div_S()
|
||||
// Function: q_div_s()
|
||||
// Usage:
|
||||
// Q_Div_S(q, s)
|
||||
// q_div_s(q, s)
|
||||
// Description:
|
||||
// Divides each part of a quaternion `q` by a scalar value `s`.
|
||||
// The returned quaternion is usually not normalized.
|
||||
function Q_Div_S(q, s) =
|
||||
function q_div_s(q, s) =
|
||||
assert( is_finite(s) && ! approx(s,0) , "Invalid scalar" )
|
||||
q/s;
|
||||
|
||||
|
||||
// Function: Q_Add()
|
||||
// Function: q_add()
|
||||
// Usage:
|
||||
// Q_Add(a, b)
|
||||
// q_add(a, b)
|
||||
// Description:
|
||||
// Adds each part of two quaternions together.
|
||||
// The returned quaternion is usually not normalized.
|
||||
function Q_Add(a, b) =
|
||||
assert( Q_is_quat(a) && Q_is_quat(a), "Invalid quaternion(s)")
|
||||
function q_add(a, b) =
|
||||
assert( is_quaternion(a) && is_quaternion(a), "Invalid quaternion(s)")
|
||||
assert( ! approx(norm(a+b),0), "Quaternions cannot be opposed" )
|
||||
a+b;
|
||||
|
||||
|
||||
// Function: Q_Sub()
|
||||
// Function: q_sub()
|
||||
// Usage:
|
||||
// Q_Sub(a, b)
|
||||
// q_sub(a, b)
|
||||
// Description:
|
||||
// Subtracts each part of quaternion `b` from quaternion `a`.
|
||||
// The returned quaternion is usually not normalized.
|
||||
function Q_Sub(a, b) =
|
||||
assert( Q_is_quat(a) && Q_is_quat(a), "Invalid quaternion(s)")
|
||||
function q_sub(a, b) =
|
||||
assert( is_quaternion(a) && is_quaternion(a), "Invalid quaternion(s)")
|
||||
assert( ! approx(a,b), "Quaternions cannot be equal" )
|
||||
a-b;
|
||||
|
||||
|
||||
// Function: Q_Mul()
|
||||
// Function: q_mul()
|
||||
// Usage:
|
||||
// Q_Mul(a, b)
|
||||
// q_mul(a, b)
|
||||
// Description:
|
||||
// Multiplies quaternion `a` by quaternion `b`.
|
||||
// The returned quaternion is normalized if both `a` and `b` are normalized
|
||||
function Q_Mul(a, b) =
|
||||
assert( Q_is_quat(a) && Q_is_quat(b), "Invalid quaternion(s)")
|
||||
function q_mul(a, b) =
|
||||
assert( is_quaternion(a) && is_quaternion(b), "Invalid quaternion(s)")
|
||||
[
|
||||
a[3]*b.x + a.x*b[3] + a.y*b.z - a.z*b.y,
|
||||
a[3]*b.y - a.x*b.z + a.y*b[3] + a.z*b.x,
|
||||
|
@ -208,94 +208,94 @@ function Q_Mul(a, b) =
|
|||
];
|
||||
|
||||
|
||||
// Function: Q_Cumulative()
|
||||
// Function: q_cumulative()
|
||||
// Usage:
|
||||
// Q_Cumulative(v);
|
||||
// q_cumulative(v);
|
||||
// Description:
|
||||
// Given a list of Quaternions, cumulatively multiplies them, returning a list
|
||||
// of each cumulative Quaternion product. It starts with the first quaternion
|
||||
// given in the list, and applies successive quaternion rotations in list order.
|
||||
// The quaternion in the returned list are normalized if each quaternion in v
|
||||
// is normalized.
|
||||
function Q_Cumulative(v, _i=0, _acc=[]) =
|
||||
function q_cumulative(v, _i=0, _acc=[]) =
|
||||
_i==len(v) ? _acc :
|
||||
Q_Cumulative(
|
||||
q_cumulative(
|
||||
v, _i+1,
|
||||
concat(
|
||||
_acc,
|
||||
[_i==0 ? v[_i] : Q_Mul(v[_i], last(_acc))]
|
||||
[_i==0 ? v[_i] : q_mul(v[_i], last(_acc))]
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// Function: Q_Dot()
|
||||
// Function: q_dot()
|
||||
// Usage:
|
||||
// Q_Dot(a, b)
|
||||
// q_dot(a, b)
|
||||
// Description: Calculates the dot product between quaternions `a` and `b`.
|
||||
function Q_Dot(a, b) =
|
||||
assert( Q_is_quat(a) && Q_is_quat(b), "Invalid quaternion(s)" )
|
||||
function q_dot(a, b) =
|
||||
assert( is_quaternion(a) && is_quaternion(b), "Invalid quaternion(s)" )
|
||||
a*b;
|
||||
|
||||
// Function: Q_Neg()
|
||||
// Function: q_neg()
|
||||
// Usage:
|
||||
// Q_Neg(q)
|
||||
// q_neg(q)
|
||||
// Description: Returns the negative of quaternion `q`.
|
||||
function Q_Neg(q) =
|
||||
assert( Q_is_quat(q), "Invalid quaternion" )
|
||||
function q_neg(q) =
|
||||
assert( is_quaternion(q), "Invalid quaternion" )
|
||||
-q;
|
||||
|
||||
|
||||
// Function: Q_Conj()
|
||||
// Function: q_conj()
|
||||
// Usage:
|
||||
// Q_Conj(q)
|
||||
// q_conj(q)
|
||||
// Description: Returns the conjugate of quaternion `q`.
|
||||
function Q_Conj(q) =
|
||||
assert( Q_is_quat(q), "Invalid quaternion" )
|
||||
function q_conj(q) =
|
||||
assert( is_quaternion(q), "Invalid quaternion" )
|
||||
[-q.x, -q.y, -q.z, q[3]];
|
||||
|
||||
|
||||
// Function: Q_Inverse()
|
||||
// Function: q_inverse()
|
||||
// Usage:
|
||||
// qc = Q_Inverse(q)
|
||||
// qc = q_inverse(q)
|
||||
// Description: Returns the multiplication inverse of quaternion `q` that is normalized only if `q` is normalized.
|
||||
function Q_Inverse(q) =
|
||||
assert( Q_is_quat(q), "Invalid quaternion" )
|
||||
let(q = _Qnorm(q) )
|
||||
function q_inverse(q) =
|
||||
assert( is_quaternion(q), "Invalid quaternion" )
|
||||
let(q = _qnorm(q) )
|
||||
[-q.x, -q.y, -q.z, q[3]];
|
||||
|
||||
|
||||
// Function: Q_Norm()
|
||||
// Function: q_norm()
|
||||
// Usage:
|
||||
// Q_Norm(q)
|
||||
// q_norm(q)
|
||||
// Description:
|
||||
// Returns the `norm()` "length" of quaternion `q`.
|
||||
// Normalized quaternions have unitary norm.
|
||||
function Q_Norm(q) =
|
||||
assert( Q_is_quat(q), "Invalid quaternion" )
|
||||
function q_norm(q) =
|
||||
assert( is_quaternion(q), "Invalid quaternion" )
|
||||
norm(q);
|
||||
|
||||
|
||||
// Function: Q_Normalize()
|
||||
// Function: q_normalize()
|
||||
// Usage:
|
||||
// Q_Normalize(q)
|
||||
// q_normalize(q)
|
||||
// Description: Normalizes quaternion `q`, so that norm([W,X,Y,Z]) == 1.
|
||||
function Q_Normalize(q) =
|
||||
assert( Q_is_quat(q) , "Invalid quaternion" )
|
||||
function q_normalize(q) =
|
||||
assert( is_quaternion(q) , "Invalid quaternion" )
|
||||
q/norm(q);
|
||||
|
||||
|
||||
// Function: Q_Dist()
|
||||
// Function: q_dist()
|
||||
// Usage:
|
||||
// Q_Dist(q1, q2)
|
||||
// q_dist(q1, q2)
|
||||
// Description: Returns the "distance" between two quaternions.
|
||||
function Q_Dist(q1, q2) =
|
||||
assert( Q_is_quat(q1) && Q_is_quat(q2), "Invalid quaternion(s)" )
|
||||
function q_dist(q1, q2) =
|
||||
assert( is_quaternion(q1) && is_quaternion(q2), "Invalid quaternion(s)" )
|
||||
norm(q2-q1);
|
||||
|
||||
|
||||
// Function: Q_Slerp()
|
||||
// Function: q_slerp()
|
||||
// Usage:
|
||||
// Q_Slerp(q1, q2, u);
|
||||
// q_slerp(q1, q2, u);
|
||||
// Description:
|
||||
// Returns a quaternion that is a spherical interpolation between two quaternions.
|
||||
// Arguments:
|
||||
|
@ -303,45 +303,45 @@ function Q_Dist(q1, q2) =
|
|||
// q2 = The second quaternion. (u=1)
|
||||
// u = The proportional value, from 0 to 1, of what part of the interpolation to return.
|
||||
// Example(3D): Giving `u` as a Scalar
|
||||
// a = QuatY(-135);
|
||||
// b = QuatXYZ([0,-30,30]);
|
||||
// a = quat_y(-135);
|
||||
// b = quat_xyz([0,-30,30]);
|
||||
// for (u=[0:0.1:1])
|
||||
// Qrot(Q_Slerp(a, b, u))
|
||||
// q_rot(q_slerp(a, b, u))
|
||||
// right(80) cube([10,10,1]);
|
||||
// #sphere(r=80);
|
||||
// Example(3D): Giving `u` as a Range
|
||||
// a = QuatZ(-135);
|
||||
// b = QuatXYZ([90,0,-45]);
|
||||
// for (q = Q_Slerp(a, b, [0:0.1:1]))
|
||||
// Qrot(q) right(80) cube([10,10,1]);
|
||||
// a = quat_z(-135);
|
||||
// b = quat_xyz([90,0,-45]);
|
||||
// for (q = q_slerp(a, b, [0:0.1:1]))
|
||||
// q_rot(q) right(80) cube([10,10,1]);
|
||||
// #sphere(r=80);
|
||||
function Q_Slerp(q1, q2, u, _dot) =
|
||||
function q_slerp(q1, q2, u, _dot) =
|
||||
is_undef(_dot)
|
||||
? assert(is_finite(u) || is_range(u) || is_vector(u), "Invalid interpolation coefficient(s)")
|
||||
assert(Q_is_quat(q1) && Q_is_quat(q2), "Invalid quaternion(s)" )
|
||||
assert(is_quaternion(q1) && is_quaternion(q2), "Invalid quaternion(s)" )
|
||||
let(
|
||||
_dot = q1*q2,
|
||||
q1 = q1/norm(q1),
|
||||
q2 = _dot<0 ? -q2/norm(q2) : q2/norm(q2),
|
||||
dot = abs(_dot)
|
||||
)
|
||||
! is_finite(u) ? [for (uu=u) Q_Slerp(q1, q2, uu, dot)] :
|
||||
Q_Slerp(q1, q2, u, dot)
|
||||
! is_finite(u) ? [for (uu=u) q_slerp(q1, q2, uu, dot)] :
|
||||
q_slerp(q1, q2, u, dot)
|
||||
: _dot>0.9995
|
||||
? _Qnorm(q1 + u*(q2-q1))
|
||||
? _qnorm(q1 + u*(q2-q1))
|
||||
: let( theta = u*acos(_dot),
|
||||
q3 = _Qnorm(q2 - _dot*q1)
|
||||
q3 = _qnorm(q2 - _dot*q1)
|
||||
)
|
||||
_Qnorm(q1*cos(theta) + q3*sin(theta));
|
||||
_qnorm(q1*cos(theta) + q3*sin(theta));
|
||||
|
||||
|
||||
// Function: Q_Matrix3()
|
||||
// Function: q_matrix3()
|
||||
// Usage:
|
||||
// Q_Matrix3(q);
|
||||
// q_matrix3(q);
|
||||
// Description:
|
||||
// Returns the 3x3 rotation matrix for the given normalized quaternion q.
|
||||
function Q_Matrix3(q) =
|
||||
let( q = Q_Normalize(q) )
|
||||
function q_matrix3(q) =
|
||||
let( q = q_normalize(q) )
|
||||
[
|
||||
[1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3]],
|
||||
[ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3]],
|
||||
|
@ -349,13 +349,13 @@ function Q_Matrix3(q) =
|
|||
];
|
||||
|
||||
|
||||
// Function: Q_Matrix4()
|
||||
// Function: q_matrix4()
|
||||
// Usage:
|
||||
// Q_Matrix4(q);
|
||||
// q_matrix4(q);
|
||||
// Description:
|
||||
// Returns the 4x4 rotation matrix for the given normalized quaternion q.
|
||||
function Q_Matrix4(q) =
|
||||
let( q = Q_Normalize(q) )
|
||||
function q_matrix4(q) =
|
||||
let( q = q_normalize(q) )
|
||||
[
|
||||
[1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3], 0],
|
||||
[ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3], 0],
|
||||
|
@ -364,115 +364,115 @@ function Q_Matrix4(q) =
|
|||
];
|
||||
|
||||
|
||||
// Function: Q_Axis()
|
||||
// Function: q_axis()
|
||||
// Usage:
|
||||
// Q_Axis(q)
|
||||
// q_axis(q)
|
||||
// Description:
|
||||
// Returns the axis of rotation of a normalized quaternion `q`.
|
||||
// The input doesn't need to be normalized.
|
||||
function Q_Axis(q) =
|
||||
assert( Q_is_quat(q) , "Invalid quaternion" )
|
||||
let( d = norm(_Qvec(q)) )
|
||||
approx(d,0)? [0,0,1] : _Qvec(q)/d;
|
||||
function q_axis(q) =
|
||||
assert( is_quaternion(q) , "Invalid quaternion" )
|
||||
let( d = norm(_qvec(q)) )
|
||||
approx(d,0)? [0,0,1] : _qvec(q)/d;
|
||||
|
||||
// Function: Q_Angle()
|
||||
// Function: q_angle()
|
||||
// Usage:
|
||||
// a = Q_Angle(q)
|
||||
// a12 = Q_Angle(q1,q2);
|
||||
// a = q_angle(q)
|
||||
// a12 = q_angle(q1,q2);
|
||||
// Description:
|
||||
// If only q1 is given, returns the angle of rotation (in degrees) of that quaternion.
|
||||
// If both q1 and q2 are given, returns the angle (in degrees) between them.
|
||||
// The input quaternions don't need to be normalized.
|
||||
function Q_Angle(q1,q2) =
|
||||
assert(Q_is_quat(q1) && (is_undef(q2) || Q_is_quat(q2)), "Invalid quaternion(s)" )
|
||||
let( n1 = is_undef(q2)? norm(_Qvec(q1)): norm(q1) )
|
||||
function q_angle(q1,q2) =
|
||||
assert(is_quaternion(q1) && (is_undef(q2) || is_quaternion(q2)), "Invalid quaternion(s)" )
|
||||
let( n1 = is_undef(q2)? norm(_qvec(q1)): norm(q1) )
|
||||
is_undef(q2)
|
||||
? 2 * atan2(n1,_Qreal(q1))
|
||||
? 2 * atan2(n1,_qreal(q1))
|
||||
: let( q1 = q1/norm(q1),
|
||||
q2 = q2/norm(q2) )
|
||||
4 * atan2(norm(q1 - q2), norm(q1 + q2));
|
||||
|
||||
// Function&Module: Qrot()
|
||||
// Function&Module: q_rot()
|
||||
// Usage: As Module
|
||||
// Qrot(q) ...
|
||||
// q_rot(q) ...
|
||||
// Usage: As Function
|
||||
// pts = Qrot(q,p);
|
||||
// pts = q_rot(q,p);
|
||||
// Description:
|
||||
// When called as a module, rotates all children by the rotation stored in quaternion `q`.
|
||||
// When called as a function with a `p` argument, rotates the point or list of points in `p` by the rotation stored in quaternion `q`.
|
||||
// When called as a function without a `p` argument, returns the affine3d rotation matrix for the rotation stored in quaternion `q`.
|
||||
// Example(FlatSpin,VPD=225,VPT=[71,-26,16]):
|
||||
// module shape() translate([80,0,0]) cube([10,10,1]);
|
||||
// q = QuatXYZ([90,-15,-45]);
|
||||
// Qrot(q) shape();
|
||||
// q = quat_xyz([90,-15,-45]);
|
||||
// q_rot(q) shape();
|
||||
// #shape();
|
||||
// Example(NORENDER):
|
||||
// q = QuatXYZ([45,35,10]);
|
||||
// mat4x4 = Qrot(q);
|
||||
// q = quat_xyz([45,35,10]);
|
||||
// mat4x4 = q_rot(q);
|
||||
// Example(NORENDER):
|
||||
// q = QuatXYZ([45,35,10]);
|
||||
// pt = Qrot(q, p=[4,5,6]);
|
||||
// q = quat_xyz([45,35,10]);
|
||||
// pt = q_rot(q, p=[4,5,6]);
|
||||
// Example(NORENDER):
|
||||
// q = QuatXYZ([45,35,10]);
|
||||
// pts = Qrot(q, p=[[2,3,4], [4,5,6], [9,2,3]]);
|
||||
module Qrot(q) {
|
||||
multmatrix(Q_Matrix4(q)) {
|
||||
// q = quat_xyz([45,35,10]);
|
||||
// pts = q_rot(q, p=[[2,3,4], [4,5,6], [9,2,3]]);
|
||||
module q_rot(q) {
|
||||
multmatrix(q_matrix4(q)) {
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
function Qrot(q,p) =
|
||||
is_undef(p)? Q_Matrix4(q) :
|
||||
is_vector(p)? Qrot(q,[p])[0] :
|
||||
apply(Q_Matrix4(q), p);
|
||||
function q_rot(q,p) =
|
||||
is_undef(p)? q_matrix4(q) :
|
||||
is_vector(p)? q_rot(q,[p])[0] :
|
||||
apply(q_matrix4(q), p);
|
||||
|
||||
|
||||
// Module: Qrot_copies()
|
||||
// Module: q_rot_copies()
|
||||
// Usage:
|
||||
// Qrot_copies(quats) ...
|
||||
// q_rot_copies(quats) ...
|
||||
// Description:
|
||||
// For each quaternion given in the list `quats`, rotates to that orientation and creates a copy
|
||||
// of all children. This is equivalent to `for (q=quats) Qrot(q) ...`.
|
||||
// of all children. This is equivalent to `for (q=quats) q_rot(q) ...`.
|
||||
// Arguments:
|
||||
// quats = A list containing all quaternions to rotate to and create copies of all children for.
|
||||
// Example:
|
||||
// a = QuatZ(-135);
|
||||
// b = QuatXYZ([0,-30,30]);
|
||||
// Qrot_copies(Q_Slerp(a, b, [0:0.1:1]))
|
||||
// a = quat_z(-135);
|
||||
// b = quat_xyz([0,-30,30]);
|
||||
// q_rot_copies(q_slerp(a, b, [0:0.1:1]))
|
||||
// right(80) cube([10,10,1]);
|
||||
// #sphere(r=80);
|
||||
module Qrot_copies(quats) for (q=quats) Qrot(q) children();
|
||||
module q_rot_copies(quats) for (q=quats) q_rot(q) children();
|
||||
|
||||
|
||||
// Function: Q_Rotation()
|
||||
// Function: q_rotation()
|
||||
// Usage:
|
||||
// Q_Rotation(R)
|
||||
// q_rotation(R)
|
||||
// Description:
|
||||
// Returns a normalized quaternion corresponding to the rotation matrix R.
|
||||
// R may be a 3x3 rotation matrix or a homogeneous 4x4 rotation matrix.
|
||||
// The last row and last column of R are ignored for 4x4 matrices.
|
||||
// It doesn't check whether R is in fact a rotation matrix.
|
||||
// If R is not a rotation, the returned quaternion is an unpredictable quaternion .
|
||||
function Q_Rotation(R) =
|
||||
function q_rotation(R) =
|
||||
assert( is_matrix(R,3,3) || is_matrix(R,4,4) ,
|
||||
"Matrix is neither 3x3 nor 4x4")
|
||||
let( tr = R[0][0]+R[1][1]+R[2][2] ) // R trace
|
||||
tr>0
|
||||
? let( r = 1+tr )
|
||||
_Qnorm( _Qset([ R[1][2]-R[2][1], R[2][0]-R[0][2], R[0][1]-R[1][0] ], -r ) )
|
||||
_qnorm( _qset([ R[1][2]-R[2][1], R[2][0]-R[0][2], R[0][1]-R[1][0] ], -r ) )
|
||||
: let( i = max_index([ R[0][0], R[1][1], R[2][2] ]),
|
||||
r = 1 + 2*R[i][i] -R[0][0] -R[1][1] -R[2][2] )
|
||||
i==0 ? _Qnorm( _Qset( [ 4*r, (R[1][0]+R[0][1]), (R[0][2]+R[2][0]) ], (R[2][1]-R[1][2])) ):
|
||||
i==1 ? _Qnorm( _Qset( [ (R[1][0]+R[0][1]), 4*r, (R[2][1]+R[1][2]) ], (R[0][2]-R[2][0])) ):
|
||||
_Qnorm( _Qset( [ (R[2][0]+R[0][2]), (R[1][2]+R[2][1]), 4*r ], (R[1][0]-R[0][1])) ) ;
|
||||
i==0 ? _qnorm( _qset( [ 4*r, (R[1][0]+R[0][1]), (R[0][2]+R[2][0]) ], (R[2][1]-R[1][2])) ):
|
||||
i==1 ? _qnorm( _qset( [ (R[1][0]+R[0][1]), 4*r, (R[2][1]+R[1][2]) ], (R[0][2]-R[2][0])) ):
|
||||
_qnorm( _qset( [ (R[2][0]+R[0][2]), (R[1][2]+R[2][1]), 4*r ], (R[1][0]-R[0][1])) ) ;
|
||||
|
||||
|
||||
// Function&Module: Q_Rotation_path()
|
||||
// Function&Module: q_rotation_path()
|
||||
// Usage: As a function
|
||||
// path = Q_Rotation_path(q1, n, q2);
|
||||
// path = Q_Rotation_path(q1, n);
|
||||
// path = q_rotation_path(q1, n, q2);
|
||||
// path = q_rotation_path(q1, n);
|
||||
// Usage: As a module
|
||||
// Q_Rotation_path(q1, n, q2) ...
|
||||
// q_rotation_path(q1, n, q2) ...
|
||||
// Description:
|
||||
// If q2 is undef and it is called as a function, the path, with length n+1 (n>=1), will be the
|
||||
// cumulative multiplications of the matrix rotation of q1 by itself.
|
||||
|
@ -488,50 +488,50 @@ function Q_Rotation(R) =
|
|||
// q2 = The quaternion of the last rotation.
|
||||
// n = An integer defining the path length ( path length = n+1).
|
||||
// Example(3D): as a function
|
||||
// a = QuatY(-135);
|
||||
// b = QuatXYZ([0,-30,30]);
|
||||
// for (M=Q_Rotation_path(a, 10, b))
|
||||
// a = quat_y(-135);
|
||||
// b = quat_xyz([0,-30,30]);
|
||||
// for (M=q_rotation_path(a, 10, b))
|
||||
// multmatrix(M)
|
||||
// right(80) cube([10,10,1]);
|
||||
// #sphere(r=80);
|
||||
// Example(3D): as a module
|
||||
// a = QuatY(-135);
|
||||
// b = QuatXYZ([0,-30,30]);
|
||||
// Q_Rotation_path(a, 10, b)
|
||||
// a = quat_y(-135);
|
||||
// b = quat_xyz([0,-30,30]);
|
||||
// q_rotation_path(a, 10, b)
|
||||
// right(80) cube([10,10,1]);
|
||||
// #sphere(r=80);
|
||||
// Example(3D): as a function
|
||||
// a = QuatY(5);
|
||||
// for (M=Q_Rotation_path(a, 10))
|
||||
// a = quat_y(5);
|
||||
// for (M=q_rotation_path(a, 10))
|
||||
// multmatrix(M)
|
||||
// right(80) cube([10,10,1]);
|
||||
// #sphere(r=80);
|
||||
// Example(3D): as a module
|
||||
// a = QuatY(5);
|
||||
// Q_Rotation_path(a, 10)
|
||||
// a = quat_y(5);
|
||||
// q_rotation_path(a, 10)
|
||||
// right(80) cube([10,10,1]);
|
||||
// #sphere(r=80);
|
||||
function Q_Rotation_path(q1, n=1, q2) =
|
||||
assert( Q_is_quat(q1) && (is_undef(q2) || Q_is_quat(q2) ), "Invalid quaternion(s)" )
|
||||
function q_rotation_path(q1, n=1, q2) =
|
||||
assert( is_quaternion(q1) && (is_undef(q2) || is_quaternion(q2) ), "Invalid quaternion(s)" )
|
||||
assert( is_finite(n) && n>=1 && n==floor(n), "Invalid integer" )
|
||||
assert( is_undef(q2) || ! approx(norm(q1+q2),0), "Quaternions cannot be opposed" )
|
||||
is_undef(q2)
|
||||
? [for( i=0, dR=Q_Matrix4(q1), R=dR; i<=n; i=i+1, R=dR*R ) R]
|
||||
: let( q2 = Q_Normalize( q1*q2<0 ? -q2: q2 ),
|
||||
dq = Q_pow( Q_Mul( q2, Q_Inverse(q1) ), 1/n ),
|
||||
dR = Q_Matrix4(dq) )
|
||||
[for( i=0, R=Q_Matrix4(q1); i<=n; i=i+1, R=dR*R ) R];
|
||||
? [for( i=0, dR=q_matrix4(q1), R=dR; i<=n; i=i+1, R=dR*R ) R]
|
||||
: let( q2 = q_normalize( q1*q2<0 ? -q2: q2 ),
|
||||
dq = q_pow( q_mul( q2, q_inverse(q1) ), 1/n ),
|
||||
dR = q_matrix4(dq) )
|
||||
[for( i=0, R=q_matrix4(q1); i<=n; i=i+1, R=dR*R ) R];
|
||||
|
||||
module Q_Rotation_path(q1, n=1, q2) {
|
||||
for(Mi=Q_Rotation_path(q1, n, q2))
|
||||
module q_rotation_path(q1, n=1, q2) {
|
||||
for(Mi=q_rotation_path(q1, n, q2))
|
||||
multmatrix(Mi)
|
||||
children();
|
||||
}
|
||||
|
||||
|
||||
// Function: Q_Nlerp()
|
||||
// Function: q_nlerp()
|
||||
// Usage:
|
||||
// q = Q_Nlerp(q1, q2, u);
|
||||
// q = q_nlerp(q1, q2, u);
|
||||
// Description:
|
||||
// Returns a quaternion that is a normalized linear interpolation between two quaternions
|
||||
// when u is a number.
|
||||
|
@ -543,33 +543,33 @@ module Q_Rotation_path(q1, n=1, q2) {
|
|||
// q2 = The second quaternion. (u=1)
|
||||
// u = A value (or a list of values), between 0 and 1, of the proportion(s) of each quaternion in the interpolation.
|
||||
// Example(3D): Giving `u` as a Scalar
|
||||
// a = QuatY(-135);
|
||||
// b = QuatXYZ([0,-30,30]);
|
||||
// a = quat_y(-135);
|
||||
// b = quat_xyz([0,-30,30]);
|
||||
// for (u=[0:0.1:1])
|
||||
// Qrot(Q_Nlerp(a, b, u))
|
||||
// q_rot(q_nlerp(a, b, u))
|
||||
// right(80) cube([10,10,1]);
|
||||
// #sphere(r=80);
|
||||
// Example(3D): Giving `u` as a Range
|
||||
// a = QuatZ(-135);
|
||||
// b = QuatXYZ([90,0,-45]);
|
||||
// for (q = Q_Nlerp(a, b, [0:0.1:1]))
|
||||
// Qrot(q) right(80) cube([10,10,1]);
|
||||
// a = quat_z(-135);
|
||||
// b = quat_xyz([90,0,-45]);
|
||||
// for (q = q_nlerp(a, b, [0:0.1:1]))
|
||||
// q_rot(q) right(80) cube([10,10,1]);
|
||||
// #sphere(r=80);
|
||||
function Q_Nlerp(q1,q2,u) =
|
||||
function q_nlerp(q1,q2,u) =
|
||||
assert(is_finite(u) || is_range(u) || is_vector(u) ,
|
||||
"Invalid interpolation coefficient(s)" )
|
||||
assert(Q_is_quat(q1) && Q_is_quat(q2), "Invalid quaternion(s)" )
|
||||
assert(is_quaternion(q1) && is_quaternion(q2), "Invalid quaternion(s)" )
|
||||
assert( ! approx(norm(q1+q2),0), "Quaternions cannot be opposed" )
|
||||
let( q1 = Q_Normalize(q1),
|
||||
q2 = Q_Normalize(q2) )
|
||||
let( q1 = q_normalize(q1),
|
||||
q2 = q_normalize(q2) )
|
||||
is_num(u)
|
||||
? _Qnorm((1-u)*q1 + u*q2 )
|
||||
: [for (ui=u) _Qnorm((1-ui)*q1 + ui*q2 ) ];
|
||||
? _qnorm((1-u)*q1 + u*q2 )
|
||||
: [for (ui=u) _qnorm((1-ui)*q1 + ui*q2 ) ];
|
||||
|
||||
|
||||
// Function: Q_Squad()
|
||||
// Function: q_squad()
|
||||
// Usage:
|
||||
// qn = Q_Squad(q1,q2,q3,q4,u);
|
||||
// qn = q_squad(q1,q2,q3,q4,u);
|
||||
// Description:
|
||||
// Returns a quaternion that is a cubic spherical interpolation of the quaternions
|
||||
// q1 and q4 taking the other two quaternions, q2 and q3, as parameter of a cubic
|
||||
|
@ -586,71 +586,72 @@ function Q_Nlerp(q1,q2,u) =
|
|||
// q4 = The end quaternion. (u=1)
|
||||
// u = A value (or a list of values), of the proportion(s) of each quaternion in the cubic interpolation.
|
||||
// Example(3D): Giving `u` as a Scalar
|
||||
// a = QuatY(-135);
|
||||
// b = QuatXYZ([-50,-50,120]);
|
||||
// c = QuatXYZ([-50,-40,30]);
|
||||
// d = QuatY(-45);
|
||||
// a = quat_y(-135);
|
||||
// b = quat_xyz([-50,-50,120]);
|
||||
// c = quat_xyz([-50,-40,30]);
|
||||
// d = quat_y(-45);
|
||||
// color("red"){
|
||||
// Qrot(b) right(80) cube([10,10,1]);
|
||||
// Qrot(c) right(80) cube([10,10,1]);
|
||||
// q_rot(b) right(80) cube([10,10,1]);
|
||||
// q_rot(c) right(80) cube([10,10,1]);
|
||||
// }
|
||||
// for (u=[0:0.05:1])
|
||||
// Qrot(Q_Squad(a, b, c, d, u))
|
||||
// q_rot(q_squad(a, b, c, d, u))
|
||||
// right(80) cube([10,10,1]);
|
||||
// #sphere(r=80);
|
||||
// Example(3D): Giving `u` as a Range
|
||||
// a = QuatY(-135);
|
||||
// b = QuatXYZ([-50,-50,120]);
|
||||
// c = QuatXYZ([-50,-40,30]);
|
||||
// d = QuatY(-45);
|
||||
// for (q = Q_Squad(a, b, c, d, [0:0.05:1]))
|
||||
// Qrot(q) right(80) cube([10,10,1]);
|
||||
// a = quat_y(-135);
|
||||
// b = quat_xyz([-50,-50,120]);
|
||||
// c = quat_xyz([-50,-40,30]);
|
||||
// d = quat_y(-45);
|
||||
// for (q = q_squad(a, b, c, d, [0:0.05:1]))
|
||||
// q_rot(q) right(80) cube([10,10,1]);
|
||||
// #sphere(r=80);
|
||||
function Q_Squad(q1,q2,q3,q4,u) =
|
||||
function q_squad(q1,q2,q3,q4,u) =
|
||||
assert(is_finite(u) || is_range(u) || is_vector(u) ,
|
||||
"Invalid interpolation coefficient(s)" )
|
||||
is_num(u)
|
||||
? Q_Slerp( Q_Slerp(q1,q4,u), Q_Slerp(q2,q3,u), 2*u*(1-u))
|
||||
: [for(ui=u) Q_Slerp( Q_Slerp(q1,q4,ui), Q_Slerp(q2,q3,ui), 2*ui*(1-ui) ) ];
|
||||
? q_slerp( q_slerp(q1,q4,u), q_slerp(q2,q3,u), 2*u*(1-u))
|
||||
: [for(ui=u) q_slerp( q_slerp(q1,q4,ui), q_slerp(q2,q3,ui), 2*ui*(1-ui) ) ];
|
||||
|
||||
|
||||
// Function: Q_exp()
|
||||
// Function: q_exp()
|
||||
// Usage:
|
||||
// q2 = Q_exp(q);
|
||||
// q2 = q_exp(q);
|
||||
// Description:
|
||||
// Returns the quaternion that is the exponential of the quaternion q in base e
|
||||
// The returned quaternion is usually not normalized.
|
||||
function Q_exp(q) =
|
||||
function q_exp(q) =
|
||||
assert( is_vector(q,4), "Input is not a valid quaternion")
|
||||
let( nv = norm(_Qvec(q)) ) // q may be equal to zero here!
|
||||
exp(_Qreal(q))*Quat(_Qvec(q),2*nv);
|
||||
let( nv = norm(_qvec(q)) ) // q may be equal to zero here!
|
||||
exp(_qreal(q))*quat(_qvec(q),2*nv);
|
||||
|
||||
|
||||
// Function: Q_ln()
|
||||
// Function: q_ln()
|
||||
// Usage:
|
||||
// q2 = Q_ln(q);
|
||||
// q2 = q_ln(q);
|
||||
// Description:
|
||||
// Returns the quaternion that is the natural logarithm of the quaternion q.
|
||||
// The returned quaternion is usually not normalized and may be zero.
|
||||
function Q_ln(q) =
|
||||
assert(Q_is_quat(q), "Input is not a valid quaternion")
|
||||
let( nq = norm(q),
|
||||
nv = norm(_Qvec(q)) )
|
||||
approx(nv,0) ? _Qset([0,0,0] , ln(nq) ) :
|
||||
_Qset(_Qvec(q)*atan2(nv,_Qreal(q))/nv, ln(nq));
|
||||
function q_ln(q) =
|
||||
assert(is_quaternion(q), "Input is not a valid quaternion")
|
||||
let(
|
||||
nq = norm(q),
|
||||
nv = norm(_qvec(q))
|
||||
)
|
||||
approx(nv,0) ? _qset([0,0,0] , ln(nq) ) :
|
||||
_qset(_qvec(q)*atan2(nv,_qreal(q))/nv, ln(nq));
|
||||
|
||||
|
||||
// Function: Q_pow()
|
||||
// Function: q_pow()
|
||||
// Usage:
|
||||
// q2 = Q_pow(q, r);
|
||||
// q2 = q_pow(q, r);
|
||||
// Description:
|
||||
// Returns the quaternion that is the power of the quaternion q to the real exponent r.
|
||||
// The returned quaternion is normalized if `q` is normalized.
|
||||
function Q_pow(q,r=1) =
|
||||
assert( Q_is_quat(q) && is_finite(r),
|
||||
"Invalid inputs")
|
||||
let( theta = 2*atan2(norm(_Qvec(q)),_Qreal(q)) )
|
||||
Quat(_Qvec(q), r*theta); // Q_exp(r*Q_ln(q));
|
||||
function q_pow(q,r=1) =
|
||||
assert( is_quaternion(q) && is_finite(r), "Invalid inputs")
|
||||
let( theta = 2*atan2(norm(_qvec(q)),_qreal(q)) )
|
||||
quat(_qvec(q), r*theta); // q_exp(r*q_ln(q));
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
VERFILE="version.scad"
|
||||
|
||||
vernums=$(grep ^BOSL_VERSION "$VERFILE" | sed 's/^.*[[]\([0-9,]*\)[]].*$/\1/')
|
||||
major=$(echo "$vernums" | awk -F, '{print $1}')
|
||||
minor=$(echo "$vernums" | awk -F, '{print $2}')
|
||||
revision=$(echo "$vernums" | awk -F, '{print $3}')
|
||||
if [[ "$(cat "$VERFILE")" =~ BOSL_VERSION.*=.*\[([0-9]+),\ *([0-9]+),\ *([0-9]+)\]\; ]]; then
|
||||
major=${BASH_REMATCH[1]} minor=${BASH_REMATCH[2]} revision=${BASH_REMATCH[3]}
|
||||
new_revision=$(( revision+1 ))
|
||||
|
||||
newrev=$(($revision+1))
|
||||
echo "Current Version: $major.$minor.$revision"
|
||||
echo "New Version: $major.$minor.$newrev"
|
||||
|
||||
sed -i '' 's/^BOSL_VERSION = .*$/BOSL_VERSION = ['"$major,$minor,$newrev];/g" $VERFILE
|
||||
echo "Current Version: $major.$minor.$revision"
|
||||
echo "New Version: $major.$minor.$new_revision"
|
||||
|
||||
sed -i.bak -e 's/^BOSL_VERSION = .*$/BOSL_VERSION = ['"$major,$minor,$new_revision];/g" "$VERFILE"
|
||||
rm "$VERFILE".bak
|
||||
else
|
||||
echo "Could not extract version number from $VERFILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
#!/bin/bash
|
||||
|
||||
lib_comment_lines=$(grep '^// ' *.scad | wc -l)
|
||||
lib_code_lines=$(grep '^ *[^ /]' *.scad | wc -l)
|
||||
script_code_lines=$(grep '^ *[^ /]' scripts/*.sh scripts/*.py | wc -l)
|
||||
example_code_lines=$(grep '^ *[^ /]' examples/*.scad | wc -l)
|
||||
test_code_lines=$(grep '^ *[^ /]' tests/*.scad | wc -l)
|
||||
tutorial_lines=$(grep '^ *[^ /]' tutorials/*.md | wc -l)
|
||||
lib_comment_lines=$(cat -- *.scad | grep -c '^// ')
|
||||
tutorial_lines=$(cat tutorials/*.md | grep -c '^ *[^ /]')
|
||||
|
||||
y=$(printf "%06d" 13)
|
||||
|
||||
printf "Documentation Lines : %6d\n" $(($lib_comment_lines+$tutorial_lines))
|
||||
printf "Example Code Lines : %6d\n" $example_code_lines
|
||||
printf "Library Code Lines : %6d\n" $lib_code_lines
|
||||
printf "Support Script Lines: %6d\n" $script_code_lines
|
||||
printf "Test Code Lines : %6d\n" $test_code_lines
|
||||
printf '%-20s: %6d\n' \
|
||||
'Documentation Lines' "$(( lib_comment_lines + tutorial_lines ))" \
|
||||
'Example Code Lines' "$(cat examples/*.scad | grep -c '^ *[^ /]')" \
|
||||
'Library Code Lines' "$(cat -- *.scad | grep -c '^ *[^ /]')" \
|
||||
'Support Script Lines' "$(cat scripts/*.sh scripts/*.py | grep -c '^ *[^ /]')" \
|
||||
'Test Code Lines' "$(cat tests/*.scad | grep -c '^ *[^ /]')"
|
||||
|
||||
|
|
|
@ -1,40 +1,38 @@
|
|||
#!/bin/bash
|
||||
|
||||
FORCED=""
|
||||
FILES=""
|
||||
DISPMD=""
|
||||
DISPMD=0
|
||||
GEN_ARGS=()
|
||||
FILES=()
|
||||
for opt in "$@" ; do
|
||||
case $opt in
|
||||
-f ) FORCED=$opt ;;
|
||||
-d ) DISPMD=$opt ;;
|
||||
-* ) echo "Unknown option $opt"; exit -1 ;;
|
||||
* ) FILES="$FILES $opt" ;;
|
||||
case "$opt" in
|
||||
-f ) GEN_ARGS+=(-f) ;;
|
||||
-d ) DISPMD=1 ;;
|
||||
-* ) echo "Unknown option $opt" >&2; exit 1 ;;
|
||||
* ) FILES+=("$opt") ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$FILES" != "" ]]; then
|
||||
PREVIEW_LIBS="$FILES"
|
||||
else
|
||||
PREVIEW_LIBS="Shapes2d Shapes3d Transforms Distributors Mutators Attachments Paths FractalTree"
|
||||
if (( ${#FILES[@]} == 0 )); then
|
||||
FILES=(Shapes2d Shapes3d Transforms Distributors Mutators Attachments Paths FractalTree)
|
||||
fi
|
||||
|
||||
dir="$(basename $PWD)"
|
||||
if [ "$dir" = "BOSL2" ]; then
|
||||
cd BOSL2.wiki
|
||||
elif [ "$dir" != "BOSL2.wiki" ]; then
|
||||
echo "Must run this script from the BOSL2 or BOSL2/BOSL2.wiki directories."
|
||||
# Try to cd to the BOSL2.wiki directory if run from the BOSL2 root
|
||||
if [[ "$(basename "$PWD")" != "BOSL2.wiki" ]]; then
|
||||
if ! cd BOSL2.wiki; then
|
||||
echo "BOSL2.wiki directory not found, try running from the BOSL2 or BOSL2/BOSL2.wiki directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -f tmp_*.scad
|
||||
for base in $PREVIEW_LIBS; do
|
||||
base="$(basename $base .md)"
|
||||
for base in "${FILES[@]}"; do
|
||||
base="$(basename "$base" .md)"
|
||||
mkdir -p images/tutorials
|
||||
rm -f images/tutorials/${base}_*.png images/tutorials/${base}_*.gif
|
||||
echo "$base.md"
|
||||
../scripts/tutorial_gen.py ../tutorials/$base.md -o Tutorial-$base.md $FORCED -I images/tutorials/ || exit 1
|
||||
if [ "$DISPMD" != "" ]; then
|
||||
open -a Typora Tutorial-$base.md
|
||||
rm -f "images/tutorials/${base}"_*.png "images/tutorials/${base}"_*.gif
|
||||
echo "${base}.md"
|
||||
../scripts/tutorial_gen.py "../tutorials/${base}.md" -o "Tutorial-${base}.md" "${GEN_ARGS[@]}" -I images/tutorials/ || exit 1
|
||||
if (( DISPMD )); then
|
||||
open -a Typora "Tutorial-${base}.md"
|
||||
fi
|
||||
done
|
||||
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [[ ! -d BOSL2.wiki/.git ]] ; then
|
||||
echo "Must be run from the BOSL2 directory, with the BOSL2.wiki repo inside."
|
||||
exit -1
|
||||
echo "Must be run from above the BOSL2.wiki repo." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -e # short-circuit if any command fails
|
||||
cd BOSL2.wiki
|
||||
rm -rf .git
|
||||
git init
|
||||
git add .
|
||||
git commit -m "Purged wiki history."
|
||||
git config pull.rebase false
|
||||
git remote add origin git@github.com:revarbat/BOSL2.wiki.git
|
||||
git push -u --force origin master
|
||||
cd ..
|
||||
|
||||
|
|
|
@ -1,35 +1,31 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ "$(uname -s)" != "Darwin" ]; then
|
||||
OPENSCAD=openscad
|
||||
else
|
||||
OPENSCAD=openscad
|
||||
if [ "$(uname -s)" == "Darwin" ]; then
|
||||
OPENSCAD=/Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD
|
||||
fi
|
||||
|
||||
if [ "$*" != "" ] ; then
|
||||
INFILES="$*"
|
||||
else
|
||||
INFILES="tests/test_*.scad"
|
||||
INFILES=("$@")
|
||||
if (( ${#INFILES[@]} == 0 )); then
|
||||
INFILES=(tests/test_*.scad)
|
||||
fi
|
||||
|
||||
OUTCODE=0
|
||||
for testscript in $INFILES ; do
|
||||
repname="$(basename $testscript | sed 's/^test_//')"
|
||||
testfile="tests/test_$repname"
|
||||
if [ -f "$testfile" ] ; then
|
||||
${OPENSCAD} -o out.echo --hardwarnings --check-parameters true --check-parameter-ranges true $testfile 2>&1
|
||||
for testfile in "${INFILES[@]}"; do
|
||||
if [[ -f "$testfile" ]] ; then
|
||||
repname="$(basename "$testfile" | sed 's/^test_//')"
|
||||
"${OPENSCAD}" -o out.echo --hardwarnings --check-parameters true --check-parameter-ranges true "$testfile" 2>&1
|
||||
retcode=$?
|
||||
res=$(cat out.echo)
|
||||
if [ $retcode -eq 0 ] && [ "$res" = "" ] ; then
|
||||
output=$(cat out.echo)
|
||||
if (( retcode == 0 )) && [[ "$output" = "" ]]; then
|
||||
echo "$repname: PASS"
|
||||
else
|
||||
echo "$repname: FAIL!"
|
||||
cat out.echo
|
||||
echo
|
||||
OUTCODE=-1
|
||||
echo "$output"
|
||||
OUTCODE=1
|
||||
fi
|
||||
rm -f out.echo
|
||||
fi
|
||||
done
|
||||
exit $OUTCODE
|
||||
exit "$OUTCODE"
|
||||
|
||||
|
|
233
shapes.scad
233
shapes.scad
|
@ -57,17 +57,41 @@
|
|||
// Example: Rounded Edges, Untrimmed Corners
|
||||
// cuboid([30,40,50], rounding=10, trimcorners=false);
|
||||
// Example: Chamferring Selected Edges
|
||||
// cuboid([30,40,50], chamfer=5, edges=[TOP+FRONT,TOP+RIGHT,FRONT+RIGHT], $fn=24);
|
||||
// cuboid(
|
||||
// [30,40,50], chamfer=5,
|
||||
// edges=[TOP+FRONT,TOP+RIGHT,FRONT+RIGHT],
|
||||
// $fn=24
|
||||
// );
|
||||
// Example: Rounding Selected Edges
|
||||
// cuboid([30,40,50], rounding=5, edges=[TOP+FRONT,TOP+RIGHT,FRONT+RIGHT], $fn=24);
|
||||
// cuboid(
|
||||
// [30,40,50], rounding=5,
|
||||
// edges=[TOP+FRONT,TOP+RIGHT,FRONT+RIGHT],
|
||||
// $fn=24
|
||||
// );
|
||||
// Example: Negative Chamferring
|
||||
// cuboid([30,40,50], chamfer=-5, edges=[TOP,BOT], except_edges=RIGHT, $fn=24);
|
||||
// cuboid(
|
||||
// [30,40,50], chamfer=-5,
|
||||
// edges=[TOP,BOT], except_edges=RIGHT,
|
||||
// $fn=24
|
||||
// );
|
||||
// Example: Negative Chamferring, Untrimmed Corners
|
||||
// cuboid([30,40,50], chamfer=-5, edges=[TOP,BOT], except_edges=RIGHT, trimcorners=false, $fn=24);
|
||||
// cuboid(
|
||||
// [30,40,50], chamfer=-5,
|
||||
// edges=[TOP,BOT], except_edges=RIGHT,
|
||||
// trimcorners=false, $fn=24
|
||||
// );
|
||||
// Example: Negative Rounding
|
||||
// cuboid([30,40,50], rounding=-5, edges=[TOP,BOT], except_edges=RIGHT, $fn=24);
|
||||
// cuboid(
|
||||
// [30,40,50], rounding=-5,
|
||||
// edges=[TOP,BOT], except_edges=RIGHT,
|
||||
// $fn=24
|
||||
// );
|
||||
// Example: Negative Rounding, Untrimmed Corners
|
||||
// cuboid([30,40,50], rounding=-5, edges=[TOP,BOT], except_edges=RIGHT, trimcorners=false, $fn=24);
|
||||
// cuboid(
|
||||
// [30,40,50], rounding=-5,
|
||||
// edges=[TOP,BOT], except_edges=RIGHT,
|
||||
// trimcorners=false, $fn=24
|
||||
// );
|
||||
// Example: Standard Connectors
|
||||
// cuboid(40) show_anchors();
|
||||
module cuboid(
|
||||
|
@ -87,9 +111,9 @@ module cuboid(
|
|||
cnt = sum(e);
|
||||
r = first_defined([chamfer, rounding, 0]);
|
||||
c = [min(r,size.x/2), min(r,size.y/2), min(r,size.z/2)];
|
||||
c2 = vmul(corner,c/2);
|
||||
c2 = v_mul(corner,c/2);
|
||||
$fn = is_finite(chamfer)? 4 : segs(r);
|
||||
translate(vmul(corner, size/2-c)) {
|
||||
translate(v_mul(corner, size/2-c)) {
|
||||
if (cnt == 0 || approx(r,0)) {
|
||||
translate(c2) cube(c, center=true);
|
||||
} else if (cnt == 1) {
|
||||
|
@ -130,6 +154,7 @@ module cuboid(
|
|||
size = scalar_vec3(size);
|
||||
edges = edges(edges, except=except_edges);
|
||||
assert(is_vector(size,3));
|
||||
assert(all_positive(size));
|
||||
assert(is_undef(chamfer) || is_finite(chamfer));
|
||||
assert(is_undef(rounding) || is_finite(rounding));
|
||||
assert(is_undef(p1) || is_vector(p1));
|
||||
|
@ -138,7 +163,7 @@ module cuboid(
|
|||
if (!is_undef(p1)) {
|
||||
if (!is_undef(p2)) {
|
||||
translate(pointlist_bounds([p1,p2])[0]) {
|
||||
cuboid(size=vabs(p2-p1), chamfer=chamfer, rounding=rounding, edges=edges, trimcorners=trimcorners, anchor=ALLNEG) children();
|
||||
cuboid(size=v_abs(p2-p1), chamfer=chamfer, rounding=rounding, edges=edges, trimcorners=trimcorners, anchor=ALLNEG) children();
|
||||
}
|
||||
} else {
|
||||
translate(p1) {
|
||||
|
@ -184,7 +209,7 @@ module cuboid(
|
|||
for (i = [0:3], axis=[0:1]) {
|
||||
if (edges[axis][i]>0) {
|
||||
vec = EDGE_OFFSETS[axis][i];
|
||||
translate(vmul(vec/2, size+[ach,ach,-ach])) {
|
||||
translate(v_mul(vec/2, size+[ach,ach,-ach])) {
|
||||
rotate(majrots[axis]) {
|
||||
cube([ach, ach, size[axis]], center=true);
|
||||
}
|
||||
|
@ -197,7 +222,7 @@ module cuboid(
|
|||
for (za=[-1,1], ya=[-1,1], xa=[-1,1]) {
|
||||
ce = corner_edges(edges, [xa,ya,za]);
|
||||
if (ce.x + ce.y > 1) {
|
||||
translate(vmul([xa,ya,za]/2, size+[ach-0.01,ach-0.01,-ach])) {
|
||||
translate(v_mul([xa,ya,za]/2, size+[ach-0.01,ach-0.01,-ach])) {
|
||||
cube([ach+0.01,ach+0.01,ach], center=true);
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +234,7 @@ module cuboid(
|
|||
for (i = [0:3], axis=[0:1]) {
|
||||
if (edges[axis][i]>0) {
|
||||
vec = EDGE_OFFSETS[axis][i];
|
||||
translate(vmul(vec/2, size+[2*ach,2*ach,-2*ach])) {
|
||||
translate(v_mul(vec/2, size+[2*ach,2*ach,-2*ach])) {
|
||||
rotate(majrots[axis]) {
|
||||
zrot(45) cube([ach*sqrt(2), ach*sqrt(2), size[axis]+2.1*ach], center=true);
|
||||
}
|
||||
|
@ -271,7 +296,7 @@ module cuboid(
|
|||
for (i = [0:3], axis=[0:1]) {
|
||||
if (edges[axis][i]>0) {
|
||||
vec = EDGE_OFFSETS[axis][i];
|
||||
translate(vmul(vec/2, size+[ard,ard,-ard])) {
|
||||
translate(v_mul(vec/2, size+[ard,ard,-ard])) {
|
||||
rotate(majrots[axis]) {
|
||||
cube([ard, ard, size[axis]], center=true);
|
||||
}
|
||||
|
@ -284,7 +309,7 @@ module cuboid(
|
|||
for (za=[-1,1], ya=[-1,1], xa=[-1,1]) {
|
||||
ce = corner_edges(edges, [xa,ya,za]);
|
||||
if (ce.x + ce.y > 1) {
|
||||
translate(vmul([xa,ya,za]/2, size+[ard-0.01,ard-0.01,-ard])) {
|
||||
translate(v_mul([xa,ya,za]/2, size+[ard-0.01,ard-0.01,-ard])) {
|
||||
cube([ard+0.01,ard+0.01,ard], center=true);
|
||||
}
|
||||
}
|
||||
|
@ -296,7 +321,7 @@ module cuboid(
|
|||
for (i = [0:3], axis=[0:1]) {
|
||||
if (edges[axis][i]>0) {
|
||||
vec = EDGE_OFFSETS[axis][i];
|
||||
translate(vmul(vec/2, size+[2*ard,2*ard,-2*ard])) {
|
||||
translate(v_mul(vec/2, size+[2*ard,2*ard,-2*ard])) {
|
||||
rotate(majrots[axis]) {
|
||||
cyl(l=size[axis]+2.1*ard, r=ard);
|
||||
}
|
||||
|
@ -363,10 +388,6 @@ function cuboid(
|
|||
// Creates a rectangular prismoid shape with optional roundovers and chamfering.
|
||||
// You can only round or chamfer the vertical(ish) edges. For those edges, you can
|
||||
// specify rounding and/or chamferring per-edge, and for top and bottom separately.
|
||||
// Note: if using chamfers or rounding, you **must** also include the hull.scad file:
|
||||
// ```
|
||||
// include <BOSL2/hull.scad>
|
||||
// ```
|
||||
//
|
||||
// Arguments:
|
||||
// size1 = [width, length] of the bottom end of the prism.
|
||||
|
@ -374,12 +395,12 @@ function cuboid(
|
|||
// h|l = Height of the prism.
|
||||
// shift = [X,Y] amount to shift the center of the top end with respect to the center of the bottom end.
|
||||
// ---
|
||||
// rounding = The roundover radius for the vertical-ish edges of the prismoid. Requires including hull.scad. 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)
|
||||
// rounding1 = The roundover radius for the bottom of the vertical-ish edges of the prismoid. Requires including hull.scad. 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-].
|
||||
// rounding2 = The roundover radius for the top of the vertical-ish edges of the prismoid. Requires including hull.scad. 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-].
|
||||
// chamfer = The chamfer size for the vertical-ish edges of the prismoid. Requires including hull.scad. 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)
|
||||
// chamfer1 = The chamfer size for the bottom of the vertical-ish edges of the prismoid. Requires including hull.scad. 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-].
|
||||
// chamfer2 = The chamfer size for the top of the vertical-ish edges of the prismoid. Requires including hull.scad. 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-].
|
||||
// rounding = The roundover radius for the vertical-ish edges of the prismoid. 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)
|
||||
// rounding1 = The roundover radius for the bottom of the vertical-ish edges of the prismoid. 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-].
|
||||
// rounding2 = The roundover radius for the top of the vertical-ish edges of the prismoid. 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-].
|
||||
// chamfer = The chamfer size for the vertical-ish edges of the prismoid. 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)
|
||||
// chamfer1 = The chamfer size for the bottom of the vertical-ish edges of the prismoid. 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-].
|
||||
// chamfer2 = The chamfer size for the top of the vertical-ish edges of the prismoid. 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-].
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||
|
@ -401,25 +422,22 @@ function cuboid(
|
|||
// Example(FlatSpin,VPD=160,VPT=[0,0,10]): Shifting/Skewing
|
||||
// prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]);
|
||||
// Example: Rounding
|
||||
// include <BOSL2/hull.scad>
|
||||
// prismoid(100, 80, rounding=10, h=30);
|
||||
// Example: Outer Chamfer Only
|
||||
// include <BOSL2/hull.scad>
|
||||
// prismoid(100, 80, chamfer=5, h=30);
|
||||
// Example: Gradiant Rounding
|
||||
// include <BOSL2/hull.scad>
|
||||
// prismoid(100, 80, rounding1=10, rounding2=0, h=30);
|
||||
// Example: Per Corner Rounding
|
||||
// include <BOSL2/hull.scad>
|
||||
// prismoid(100, 80, rounding=[0,5,10,15], h=30);
|
||||
// Example: Per Corner Chamfer
|
||||
// include <BOSL2/hull.scad>
|
||||
// prismoid(100, 80, chamfer=[0,5,10,15], h=30);
|
||||
// Example: Mixing Chamfer and Rounding
|
||||
// include <BOSL2/hull.scad>
|
||||
// prismoid(100, 80, chamfer=[0,5,0,10], rounding=[5,0,10,0], h=30);
|
||||
// prismoid(
|
||||
// 100, 80, h=30,
|
||||
// chamfer=[0,5,0,10],
|
||||
// rounding=[5,0,10,0]
|
||||
// );
|
||||
// Example: Really Mixing It Up
|
||||
// include <BOSL2/hull.scad>
|
||||
// prismoid(
|
||||
// size1=[100,80], size2=[80,60], h=20,
|
||||
// chamfer1=[0,5,0,10], chamfer2=[5,0,10,0],
|
||||
|
@ -448,6 +466,10 @@ module prismoid(
|
|||
eps = pow(2,-14);
|
||||
size1 = is_num(size1)? [size1,size1] : size1;
|
||||
size2 = is_num(size2)? [size2,size2] : size2;
|
||||
assert(all_nonnegative(size1));
|
||||
assert(all_nonnegative(size2));
|
||||
assert(size1.x + size2.x > 0);
|
||||
assert(size1.y + size2.y > 0);
|
||||
s1 = [max(size1.x, eps), max(size1.y, eps)];
|
||||
s2 = [max(size2.x, eps), max(size2.y, eps)];
|
||||
rounding1 = default(rounding1, rounding);
|
||||
|
@ -499,8 +521,8 @@ function prismoid(
|
|||
let(
|
||||
corners = [[1,1],[1,-1],[-1,-1],[-1,1]] * 0.5,
|
||||
points = [
|
||||
for (p=corners) point3d(vmul(s2,p), +h/2) + shiftby,
|
||||
for (p=corners) point3d(vmul(s1,p), -h/2)
|
||||
for (p=corners) point3d(v_mul(s2,p), +h/2) + shiftby,
|
||||
for (p=corners) point3d(v_mul(s1,p), -h/2)
|
||||
],
|
||||
faces=[
|
||||
[0,1,2], [0,2,3], [0,4,5], [0,5,1],
|
||||
|
@ -551,10 +573,6 @@ function prismoid(
|
|||
// You can only round or chamfer the vertical(ish) edges. For those edges, you can
|
||||
// specify rounding and/or chamferring per-edge, and for top and bottom, inside and
|
||||
// outside separately.
|
||||
// Note: if using chamfers or rounding, you **must** also include the hull.scad file:
|
||||
// ```
|
||||
// include <BOSL2/hull.scad>
|
||||
// ```
|
||||
// Arguments:
|
||||
// h|l = The height or length of the rectangular tube. Default: 1
|
||||
// size = The outer [X,Y] size of the rectangular tube.
|
||||
|
@ -588,33 +606,42 @@ function prismoid(
|
|||
// rect_tube(isize=[60,80], wall=5, h=30);
|
||||
// rect_tube(size=[100,60], isize=[90,50], h=30);
|
||||
// rect_tube(size1=[100,60], size2=[70,40], wall=5, h=30);
|
||||
// rect_tube(size1=[100,60], size2=[70,40], isize1=[40,20], isize2=[65,35], h=15);
|
||||
// Example:
|
||||
// rect_tube(
|
||||
// size1=[100,60], size2=[70,40],
|
||||
// isize1=[40,20], isize2=[65,35], h=15
|
||||
// );
|
||||
// Example: Outer Rounding Only
|
||||
// include <BOSL2/hull.scad>
|
||||
// rect_tube(size=100, wall=5, rounding=10, irounding=0, h=30);
|
||||
// Example: Outer Chamfer Only
|
||||
// include <BOSL2/hull.scad>
|
||||
// rect_tube(size=100, wall=5, chamfer=5, ichamfer=0, h=30);
|
||||
// Example: Outer Rounding, Inner Chamfer
|
||||
// include <BOSL2/hull.scad>
|
||||
// rect_tube(size=100, wall=5, rounding=10, ichamfer=8, h=30);
|
||||
// Example: Inner Rounding, Outer Chamfer
|
||||
// include <BOSL2/hull.scad>
|
||||
// rect_tube(size=100, wall=5, chamfer=10, irounding=8, h=30);
|
||||
// Example: Gradiant Rounding
|
||||
// include <BOSL2/hull.scad>
|
||||
// rect_tube(size1=100, size2=80, wall=5, rounding1=10, rounding2=0, irounding1=8, irounding2=0, h=30);
|
||||
// rect_tube(
|
||||
// size1=100, size2=80, wall=5, h=30,
|
||||
// rounding1=10, rounding2=0,
|
||||
// irounding1=8, irounding2=0
|
||||
// );
|
||||
// Example: Per Corner Rounding
|
||||
// include <BOSL2/hull.scad>
|
||||
// rect_tube(size=100, wall=10, rounding=[0,5,10,15], irounding=0, h=30);
|
||||
// rect_tube(
|
||||
// size=100, wall=10, h=30,
|
||||
// rounding=[0,5,10,15], irounding=0
|
||||
// );
|
||||
// Example: Per Corner Chamfer
|
||||
// include <BOSL2/hull.scad>
|
||||
// rect_tube(size=100, wall=10, chamfer=[0,5,10,15], ichamfer=0, h=30);
|
||||
// rect_tube(
|
||||
// size=100, wall=10, h=30,
|
||||
// chamfer=[0,5,10,15], ichamfer=0
|
||||
// );
|
||||
// Example: Mixing Chamfer and Rounding
|
||||
// include <BOSL2/hull.scad>
|
||||
// rect_tube(size=100, wall=10, chamfer=[0,5,0,10], ichamfer=0, rounding=[5,0,10,0], irounding=0, h=30);
|
||||
// rect_tube(
|
||||
// size=100, wall=10, h=30,
|
||||
// chamfer=[0,5,0,10], ichamfer=0,
|
||||
// rounding=[5,0,10,0], irounding=0
|
||||
// );
|
||||
// Example: Really Mixing It Up
|
||||
// include <BOSL2/hull.scad>
|
||||
// rect_tube(
|
||||
// size1=[100,80], size2=[80,60],
|
||||
// isize1=[50,30], isize2=[70,50], h=20,
|
||||
|
@ -838,7 +865,11 @@ function right_triangle(size=[1,1,1], center, anchor, spin=0, orient=UP) =
|
|||
// }
|
||||
//
|
||||
// Example: Putting it all together
|
||||
// cyl(l=40, d1=25, d2=15, chamfer1=10, chamfang1=30, from_end=true, rounding2=5);
|
||||
// cyl(
|
||||
// l=40, d1=25, d2=15,
|
||||
// chamfer1=10, chamfang1=30,
|
||||
// from_end=true, rounding2=5
|
||||
// );
|
||||
//
|
||||
// Example: External Chamfers
|
||||
// cyl(l=50, r=30, chamfer=-5, chamfang=30, $fa=1, $fs=1);
|
||||
|
@ -1504,6 +1535,9 @@ function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, or
|
|||
// Usage: Typical
|
||||
// teardrop(h|l, r, <ang>, <cap_h>, ...);
|
||||
// teardrop(h|l, d=, <ang=>, <cap_h=>, ...);
|
||||
// Usage: Psuedo-Conical
|
||||
// teardrop(h|l, r1=, r2=, <ang=>, <cap_h1=>, <cap_h2=>, ...);
|
||||
// teardrop(h|l, d1=, d2=, <ang=>, <cap_h1=>, <cap_h2=>, ...);
|
||||
// Usage: Attaching Children
|
||||
// teardrop(h|l, r, ...) <attachments>;
|
||||
//
|
||||
|
@ -1513,33 +1547,71 @@ function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, or
|
|||
// ang = Angle of hat walls from the Z axis. Default: 45 degrees
|
||||
// cap_h = If given, height above center where the shape will be truncated. Default: `undef` (no truncation)
|
||||
// ---
|
||||
// d = Diameter of circular portion of bottom. (Use instead of r)
|
||||
// r1 = Radius of circular portion of the front end of the teardrop shape.
|
||||
// r2 = Radius of circular portion of the back end of the teardrop shape.
|
||||
// d = Diameter of circular portion of the teardrop shape.
|
||||
// d1 = Diameter of circular portion of the front end of the teardrop shape.
|
||||
// d2 = Diameter of circular portion of the back end of the teardrop shape.
|
||||
// cap_h1 = If given, height above center where the shape will be truncated, on the front side. Default: `undef` (no truncation)
|
||||
// cap_h2 = If given, height above center where the shape will be truncated, on the back side. Default: `undef` (no truncation)
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||
//
|
||||
// Extra Anchors:
|
||||
// cap = The center of the top of the cap, oriented with the cap face normal.
|
||||
// cap_fwd = The front edge of the cap.
|
||||
// cap_back = The back edge of the cap.
|
||||
//
|
||||
// Example: Typical Shape
|
||||
// teardrop(r=30, h=10, ang=30);
|
||||
// Example: Crop Cap
|
||||
// teardrop(r=30, h=10, ang=30, cap_h=40);
|
||||
// Example: Close Crop
|
||||
// teardrop(r=30, h=10, ang=30, cap_h=20);
|
||||
// Example: Standard Connectors
|
||||
// teardrop(r=30, h=10, ang=30) show_anchors();
|
||||
module teardrop(h, r, ang=45, cap_h, d, l, anchor=CENTER, spin=0, orient=UP)
|
||||
// Example: Psuedo-Conical
|
||||
// teardrop(r1=20, r2=30, h=40, cap_h1=25, cap_h2=35);
|
||||
// Example: Standard Conical Connectors
|
||||
// teardrop(d1=20, d2=30, h=20, cap_h1=11, cap_h2=16)
|
||||
// show_anchors(custom=false);
|
||||
// Example(Spin,VPD=275): Named Conical Connectors
|
||||
// teardrop(d1=20, d2=30, h=20, cap_h1=11, cap_h2=16)
|
||||
// show_anchors(std=false);
|
||||
module teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, l, anchor=CENTER, spin=0, orient=UP)
|
||||
{
|
||||
r = get_radius(r=r, d=d, dflt=1);
|
||||
r1 = get_radius(r=r, r1=r1, d=d, d1=d1, dflt=1);
|
||||
r2 = get_radius(r=r, r1=r2, d=d, d1=d2, dflt=1);
|
||||
l = first_defined([l, h, 1]);
|
||||
tip_y = adj_ang_to_hyp(r, 90-ang);
|
||||
cap_h = min(default(cap_h,tip_y), tip_y);
|
||||
tip_y1 = adj_ang_to_hyp(r1, 90-ang);
|
||||
tip_y2 = adj_ang_to_hyp(r2, 90-ang);
|
||||
cap_h1 = min(first_defined([cap_h1, cap_h, tip_y1]), tip_y1);
|
||||
cap_h2 = min(first_defined([cap_h2, cap_h, tip_y2]), tip_y2);
|
||||
capvec = unit([0, cap_h1-cap_h2, l]);
|
||||
anchors = [
|
||||
["cap", [0,0,cap_h], UP, 0]
|
||||
anchorpt("cap", [0,0,(cap_h1+cap_h2)/2], capvec),
|
||||
anchorpt("cap_fwd", [0,-l/2,cap_h1], unit((capvec+FWD)/2)),
|
||||
anchorpt("cap_back", [0,+l/2,cap_h2], unit((capvec+BACK)/2), 180),
|
||||
];
|
||||
attachable(anchor,spin,orient, r=r, l=l, axis=BACK, anchors=anchors) {
|
||||
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, axis=BACK, anchors=anchors) {
|
||||
rot(from=UP,to=FWD) {
|
||||
if (l > 0) {
|
||||
linear_extrude(height=l, center=true, slices=2) {
|
||||
teardrop2d(r=r, ang=ang, cap_h=cap_h);
|
||||
if (r1 == r2) {
|
||||
linear_extrude(height=l, center=true, slices=2) {
|
||||
teardrop2d(r=r1, ang=ang, cap_h=cap_h);
|
||||
}
|
||||
} else {
|
||||
hull() {
|
||||
up(l/2-0.001) {
|
||||
linear_extrude(height=0.001, center=false) {
|
||||
teardrop2d(r=r1, ang=ang, cap_h=cap_h1);
|
||||
}
|
||||
}
|
||||
down(l/2) {
|
||||
linear_extrude(height=0.001, center=false) {
|
||||
teardrop2d(r=r2, ang=ang, cap_h=cap_h2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1702,9 +1774,15 @@ module pie_slice(
|
|||
//
|
||||
// Example:
|
||||
// union() {
|
||||
// translate([0,2,-4]) cube([20, 4, 24], anchor=BOTTOM);
|
||||
// translate([0,-10,-4]) cube([20, 20, 4], anchor=BOTTOM);
|
||||
// color("green") interior_fillet(l=20, r=10, spin=180, orient=RIGHT);
|
||||
// translate([0,2,-4])
|
||||
// cube([20, 4, 24], anchor=BOTTOM);
|
||||
// translate([0,-10,-4])
|
||||
// cube([20, 20, 4], anchor=BOTTOM);
|
||||
// color("green")
|
||||
// interior_fillet(
|
||||
// l=20, r=10,
|
||||
// spin=180, orient=RIGHT
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// Example:
|
||||
|
@ -1762,21 +1840,30 @@ module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, anchor=FRONT+LEFT, spi
|
|||
// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP`
|
||||
// Example:
|
||||
// heightfield(size=[100,100], bottom=-20, data=[
|
||||
// for (y=[-180:4:180]) [for(x=[-180:4:180]) 10*cos(3*norm([x,y]))]
|
||||
// for (y=[-180:4:180]) [
|
||||
// for(x=[-180:4:180])
|
||||
// 10*cos(3*norm([x,y]))
|
||||
// ]
|
||||
// ]);
|
||||
// Example:
|
||||
// intersection() {
|
||||
// heightfield(size=[100,100], data=[
|
||||
// for (y=[-180:5:180]) [for(x=[-180:5:180]) 10+5*cos(3*x)*sin(3*y)]
|
||||
// for (y=[-180:5:180]) [
|
||||
// for(x=[-180:5:180])
|
||||
// 10+5*cos(3*x)*sin(3*y)
|
||||
// ]
|
||||
// ]);
|
||||
// cylinder(h=50,d=100);
|
||||
// }
|
||||
// Example(NORENDER): Heightfield by Function
|
||||
// Example: Heightfield by Function
|
||||
// fn = function (x,y) 10*sin(x*360)*cos(y*360);
|
||||
// heightfield(size=[100,100], data=fn);
|
||||
// Example(NORENDER): Heightfield by Function, with Specific Ranges
|
||||
// Example: Heightfield by Function, with Specific Ranges
|
||||
// fn = function (x,y) 2*cos(5*norm([x,y]));
|
||||
// heightfield(size=[100,100], bottom=-20, data=fn, xrange=[-180:2:180], yrange=[-180:2:180]);
|
||||
// heightfield(
|
||||
// size=[100,100], bottom=-20, data=fn,
|
||||
// xrange=[-180:2:180], yrange=[-180:2:180]
|
||||
// );
|
||||
module heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04:1], yrange=[-1:0.04:1], style="default", convexity=10, anchor=CENTER, spin=0, orient=UP)
|
||||
{
|
||||
size = is_num(size)? [size,size] : point2d(size);
|
||||
|
|
|
@ -307,15 +307,15 @@ module stroke(
|
|||
multmatrix(mat) polygon(endcap_shape2);
|
||||
}
|
||||
} else {
|
||||
quatsums = Q_Cumulative([
|
||||
quatsums = q_cumulative([
|
||||
for (i = idx(path2,e=-2)) let(
|
||||
vec1 = i==0? UP : unit(path2[i]-path2[i-1], UP),
|
||||
vec2 = unit(path2[i+1]-path2[i], UP),
|
||||
axis = vector_axis(vec1,vec2),
|
||||
ang = vector_angle(vec1,vec2)
|
||||
) Quat(axis,ang)
|
||||
) quat(axis,ang)
|
||||
]);
|
||||
rotmats = [for (q=quatsums) Q_Matrix4(q)];
|
||||
rotmats = [for (q=quatsums) q_matrix4(q)];
|
||||
sides = [
|
||||
for (i = idx(path2,e=-2))
|
||||
quantup(segs(max(widths[i],widths[i+1])/2),4)
|
||||
|
@ -959,7 +959,7 @@ function rect(size=1, center, rounding=0, chamfer=0, anchor, spin=0) =
|
|||
[-size.x/2, size.y/2],
|
||||
[ size.x/2, size.y/2]
|
||||
]
|
||||
) rot(spin, p=move(-vmul(anchor,size/2), p=path)) :
|
||||
) rot(spin, p=move(-v_mul(anchor,size/2), p=path)) :
|
||||
let(
|
||||
chamfer = is_list(chamfer)? chamfer : [for (i=[0:3]) chamfer],
|
||||
rounding = is_list(rounding)? rounding : [for (i=[0:3]) rounding],
|
||||
|
@ -978,7 +978,7 @@ function rect(size=1, center, rounding=0, chamfer=0, anchor, spin=0) =
|
|||
quad = quadorder[i],
|
||||
inset = insets[quad],
|
||||
cverts = quant(segs(inset),4)/4,
|
||||
cp = vmul(size/2-[inset,inset], quadpos[quad]),
|
||||
cp = v_mul(size/2-[inset,inset], quadpos[quad]),
|
||||
step = 90/cverts,
|
||||
angs =
|
||||
chamfer[quad] > 0? [0,-90]-90*[i,i] :
|
||||
|
|
36
skin.scad
36
skin.scad
|
@ -812,24 +812,30 @@ function _skin_tangent_match(poly1, poly2) =
|
|||
newbig = polygon_shift(big, shift),
|
||||
repeat_counts = [for(i=[0:len(small)-1]) posmod(cutpts[i]-select(cutpts,i-1),len(big))],
|
||||
newsmall = repeat_entries(small,repeat_counts)
|
||||
)
|
||||
assert(len(newsmall)==len(newbig), "Tangent alignment failed, probably because of insufficient points or a concave curve")
|
||||
swap ? [newbig, newsmall] : [newsmall, newbig];
|
||||
)
|
||||
assert(len(newsmall)==len(newbig), "Tangent alignment failed, probably because of insufficient points or a concave curve")
|
||||
swap ? [newbig, newsmall] : [newsmall, newbig];
|
||||
|
||||
|
||||
function _find_one_tangent(curve, edge, curve_offset=[0,0,0], closed=true) =
|
||||
let(
|
||||
angles =
|
||||
[for(i=[0:len(curve)-(closed?1:2)])
|
||||
let(
|
||||
plane = plane3pt( edge[0], edge[1], curve[i]),
|
||||
tangent = [curve[i], select(curve,i+1)]
|
||||
)
|
||||
plane_line_angle(plane,tangent)],
|
||||
zero_cross = [for(i=[0:len(curve)-(closed?1:2)]) if (sign(angles[i]) != sign(select(angles,i+1))) i],
|
||||
d = [for(i=zero_cross) distance_from_line(edge, curve[i]+curve_offset)]
|
||||
)
|
||||
zero_cross[min_index(d)];
|
||||
let(
|
||||
angles = [
|
||||
for (i = [0:len(curve)-(closed?1:2)])
|
||||
let(
|
||||
plane = plane3pt( edge[0], edge[1], curve[i]),
|
||||
tangent = [curve[i], select(curve,i+1)]
|
||||
) plane_line_angle(plane,tangent)
|
||||
],
|
||||
zero_cross = [
|
||||
for (i = [0:len(curve)-(closed?1:2)])
|
||||
if (sign(angles[i]) != sign(select(angles,i+1)))
|
||||
i
|
||||
],
|
||||
d = [
|
||||
for (i = zero_cross)
|
||||
point_line_distance(curve[i]+curve_offset, edge)
|
||||
]
|
||||
) zero_cross[min_index(d)];
|
||||
|
||||
|
||||
// Function: associate_vertices()
|
||||
|
|
1
std.scad
1
std.scad
|
@ -27,6 +27,7 @@ include <quaternions.scad>
|
|||
include <affine.scad>
|
||||
include <coords.scad>
|
||||
include <geometry.scad>
|
||||
include <hull.scad>
|
||||
include <regions.scad>
|
||||
include <strings.scad>
|
||||
include <skin.scad>
|
||||
|
|
|
@ -1264,4 +1264,36 @@ module test_poly_add(){
|
|||
}
|
||||
test_poly_add();
|
||||
|
||||
|
||||
module test_root_find(){
|
||||
flist = [
|
||||
function(x) x*x*x-2*x-5,
|
||||
function(x) 1-1/x/x,
|
||||
function(x) pow(x-3,3),
|
||||
function(x) pow(x-2,5),
|
||||
function(x) (let(xi=0.61489) -3062*(1-xi)*exp(-x)/(xi+(1-xi)*exp(-x)) -1013 + 1628/x),
|
||||
function(x) exp(x)-2-.01/x/x + .000002/x/x/x,
|
||||
];
|
||||
fint=[
|
||||
[0,4],
|
||||
[1e-4, 4],
|
||||
[0,6],
|
||||
[0,4],
|
||||
[1e-4,5],
|
||||
[-1,4]
|
||||
];
|
||||
answers = [2.094551481542328,
|
||||
1,
|
||||
3,
|
||||
2,
|
||||
1.037536033287040,
|
||||
0.7032048403631350
|
||||
];
|
||||
|
||||
roots = [for(i=idx(flist)) root_find(flist[i], fint[i][0], fint[i][1])];
|
||||
assert_approx(roots, answers, 1e-10);
|
||||
}
|
||||
test_root_find();
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
@ -1,403 +1,384 @@
|
|||
include <../std.scad>
|
||||
include <../strings.scad>
|
||||
|
||||
|
||||
function rec_cmp(a,b,eps=1e-9) =
|
||||
typeof(a)!=typeof(b)? false :
|
||||
is_num(a)? approx(a,b,eps=eps) :
|
||||
is_list(a)? len(a)==len(b) && all([for (i=idx(a)) rec_cmp(a[i],b[i],eps=eps)]) :
|
||||
a == b;
|
||||
|
||||
function Qstandard(q) = sign([for(qi=q) if( ! approx(qi,0)) qi,0 ][0])*q;
|
||||
function _q_standard(q) = sign([for(qi=q) if( ! approx(qi,0)) qi,0 ][0])*q;
|
||||
|
||||
module verify_f(actual,expected) {
|
||||
if (!rec_cmp(actual,expected)) {
|
||||
echo(str("Expected: ",fmt_float(expected,10)));
|
||||
echo(str(" : ",expected));
|
||||
echo(str("Actual : ",fmt_float(actual,10)));
|
||||
echo(str(" : ",actual));
|
||||
echo(str("Delta : ",fmt_float(expected-actual,10)));
|
||||
echo(str(" : ",expected-actual));
|
||||
assert(approx(expected,actual));
|
||||
}
|
||||
|
||||
module test_is_quaternion() {
|
||||
assert_approx(is_quaternion([0]),false);
|
||||
assert_approx(is_quaternion([0,0,0,0]),false);
|
||||
assert_approx(is_quaternion([1,0,2,0]),true);
|
||||
assert_approx(is_quaternion([1,0,2,0,0]),false);
|
||||
}
|
||||
test_is_quaternion();
|
||||
|
||||
|
||||
module test_Q_is_quat() {
|
||||
verify_f(Q_is_quat([0]),false);
|
||||
verify_f(Q_is_quat([0,0,0,0]),false);
|
||||
verify_f(Q_is_quat([1,0,2,0]),true);
|
||||
verify_f(Q_is_quat([1,0,2,0,0]),false);
|
||||
module test_quat() {
|
||||
assert_approx(quat(UP,0),[0,0,0,1]);
|
||||
assert_approx(quat(FWD,0),[0,0,0,1]);
|
||||
assert_approx(quat(LEFT,0),[0,0,0,1]);
|
||||
assert_approx(quat(UP,45),[0,0,0.3826834324,0.9238795325]);
|
||||
assert_approx(quat(LEFT,45),[-0.3826834324, 0, 0, 0.9238795325]);
|
||||
assert_approx(quat(BACK,45),[0,0.3826834323,0,0.9238795325]);
|
||||
assert_approx(quat(FWD+RIGHT,30),[0.1830127019, -0.1830127019, 0, 0.9659258263]);
|
||||
}
|
||||
test_Q_is_quat();
|
||||
test_quat();
|
||||
|
||||
|
||||
module test_Quat() {
|
||||
verify_f(Quat(UP,0),[0,0,0,1]);
|
||||
verify_f(Quat(FWD,0),[0,0,0,1]);
|
||||
verify_f(Quat(LEFT,0),[0,0,0,1]);
|
||||
verify_f(Quat(UP,45),[0,0,0.3826834324,0.9238795325]);
|
||||
verify_f(Quat(LEFT,45),[-0.3826834324, 0, 0, 0.9238795325]);
|
||||
verify_f(Quat(BACK,45),[0,0.3826834323,0,0.9238795325]);
|
||||
verify_f(Quat(FWD+RIGHT,30),[0.1830127019, -0.1830127019, 0, 0.9659258263]);
|
||||
module test_quat_x() {
|
||||
assert_approx(quat_x(0),[0,0,0,1]);
|
||||
assert_approx(quat_x(35),[0.3007057995,0,0,0.9537169507]);
|
||||
assert_approx(quat_x(45),[0.3826834324,0,0,0.9238795325]);
|
||||
}
|
||||
test_Quat();
|
||||
test_quat_x();
|
||||
|
||||
|
||||
module test_QuatX() {
|
||||
verify_f(QuatX(0),[0,0,0,1]);
|
||||
verify_f(QuatX(35),[0.3007057995,0,0,0.9537169507]);
|
||||
verify_f(QuatX(45),[0.3826834324,0,0,0.9238795325]);
|
||||
module test_quat_y() {
|
||||
assert_approx(quat_y(0),[0,0,0,1]);
|
||||
assert_approx(quat_y(35),[0,0.3007057995,0,0.9537169507]);
|
||||
assert_approx(quat_y(45),[0,0.3826834323,0,0.9238795325]);
|
||||
}
|
||||
test_QuatX();
|
||||
test_quat_y();
|
||||
|
||||
|
||||
module test_QuatY() {
|
||||
verify_f(QuatY(0),[0,0,0,1]);
|
||||
verify_f(QuatY(35),[0,0.3007057995,0,0.9537169507]);
|
||||
verify_f(QuatY(45),[0,0.3826834323,0,0.9238795325]);
|
||||
module test_quat_z() {
|
||||
assert_approx(quat_z(0),[0,0,0,1]);
|
||||
assert_approx(quat_z(36),[0,0,0.3090169944,0.9510565163]);
|
||||
assert_approx(quat_z(45),[0,0,0.3826834324,0.9238795325]);
|
||||
}
|
||||
test_QuatY();
|
||||
test_quat_z();
|
||||
|
||||
|
||||
module test_QuatZ() {
|
||||
verify_f(QuatZ(0),[0,0,0,1]);
|
||||
verify_f(QuatZ(36),[0,0,0.3090169944,0.9510565163]);
|
||||
verify_f(QuatZ(45),[0,0,0.3826834324,0.9238795325]);
|
||||
module test_quat_xyz() {
|
||||
assert_approx(quat_xyz([0,0,0]), [0,0,0,1]);
|
||||
assert_approx(quat_xyz([30,0,0]), [0.2588190451, 0, 0, 0.9659258263]);
|
||||
assert_approx(quat_xyz([90,0,0]), [0.7071067812, 0, 0, 0.7071067812]);
|
||||
assert_approx(quat_xyz([-270,0,0]), [-0.7071067812, 0, 0, -0.7071067812]);
|
||||
assert_approx(quat_xyz([180,0,0]), [1,0,0,0]);
|
||||
assert_approx(quat_xyz([270,0,0]), [0.7071067812, 0, 0, -0.7071067812]);
|
||||
assert_approx(quat_xyz([-90,0,0]), [-0.7071067812, 0, 0, 0.7071067812]);
|
||||
assert_approx(quat_xyz([360,0,0]), [0,0,0,-1]);
|
||||
|
||||
assert_approx(quat_xyz([0,0,0]), [0,0,0,1]);
|
||||
assert_approx(quat_xyz([0,30,0]), [0, 0.2588190451, 0, 0.9659258263]);
|
||||
assert_approx(quat_xyz([0,90,0]), [0, 0.7071067812, 0, 0.7071067812]);
|
||||
assert_approx(quat_xyz([0,-270,0]), [0, -0.7071067812, 0, -0.7071067812]);
|
||||
assert_approx(quat_xyz([0,180,0]), [0,1,0,0]);
|
||||
assert_approx(quat_xyz([0,270,0]), [0, 0.7071067812, 0, -0.7071067812]);
|
||||
assert_approx(quat_xyz([0,-90,0]), [0, -0.7071067812, 0, 0.7071067812]);
|
||||
assert_approx(quat_xyz([0,360,0]), [0,0,0,-1]);
|
||||
|
||||
assert_approx(quat_xyz([0,0,0]), [0,0,0,1]);
|
||||
assert_approx(quat_xyz([0,0,30]), [0, 0, 0.2588190451, 0.9659258263]);
|
||||
assert_approx(quat_xyz([0,0,90]), [0, 0, 0.7071067812, 0.7071067812]);
|
||||
assert_approx(quat_xyz([0,0,-270]), [0, 0, -0.7071067812, -0.7071067812]);
|
||||
assert_approx(quat_xyz([0,0,180]), [0,0,1,0]);
|
||||
assert_approx(quat_xyz([0,0,270]), [0, 0, 0.7071067812, -0.7071067812]);
|
||||
assert_approx(quat_xyz([0,0,-90]), [0, 0, -0.7071067812, 0.7071067812]);
|
||||
assert_approx(quat_xyz([0,0,360]), [0,0,0,-1]);
|
||||
|
||||
assert_approx(quat_xyz([30,30,30]), [0.1767766953, 0.3061862178, 0.1767766953, 0.9185586535]);
|
||||
assert_approx(quat_xyz([12,34,56]), [-0.04824789229, 0.3036636044, 0.4195145429, 0.8540890495]);
|
||||
}
|
||||
test_QuatZ();
|
||||
test_quat_xyz();
|
||||
|
||||
|
||||
module test_QuatXYZ() {
|
||||
verify_f(QuatXYZ([0,0,0]), [0,0,0,1]);
|
||||
verify_f(QuatXYZ([30,0,0]), [0.2588190451, 0, 0, 0.9659258263]);
|
||||
verify_f(QuatXYZ([90,0,0]), [0.7071067812, 0, 0, 0.7071067812]);
|
||||
verify_f(QuatXYZ([-270,0,0]), [-0.7071067812, 0, 0, -0.7071067812]);
|
||||
verify_f(QuatXYZ([180,0,0]), [1,0,0,0]);
|
||||
verify_f(QuatXYZ([270,0,0]), [0.7071067812, 0, 0, -0.7071067812]);
|
||||
verify_f(QuatXYZ([-90,0,0]), [-0.7071067812, 0, 0, 0.7071067812]);
|
||||
verify_f(QuatXYZ([360,0,0]), [0,0,0,-1]);
|
||||
|
||||
verify_f(QuatXYZ([0,0,0]), [0,0,0,1]);
|
||||
verify_f(QuatXYZ([0,30,0]), [0, 0.2588190451, 0, 0.9659258263]);
|
||||
verify_f(QuatXYZ([0,90,0]), [0, 0.7071067812, 0, 0.7071067812]);
|
||||
verify_f(QuatXYZ([0,-270,0]), [0, -0.7071067812, 0, -0.7071067812]);
|
||||
verify_f(QuatXYZ([0,180,0]), [0,1,0,0]);
|
||||
verify_f(QuatXYZ([0,270,0]), [0, 0.7071067812, 0, -0.7071067812]);
|
||||
verify_f(QuatXYZ([0,-90,0]), [0, -0.7071067812, 0, 0.7071067812]);
|
||||
verify_f(QuatXYZ([0,360,0]), [0,0,0,-1]);
|
||||
|
||||
verify_f(QuatXYZ([0,0,0]), [0,0,0,1]);
|
||||
verify_f(QuatXYZ([0,0,30]), [0, 0, 0.2588190451, 0.9659258263]);
|
||||
verify_f(QuatXYZ([0,0,90]), [0, 0, 0.7071067812, 0.7071067812]);
|
||||
verify_f(QuatXYZ([0,0,-270]), [0, 0, -0.7071067812, -0.7071067812]);
|
||||
verify_f(QuatXYZ([0,0,180]), [0,0,1,0]);
|
||||
verify_f(QuatXYZ([0,0,270]), [0, 0, 0.7071067812, -0.7071067812]);
|
||||
verify_f(QuatXYZ([0,0,-90]), [0, 0, -0.7071067812, 0.7071067812]);
|
||||
verify_f(QuatXYZ([0,0,360]), [0,0,0,-1]);
|
||||
|
||||
verify_f(QuatXYZ([30,30,30]), [0.1767766953, 0.3061862178, 0.1767766953, 0.9185586535]);
|
||||
verify_f(QuatXYZ([12,34,56]), [-0.04824789229, 0.3036636044, 0.4195145429, 0.8540890495]);
|
||||
module test_q_from_to() {
|
||||
assert_approx(q_mul(q_from_to([1,2,3], [4,5,2]),q_from_to([4,5,2], [1,2,3])), q_ident());
|
||||
assert_approx(q_matrix4(q_from_to([1,2,3], [4,5,2])), rot(from=[1,2,3],to=[4,5,2]));
|
||||
assert_approx(q_rot(q_from_to([1,2,3], -[1,2,3]),[1,2,3]), -[1,2,3]);
|
||||
assert_approx(unit(q_rot(q_from_to([1,2,3], [4,5,2]),[1,2,3])), unit([4,5,2]));
|
||||
}
|
||||
test_QuatXYZ();
|
||||
test_q_from_to();
|
||||
|
||||
|
||||
module test_Q_From_to() {
|
||||
verify_f(Q_Mul(Q_From_to([1,2,3], [4,5,2]),Q_From_to([4,5,2], [1,2,3])), Q_Ident());
|
||||
verify_f(Q_Matrix4(Q_From_to([1,2,3], [4,5,2])), rot(from=[1,2,3],to=[4,5,2]));
|
||||
verify_f(Qrot(Q_From_to([1,2,3], -[1,2,3]),[1,2,3]), -[1,2,3]);
|
||||
verify_f(unit(Qrot(Q_From_to([1,2,3], [4,5,2]),[1,2,3])), unit([4,5,2]));
|
||||
module test_q_ident() {
|
||||
assert_approx(q_ident(), [0,0,0,1]);
|
||||
}
|
||||
test_Q_From_to();
|
||||
test_q_ident();
|
||||
|
||||
|
||||
module test_Q_Ident() {
|
||||
verify_f(Q_Ident(), [0,0,0,1]);
|
||||
module test_q_add_s() {
|
||||
assert_approx(q_add_s([0,0,0,1],3),[0,0,0,4]);
|
||||
assert_approx(q_add_s([0,0,1,0],3),[0,0,1,3]);
|
||||
assert_approx(q_add_s([0,1,0,0],3),[0,1,0,3]);
|
||||
assert_approx(q_add_s([1,0,0,0],3),[1,0,0,3]);
|
||||
assert_approx(q_add_s(quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, 1.979924705]);
|
||||
}
|
||||
test_Q_Ident();
|
||||
test_q_add_s();
|
||||
|
||||
|
||||
module test_Q_Add_S() {
|
||||
verify_f(Q_Add_S([0,0,0,1],3),[0,0,0,4]);
|
||||
verify_f(Q_Add_S([0,0,1,0],3),[0,0,1,3]);
|
||||
verify_f(Q_Add_S([0,1,0,0],3),[0,1,0,3]);
|
||||
verify_f(Q_Add_S([1,0,0,0],3),[1,0,0,3]);
|
||||
verify_f(Q_Add_S(Quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, 1.979924705]);
|
||||
module test_q_sub_s() {
|
||||
assert_approx(q_sub_s([0,0,0,1],3),[0,0,0,-2]);
|
||||
assert_approx(q_sub_s([0,0,1,0],3),[0,0,1,-3]);
|
||||
assert_approx(q_sub_s([0,1,0,0],3),[0,1,0,-3]);
|
||||
assert_approx(q_sub_s([1,0,0,0],3),[1,0,0,-3]);
|
||||
assert_approx(q_sub_s(quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, -0.02007529538]);
|
||||
}
|
||||
test_Q_Add_S();
|
||||
test_q_sub_s();
|
||||
|
||||
|
||||
module test_Q_Sub_S() {
|
||||
verify_f(Q_Sub_S([0,0,0,1],3),[0,0,0,-2]);
|
||||
verify_f(Q_Sub_S([0,0,1,0],3),[0,0,1,-3]);
|
||||
verify_f(Q_Sub_S([0,1,0,0],3),[0,1,0,-3]);
|
||||
verify_f(Q_Sub_S([1,0,0,0],3),[1,0,0,-3]);
|
||||
verify_f(Q_Sub_S(Quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, -0.02007529538]);
|
||||
module test_q_mul_s() {
|
||||
assert_approx(q_mul_s([0,0,0,1],3),[0,0,0,3]);
|
||||
assert_approx(q_mul_s([0,0,1,0],3),[0,0,3,0]);
|
||||
assert_approx(q_mul_s([0,1,0,0],3),[0,3,0,0]);
|
||||
assert_approx(q_mul_s([1,0,0,0],3),[3,0,0,0]);
|
||||
assert_approx(q_mul_s([1,0,0,1],3),[3,0,0,3]);
|
||||
assert_approx(q_mul_s(quat(LEFT+FWD,23),4),[-0.5638976735, -0.5638976735, 0, 3.919698818]);
|
||||
}
|
||||
test_Q_Sub_S();
|
||||
test_q_mul_s();
|
||||
|
||||
|
||||
module test_Q_Mul_S() {
|
||||
verify_f(Q_Mul_S([0,0,0,1],3),[0,0,0,3]);
|
||||
verify_f(Q_Mul_S([0,0,1,0],3),[0,0,3,0]);
|
||||
verify_f(Q_Mul_S([0,1,0,0],3),[0,3,0,0]);
|
||||
verify_f(Q_Mul_S([1,0,0,0],3),[3,0,0,0]);
|
||||
verify_f(Q_Mul_S([1,0,0,1],3),[3,0,0,3]);
|
||||
verify_f(Q_Mul_S(Quat(LEFT+FWD,23),4),[-0.5638976735, -0.5638976735, 0, 3.919698818]);
|
||||
|
||||
module test_q_div_s() {
|
||||
assert_approx(q_div_s([0,0,0,1],3),[0,0,0,1/3]);
|
||||
assert_approx(q_div_s([0,0,1,0],3),[0,0,1/3,0]);
|
||||
assert_approx(q_div_s([0,1,0,0],3),[0,1/3,0,0]);
|
||||
assert_approx(q_div_s([1,0,0,0],3),[1/3,0,0,0]);
|
||||
assert_approx(q_div_s([1,0,0,1],3),[1/3,0,0,1/3]);
|
||||
assert_approx(q_div_s(quat(LEFT+FWD,23),4),[-0.03524360459, -0.03524360459, 0, 0.2449811762]);
|
||||
}
|
||||
test_Q_Mul_S();
|
||||
test_q_div_s();
|
||||
|
||||
|
||||
|
||||
module test_Q_Div_S() {
|
||||
verify_f(Q_Div_S([0,0,0,1],3),[0,0,0,1/3]);
|
||||
verify_f(Q_Div_S([0,0,1,0],3),[0,0,1/3,0]);
|
||||
verify_f(Q_Div_S([0,1,0,0],3),[0,1/3,0,0]);
|
||||
verify_f(Q_Div_S([1,0,0,0],3),[1/3,0,0,0]);
|
||||
verify_f(Q_Div_S([1,0,0,1],3),[1/3,0,0,1/3]);
|
||||
verify_f(Q_Div_S(Quat(LEFT+FWD,23),4),[-0.03524360459, -0.03524360459, 0, 0.2449811762]);
|
||||
module test_q_add() {
|
||||
assert_approx(q_add([2,3,4,5],[-1,-1,-1,-1]),[1,2,3,4]);
|
||||
assert_approx(q_add([2,3,4,5],[-3,-3,-3,-3]),[-1,0,1,2]);
|
||||
assert_approx(q_add([2,3,4,5],[0,0,0,0]),[2,3,4,5]);
|
||||
assert_approx(q_add([2,3,4,5],[1,1,1,1]),[3,4,5,6]);
|
||||
assert_approx(q_add([2,3,4,5],[1,0,0,0]),[3,3,4,5]);
|
||||
assert_approx(q_add([2,3,4,5],[0,1,0,0]),[2,4,4,5]);
|
||||
assert_approx(q_add([2,3,4,5],[0,0,1,0]),[2,3,5,5]);
|
||||
assert_approx(q_add([2,3,4,5],[0,0,0,1]),[2,3,4,6]);
|
||||
assert_approx(q_add([2,3,4,5],[2,1,2,1]),[4,4,6,6]);
|
||||
assert_approx(q_add([2,3,4,5],[1,2,1,2]),[3,5,5,7]);
|
||||
}
|
||||
test_Q_Div_S();
|
||||
test_q_add();
|
||||
|
||||
|
||||
module test_Q_Add() {
|
||||
verify_f(Q_Add([2,3,4,5],[-1,-1,-1,-1]),[1,2,3,4]);
|
||||
verify_f(Q_Add([2,3,4,5],[-3,-3,-3,-3]),[-1,0,1,2]);
|
||||
verify_f(Q_Add([2,3,4,5],[0,0,0,0]),[2,3,4,5]);
|
||||
verify_f(Q_Add([2,3,4,5],[1,1,1,1]),[3,4,5,6]);
|
||||
verify_f(Q_Add([2,3,4,5],[1,0,0,0]),[3,3,4,5]);
|
||||
verify_f(Q_Add([2,3,4,5],[0,1,0,0]),[2,4,4,5]);
|
||||
verify_f(Q_Add([2,3,4,5],[0,0,1,0]),[2,3,5,5]);
|
||||
verify_f(Q_Add([2,3,4,5],[0,0,0,1]),[2,3,4,6]);
|
||||
verify_f(Q_Add([2,3,4,5],[2,1,2,1]),[4,4,6,6]);
|
||||
verify_f(Q_Add([2,3,4,5],[1,2,1,2]),[3,5,5,7]);
|
||||
module test_q_sub() {
|
||||
assert_approx(q_sub([2,3,4,5],[-1,-1,-1,-1]),[3,4,5,6]);
|
||||
assert_approx(q_sub([2,3,4,5],[-3,-3,-3,-3]),[5,6,7,8]);
|
||||
assert_approx(q_sub([2,3,4,5],[0,0,0,0]),[2,3,4,5]);
|
||||
assert_approx(q_sub([2,3,4,5],[1,1,1,1]),[1,2,3,4]);
|
||||
assert_approx(q_sub([2,3,4,5],[1,0,0,0]),[1,3,4,5]);
|
||||
assert_approx(q_sub([2,3,4,5],[0,1,0,0]),[2,2,4,5]);
|
||||
assert_approx(q_sub([2,3,4,5],[0,0,1,0]),[2,3,3,5]);
|
||||
assert_approx(q_sub([2,3,4,5],[0,0,0,1]),[2,3,4,4]);
|
||||
assert_approx(q_sub([2,3,4,5],[2,1,2,1]),[0,2,2,4]);
|
||||
assert_approx(q_sub([2,3,4,5],[1,2,1,2]),[1,1,3,3]);
|
||||
}
|
||||
test_Q_Add();
|
||||
test_q_sub();
|
||||
|
||||
|
||||
module test_Q_Sub() {
|
||||
verify_f(Q_Sub([2,3,4,5],[-1,-1,-1,-1]),[3,4,5,6]);
|
||||
verify_f(Q_Sub([2,3,4,5],[-3,-3,-3,-3]),[5,6,7,8]);
|
||||
verify_f(Q_Sub([2,3,4,5],[0,0,0,0]),[2,3,4,5]);
|
||||
verify_f(Q_Sub([2,3,4,5],[1,1,1,1]),[1,2,3,4]);
|
||||
verify_f(Q_Sub([2,3,4,5],[1,0,0,0]),[1,3,4,5]);
|
||||
verify_f(Q_Sub([2,3,4,5],[0,1,0,0]),[2,2,4,5]);
|
||||
verify_f(Q_Sub([2,3,4,5],[0,0,1,0]),[2,3,3,5]);
|
||||
verify_f(Q_Sub([2,3,4,5],[0,0,0,1]),[2,3,4,4]);
|
||||
verify_f(Q_Sub([2,3,4,5],[2,1,2,1]),[0,2,2,4]);
|
||||
verify_f(Q_Sub([2,3,4,5],[1,2,1,2]),[1,1,3,3]);
|
||||
module test_q_mul() {
|
||||
assert_approx(q_mul(quat_z(30),quat_x(57)),[0.4608999698, 0.1234977747, 0.2274546059, 0.8488721457]);
|
||||
assert_approx(q_mul(quat_y(30),quat_z(23)),[0.05160021841, 0.2536231763, 0.1925746368, 0.94653458]);
|
||||
}
|
||||
test_Q_Sub();
|
||||
test_q_mul();
|
||||
|
||||
|
||||
module test_Q_Mul() {
|
||||
verify_f(Q_Mul(QuatZ(30),QuatX(57)),[0.4608999698, 0.1234977747, 0.2274546059, 0.8488721457]);
|
||||
verify_f(Q_Mul(QuatY(30),QuatZ(23)),[0.05160021841, 0.2536231763, 0.1925746368, 0.94653458]);
|
||||
module test_q_cumulative() {
|
||||
assert_approx(q_cumulative([quat_z(30),quat_x(57),quat_y(18)]),[[0, 0, 0.2588190451, 0.9659258263], [0.4608999698, -0.1234977747, 0.2274546059, 0.8488721457], [0.4908072659, 0.01081554785, 0.1525536221, 0.8577404293]]);
|
||||
}
|
||||
test_Q_Mul();
|
||||
test_q_cumulative();
|
||||
|
||||
|
||||
module test_Q_Cumulative() {
|
||||
verify_f(Q_Cumulative([QuatZ(30),QuatX(57),QuatY(18)]),[[0, 0, 0.2588190451, 0.9659258263], [0.4608999698, -0.1234977747, 0.2274546059, 0.8488721457], [0.4908072659, 0.01081554785, 0.1525536221, 0.8577404293]]);
|
||||
module test_q_dot() {
|
||||
assert_approx(q_dot(quat_z(30),quat_x(57)),0.8488721457);
|
||||
assert_approx(q_dot(quat_y(30),quat_z(23)),0.94653458);
|
||||
}
|
||||
test_Q_Cumulative();
|
||||
test_q_dot();
|
||||
|
||||
|
||||
module test_Q_Dot() {
|
||||
verify_f(Q_Dot(QuatZ(30),QuatX(57)),0.8488721457);
|
||||
verify_f(Q_Dot(QuatY(30),QuatZ(23)),0.94653458);
|
||||
module test_q_neg() {
|
||||
assert_approx(q_neg([1,0,0,1]),[-1,0,0,-1]);
|
||||
assert_approx(q_neg([0,1,1,0]),[0,-1,-1,0]);
|
||||
assert_approx(q_neg(quat_xyz([23,45,67])),[0.0533818345,-0.4143703268,-0.4360652669,-0.7970537592]);
|
||||
}
|
||||
test_Q_Dot();
|
||||
test_q_neg();
|
||||
|
||||
|
||||
module test_Q_Neg() {
|
||||
verify_f(Q_Neg([1,0,0,1]),[-1,0,0,-1]);
|
||||
verify_f(Q_Neg([0,1,1,0]),[0,-1,-1,0]);
|
||||
verify_f(Q_Neg(QuatXYZ([23,45,67])),[0.0533818345,-0.4143703268,-0.4360652669,-0.7970537592]);
|
||||
module test_q_conj() {
|
||||
assert_approx(q_conj([1,0,0,1]),[-1,0,0,1]);
|
||||
assert_approx(q_conj([0,1,1,0]),[0,-1,-1,0]);
|
||||
assert_approx(q_conj(quat_xyz([23,45,67])),[0.0533818345, -0.4143703268, -0.4360652669, 0.7970537592]);
|
||||
}
|
||||
test_Q_Neg();
|
||||
test_q_conj();
|
||||
|
||||
|
||||
module test_Q_Conj() {
|
||||
verify_f(Q_Conj([1,0,0,1]),[-1,0,0,1]);
|
||||
verify_f(Q_Conj([0,1,1,0]),[0,-1,-1,0]);
|
||||
verify_f(Q_Conj(QuatXYZ([23,45,67])),[0.0533818345, -0.4143703268, -0.4360652669, 0.7970537592]);
|
||||
module test_q_inverse() {
|
||||
|
||||
assert_approx(q_inverse([1,0,0,1]),[-1,0,0,1]/sqrt(2));
|
||||
assert_approx(q_inverse([0,1,1,0]),[0,-1,-1,0]/sqrt(2));
|
||||
assert_approx(q_inverse(quat_xyz([23,45,67])),q_conj(quat_xyz([23,45,67])));
|
||||
assert_approx(q_mul(q_inverse(quat_xyz([23,45,67])),quat_xyz([23,45,67])),q_ident());
|
||||
}
|
||||
test_Q_Conj();
|
||||
test_q_inverse();
|
||||
|
||||
|
||||
module test_Q_Inverse() {
|
||||
|
||||
verify_f(Q_Inverse([1,0,0,1]),[-1,0,0,1]/sqrt(2));
|
||||
verify_f(Q_Inverse([0,1,1,0]),[0,-1,-1,0]/sqrt(2));
|
||||
verify_f(Q_Inverse(QuatXYZ([23,45,67])),Q_Conj(QuatXYZ([23,45,67])));
|
||||
verify_f(Q_Mul(Q_Inverse(QuatXYZ([23,45,67])),QuatXYZ([23,45,67])),Q_Ident());
|
||||
module test_q_Norm() {
|
||||
assert_approx(q_norm([1,0,0,1]),1.414213562);
|
||||
assert_approx(q_norm([0,1,1,0]),1.414213562);
|
||||
assert_approx(q_norm(quat_xyz([23,45,67])),1);
|
||||
}
|
||||
test_Q_Inverse();
|
||||
test_q_Norm();
|
||||
|
||||
|
||||
module test_Q_Norm() {
|
||||
verify_f(Q_Norm([1,0,0,1]),1.414213562);
|
||||
verify_f(Q_Norm([0,1,1,0]),1.414213562);
|
||||
verify_f(Q_Norm(QuatXYZ([23,45,67])),1);
|
||||
module test_q_normalize() {
|
||||
assert_approx(q_normalize([1,0,0,1]),[0.7071067812, 0, 0, 0.7071067812]);
|
||||
assert_approx(q_normalize([0,1,1,0]),[0, 0.7071067812, 0.7071067812, 0]);
|
||||
assert_approx(q_normalize(quat_xyz([23,45,67])),[-0.0533818345, 0.4143703268, 0.4360652669, 0.7970537592]);
|
||||
}
|
||||
test_Q_Norm();
|
||||
test_q_normalize();
|
||||
|
||||
|
||||
module test_Q_Normalize() {
|
||||
verify_f(Q_Normalize([1,0,0,1]),[0.7071067812, 0, 0, 0.7071067812]);
|
||||
verify_f(Q_Normalize([0,1,1,0]),[0, 0.7071067812, 0.7071067812, 0]);
|
||||
verify_f(Q_Normalize(QuatXYZ([23,45,67])),[-0.0533818345, 0.4143703268, 0.4360652669, 0.7970537592]);
|
||||
module test_q_dist() {
|
||||
assert_approx(q_dist(quat_xyz([23,45,67]),quat_xyz([23,45,67])),0);
|
||||
assert_approx(q_dist(quat_xyz([23,45,67]),quat_xyz([12,34,56])),0.1257349854);
|
||||
}
|
||||
test_Q_Normalize();
|
||||
test_q_dist();
|
||||
|
||||
|
||||
module test_Q_Dist() {
|
||||
verify_f(Q_Dist(QuatXYZ([23,45,67]),QuatXYZ([23,45,67])),0);
|
||||
verify_f(Q_Dist(QuatXYZ([23,45,67]),QuatXYZ([12,34,56])),0.1257349854);
|
||||
module test_q_slerp() {
|
||||
assert_approx(q_slerp(quat_x(45),quat_y(30),0.0),quat_x(45));
|
||||
assert_approx(q_slerp(quat_x(45),quat_y(30),0.5),[0.1967063121, 0.1330377423, 0, 0.9713946602]);
|
||||
assert_approx(q_slerp(quat_x(45),quat_y(30),1.0),quat_y(30));
|
||||
}
|
||||
test_Q_Dist();
|
||||
test_q_slerp();
|
||||
|
||||
|
||||
module test_Q_Slerp() {
|
||||
verify_f(Q_Slerp(QuatX(45),QuatY(30),0.0),QuatX(45));
|
||||
verify_f(Q_Slerp(QuatX(45),QuatY(30),0.5),[0.1967063121, 0.1330377423, 0, 0.9713946602]);
|
||||
verify_f(Q_Slerp(QuatX(45),QuatY(30),1.0),QuatY(30));
|
||||
module test_q_matrix3() {
|
||||
assert_approx(q_matrix3(quat_z(37)),rot(37,planar=true));
|
||||
assert_approx(q_matrix3(quat_z(-49)),rot(-49,planar=true));
|
||||
}
|
||||
test_Q_Slerp();
|
||||
test_q_matrix3();
|
||||
|
||||
|
||||
module test_Q_Matrix3() {
|
||||
verify_f(Q_Matrix3(QuatZ(37)),rot(37,planar=true));
|
||||
verify_f(Q_Matrix3(QuatZ(-49)),rot(-49,planar=true));
|
||||
module test_q_matrix4() {
|
||||
assert_approx(q_matrix4(quat_z(37)),rot(37));
|
||||
assert_approx(q_matrix4(quat_z(-49)),rot(-49));
|
||||
assert_approx(q_matrix4(quat_x(37)),rot([37,0,0]));
|
||||
assert_approx(q_matrix4(quat_y(37)),rot([0,37,0]));
|
||||
assert_approx(q_matrix4(quat_xyz([12,34,56])),rot([12,34,56]));
|
||||
}
|
||||
test_Q_Matrix3();
|
||||
test_q_matrix4();
|
||||
|
||||
|
||||
module test_Q_Matrix4() {
|
||||
verify_f(Q_Matrix4(QuatZ(37)),rot(37));
|
||||
verify_f(Q_Matrix4(QuatZ(-49)),rot(-49));
|
||||
verify_f(Q_Matrix4(QuatX(37)),rot([37,0,0]));
|
||||
verify_f(Q_Matrix4(QuatY(37)),rot([0,37,0]));
|
||||
verify_f(Q_Matrix4(QuatXYZ([12,34,56])),rot([12,34,56]));
|
||||
module test_q_axis() {
|
||||
assert_approx(q_axis(quat_x(37)),RIGHT);
|
||||
assert_approx(q_axis(quat_x(-37)),LEFT);
|
||||
assert_approx(q_axis(quat_y(37)),BACK);
|
||||
assert_approx(q_axis(quat_y(-37)),FWD);
|
||||
assert_approx(q_axis(quat_z(37)),UP);
|
||||
assert_approx(q_axis(quat_z(-37)),DOWN);
|
||||
}
|
||||
test_Q_Matrix4();
|
||||
test_q_axis();
|
||||
|
||||
|
||||
module test_Q_Axis() {
|
||||
verify_f(Q_Axis(QuatX(37)),RIGHT);
|
||||
verify_f(Q_Axis(QuatX(-37)),LEFT);
|
||||
verify_f(Q_Axis(QuatY(37)),BACK);
|
||||
verify_f(Q_Axis(QuatY(-37)),FWD);
|
||||
verify_f(Q_Axis(QuatZ(37)),UP);
|
||||
verify_f(Q_Axis(QuatZ(-37)),DOWN);
|
||||
module test_q_angle() {
|
||||
assert_approx(q_angle(quat_x(0)),0);
|
||||
assert_approx(q_angle(quat_y(0)),0);
|
||||
assert_approx(q_angle(quat_z(0)),0);
|
||||
assert_approx(q_angle(quat_x(37)),37);
|
||||
assert_approx(q_angle(quat_x(-37)),37);
|
||||
assert_approx(q_angle(quat_y(37)),37);
|
||||
assert_approx(q_angle(quat_y(-37)),37);
|
||||
assert_approx(q_angle(quat_z(37)),37);
|
||||
assert_approx(q_angle(quat_z(-37)),37);
|
||||
|
||||
assert_approx(q_angle(quat_z(-37),quat_z(-37)), 0);
|
||||
assert_approx(q_angle(quat_z( 37.123),quat_z(-37.123)), 74.246);
|
||||
assert_approx(q_angle(quat_x( 37),quat_y(-37)), 51.86293283);
|
||||
}
|
||||
test_Q_Axis();
|
||||
test_q_angle();
|
||||
|
||||
|
||||
module test_Q_Angle() {
|
||||
verify_f(Q_Angle(QuatX(0)),0);
|
||||
verify_f(Q_Angle(QuatY(0)),0);
|
||||
verify_f(Q_Angle(QuatZ(0)),0);
|
||||
verify_f(Q_Angle(QuatX(37)),37);
|
||||
verify_f(Q_Angle(QuatX(-37)),37);
|
||||
verify_f(Q_Angle(QuatY(37)),37);
|
||||
verify_f(Q_Angle(QuatY(-37)),37);
|
||||
verify_f(Q_Angle(QuatZ(37)),37);
|
||||
verify_f(Q_Angle(QuatZ(-37)),37);
|
||||
|
||||
verify_f(Q_Angle(QuatZ(-37),QuatZ(-37)), 0);
|
||||
verify_f(Q_Angle(QuatZ( 37.123),QuatZ(-37.123)), 74.246);
|
||||
verify_f(Q_Angle(QuatX( 37),QuatY(-37)), 51.86293283);
|
||||
module test_q_rot() {
|
||||
assert_approx(q_rot(quat_xyz([12,34,56])),rot([12,34,56]));
|
||||
assert_approx(q_rot(quat_xyz([12,34,56]),p=[2,3,4]),rot([12,34,56],p=[2,3,4]));
|
||||
assert_approx(q_rot(quat_xyz([12,34,56]),p=[[2,3,4],[4,9,6]]),rot([12,34,56],p=[[2,3,4],[4,9,6]]));
|
||||
}
|
||||
test_Q_Angle();
|
||||
test_q_rot();
|
||||
|
||||
|
||||
module test_Qrot() {
|
||||
verify_f(Qrot(QuatXYZ([12,34,56])),rot([12,34,56]));
|
||||
verify_f(Qrot(QuatXYZ([12,34,56]),p=[2,3,4]),rot([12,34,56],p=[2,3,4]));
|
||||
verify_f(Qrot(QuatXYZ([12,34,56]),p=[[2,3,4],[4,9,6]]),rot([12,34,56],p=[[2,3,4],[4,9,6]]));
|
||||
module test_q_rotation() {
|
||||
assert_approx(_q_standard(q_rotation(q_matrix3(quat([12,34,56],33)))),_q_standard(quat([12,34,56],33)));
|
||||
assert_approx(q_matrix3(q_rotation(q_matrix3(quat_xyz([12,34,56])))),
|
||||
q_matrix3(quat_xyz([12,34,56])));
|
||||
}
|
||||
test_Qrot();
|
||||
test_q_rotation();
|
||||
|
||||
|
||||
module test_Q_Rotation() {
|
||||
verify_f(Qstandard(Q_Rotation(Q_Matrix3(Quat([12,34,56],33)))),Qstandard(Quat([12,34,56],33)));
|
||||
verify_f(Q_Matrix3(Q_Rotation(Q_Matrix3(QuatXYZ([12,34,56])))),
|
||||
Q_Matrix3(QuatXYZ([12,34,56])));
|
||||
}
|
||||
test_Q_Rotation();
|
||||
module test_q_rotation_path() {
|
||||
assert_approx(q_rotation_path(quat_x(135), 5, quat_y(13.5))[0] , q_matrix4(quat_x(135)));
|
||||
assert_approx(q_rotation_path(quat_x(135), 11, quat_y(13.5))[11] , yrot(13.5));
|
||||
assert_approx(q_rotation_path(quat_x(135), 16, quat_y(13.5))[8] , q_rotation_path(quat_x(135), 8, quat_y(13.5))[4]);
|
||||
assert_approx(q_rotation_path(quat_x(135), 16, quat_y(13.5))[7] ,
|
||||
q_rotation_path(quat_y(13.5),16, quat_x(135))[9]);
|
||||
|
||||
|
||||
module test_Q_Rotation_path() {
|
||||
|
||||
verify_f(Q_Rotation_path(QuatX(135), 5, QuatY(13.5))[0] , Q_Matrix4(QuatX(135)));
|
||||
verify_f(Q_Rotation_path(QuatX(135), 11, QuatY(13.5))[11] , yrot(13.5));
|
||||
verify_f(Q_Rotation_path(QuatX(135), 16, QuatY(13.5))[8] , Q_Rotation_path(QuatX(135), 8, QuatY(13.5))[4]);
|
||||
verify_f(Q_Rotation_path(QuatX(135), 16, QuatY(13.5))[7] ,
|
||||
Q_Rotation_path(QuatY(13.5),16, QuatX(135))[9]);
|
||||
|
||||
verify_f(Q_Rotation_path(QuatX(11), 5)[0] , xrot(11));
|
||||
verify_f(Q_Rotation_path(QuatX(11), 5)[4] , xrot(55));
|
||||
assert_approx(q_rotation_path(quat_x(11), 5)[0] , xrot(11));
|
||||
assert_approx(q_rotation_path(quat_x(11), 5)[4] , xrot(55));
|
||||
|
||||
}
|
||||
test_Q_Rotation_path();
|
||||
test_q_rotation_path();
|
||||
|
||||
|
||||
module test_Q_Nlerp() {
|
||||
verify_f(Q_Nlerp(QuatX(45),QuatY(30),0.0),QuatX(45));
|
||||
verify_f(Q_Nlerp(QuatX(45),QuatY(30),0.5),[0.1967063121, 0.1330377423, 0, 0.9713946602]);
|
||||
verify_f(Q_Rotation_path(QuatX(135), 16, QuatY(13.5))[8] , Q_Matrix4(Q_Nlerp(QuatX(135), QuatY(13.5),0.5)));
|
||||
verify_f(Q_Nlerp(QuatX(45),QuatY(30),1.0),QuatY(30));
|
||||
module test_q_nlerp() {
|
||||
assert_approx(q_nlerp(quat_x(45),quat_y(30),0.0),quat_x(45));
|
||||
assert_approx(q_nlerp(quat_x(45),quat_y(30),0.5),[0.1967063121, 0.1330377423, 0, 0.9713946602]);
|
||||
assert_approx(q_rotation_path(quat_x(135), 16, quat_y(13.5))[8] , q_matrix4(q_nlerp(quat_x(135), quat_y(13.5),0.5)));
|
||||
assert_approx(q_nlerp(quat_x(45),quat_y(30),1.0),quat_y(30));
|
||||
}
|
||||
test_Q_Nlerp();
|
||||
test_q_nlerp();
|
||||
|
||||
|
||||
module test_Q_Squad() {
|
||||
verify_f(Q_Squad(QuatX(45),QuatZ(30),QuatX(90),QuatY(30),0.0),QuatX(45));
|
||||
verify_f(Q_Squad(QuatX(45),QuatZ(30),QuatX(90),QuatY(30),1.0),QuatY(30));
|
||||
verify_f(Q_Squad(QuatX(0),QuatX(30),QuatX(90),QuatX(120),0.5),
|
||||
Q_Slerp(QuatX(0),QuatX(120),0.5));
|
||||
verify_f(Q_Squad(QuatY(0),QuatY(0),QuatX(120),QuatX(120),0.3),
|
||||
Q_Slerp(QuatY(0),QuatX(120),0.3));
|
||||
module test_q_squad() {
|
||||
assert_approx(q_squad(quat_x(45),quat_z(30),quat_x(90),quat_y(30),0.0),quat_x(45));
|
||||
assert_approx(q_squad(quat_x(45),quat_z(30),quat_x(90),quat_y(30),1.0),quat_y(30));
|
||||
assert_approx(q_squad(quat_x(0),quat_x(30),quat_x(90),quat_x(120),0.5),
|
||||
q_slerp(quat_x(0),quat_x(120),0.5));
|
||||
assert_approx(q_squad(quat_y(0),quat_y(0),quat_x(120),quat_x(120),0.3),
|
||||
q_slerp(quat_y(0),quat_x(120),0.3));
|
||||
}
|
||||
test_Q_Squad();
|
||||
test_q_squad();
|
||||
|
||||
|
||||
module test_Q_exp() {
|
||||
verify_f(Q_exp(Q_Ident()), exp(1)*Q_Ident());
|
||||
verify_f(Q_exp([0,0,0,33.7]), exp(33.7)*Q_Ident());
|
||||
verify_f(Q_exp(Q_ln(Q_Ident())), Q_Ident());
|
||||
verify_f(Q_exp(Q_ln([1,2,3,0])), [1,2,3,0]);
|
||||
verify_f(Q_exp(Q_ln(QuatXYZ([31,27,34]))), QuatXYZ([31,27,34]));
|
||||
let(q=QuatXYZ([12,23,34]))
|
||||
verify_f(Q_exp(q+Q_Inverse(q)),Q_Mul(Q_exp(q),Q_exp(Q_Inverse(q))));
|
||||
module test_q_exp() {
|
||||
assert_approx(q_exp(q_ident()), exp(1)*q_ident());
|
||||
assert_approx(q_exp([0,0,0,33.7]), exp(33.7)*q_ident());
|
||||
assert_approx(q_exp(q_ln(q_ident())), q_ident());
|
||||
assert_approx(q_exp(q_ln([1,2,3,0])), [1,2,3,0]);
|
||||
assert_approx(q_exp(q_ln(quat_xyz([31,27,34]))), quat_xyz([31,27,34]));
|
||||
let(q=quat_xyz([12,23,34]))
|
||||
assert_approx(q_exp(q+q_inverse(q)),q_mul(q_exp(q),q_exp(q_inverse(q))));
|
||||
|
||||
}
|
||||
test_Q_exp();
|
||||
test_q_exp();
|
||||
|
||||
|
||||
module test_Q_ln() {
|
||||
verify_f(Q_ln([1,2,3,0]), [24.0535117721, 48.1070235442, 72.1605353164, 1.31952866481]);
|
||||
verify_f(Q_ln(Q_Ident()), [0,0,0,0]);
|
||||
verify_f(Q_ln(5.5*Q_Ident()), [0,0,0,ln(5.5)]);
|
||||
verify_f(Q_ln(Q_exp(QuatXYZ([13,37,43]))), QuatXYZ([13,37,43]));
|
||||
verify_f(Q_ln(QuatXYZ([12,23,34]))+Q_ln(Q_Inverse(QuatXYZ([12,23,34]))), [0,0,0,0]);
|
||||
module test_q_ln() {
|
||||
assert_approx(q_ln([1,2,3,0]), [24.0535117721, 48.1070235442, 72.1605353164, 1.31952866481]);
|
||||
assert_approx(q_ln(q_ident()), [0,0,0,0]);
|
||||
assert_approx(q_ln(5.5*q_ident()), [0,0,0,ln(5.5)]);
|
||||
assert_approx(q_ln(q_exp(quat_xyz([13,37,43]))), quat_xyz([13,37,43]));
|
||||
assert_approx(q_ln(quat_xyz([12,23,34]))+q_ln(q_inverse(quat_xyz([12,23,34]))), [0,0,0,0]);
|
||||
}
|
||||
test_Q_ln();
|
||||
test_q_ln();
|
||||
|
||||
|
||||
module test_Q_pow() {
|
||||
q = Quat([1,2,3],77);
|
||||
verify_f(Q_pow(q,1), q);
|
||||
verify_f(Q_pow(q,0), Q_Ident());
|
||||
verify_f(Q_pow(q,-1), Q_Inverse(q));
|
||||
verify_f(Q_pow(q,2), Q_Mul(q,q));
|
||||
verify_f(Q_pow(q,3), Q_Mul(q,Q_pow(q,2)));
|
||||
verify_f(Q_Mul(Q_pow(q,0.456),Q_pow(q,0.544)), q);
|
||||
verify_f(Q_Mul(Q_pow(q,0.335),Q_Mul(Q_pow(q,.552),Q_pow(q,.113))), q);
|
||||
module test_q_pow() {
|
||||
q = quat([1,2,3],77);
|
||||
assert_approx(q_pow(q,1), q);
|
||||
assert_approx(q_pow(q,0), q_ident());
|
||||
assert_approx(q_pow(q,-1), q_inverse(q));
|
||||
assert_approx(q_pow(q,2), q_mul(q,q));
|
||||
assert_approx(q_pow(q,3), q_mul(q,q_pow(q,2)));
|
||||
assert_approx(q_mul(q_pow(q,0.456),q_pow(q,0.544)), q);
|
||||
assert_approx(q_mul(q_pow(q,0.335),q_mul(q_pow(q,.552),q_pow(q,.113))), q);
|
||||
}
|
||||
test_Q_pow();
|
||||
test_q_pow();
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -9,17 +9,17 @@ module test_prismoid() {
|
|||
assert_approx(prismoid([100,80],[40,50],h=50,anchor=BOT), [[[20,25,50],[20,-25,50],[-20,-25,50],[-20,25,50],[50,40,0],[50,-40,0],[-50,-40,0],[-50,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,anchor=TOP+RIGHT), [[[0,25,0],[0,-25,0],[-40,-25,0],[-40,25,0],[30,40,-50],[30,-40,-50],[-70,-40,-50],[-70,40,-50]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5]), [[[30,30,50],[30,-20,50],[-10,-20,50],[-10,30,50],[50,40,0],[50,-40,0],[-50,-40,0],[-50,40,0]],[[0,1,2],[0,2,3],[0,4,5],[0,5,1],[1,5,6],[1,6,2],[2,6,7],[2,7,3],[3,7,4],[3,4,0],[4,7,6],[4,6,5]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer=5), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-15,50],[25,-20,50],[-5,-20,50],[-10,-15,50],[-10,25,50],[-5,30,50],[25,30,50],[30,25,50]],[[4,1,0],[1,4,2],[2,4,3],[4,0,5],[14,5,6],[5,0,6],[14,6,7],[6,0,7],[0,1,8],[7,0,8],[1,2,9],[8,1,9],[14,8,9],[2,3,10],[9,2,10],[14,9,10],[14,10,11],[3,4,11],[10,3,11],[4,5,12],[14,11,12],[11,4,12],[5,14,13],[14,12,13],[12,5,13],[14,7,15],[7,8,15],[8,14,15]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=5),[[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-20,50],[-10,-20,50],[-10,30,50],[30,30,50]],[[4,1,0],[1,4,2],[2,4,3],[4,0,5],[11,5,6],[5,0,6],[0,11,7],[11,6,7],[6,0,7],[11,0,8],[0,1,8],[1,2,8],[3,4,9],[2,3,9],[8,2,9],[11,8,9],[4,5,10],[5,11,10],[11,9,10],[9,4,10]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer2=5), [[[50,-40,0],[-50,-40,0],[-50,40,0],[50,40,0],[30,-15,50],[25,-20,50],[-5,-20,50],[-10,-15,50],[-10,25,50],[-5,30,50],[25,30,50],[30,25,50]],[[2,1,0],[10,2,3],[2,0,3],[3,0,4],[10,4,5],[0,1,5],[4,0,5],[10,5,6],[5,1,6],[1,2,7],[6,1,7],[10,6,7],[10,7,8],[7,2,8],[2,10,9],[10,8,9],[8,2,9],[10,3,11],[3,4,11],[4,10,11]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=5,chamfer2=10), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-10,50],[20,-20,50],[0,-20,50],[-10,-10,50],[-10,20,50],[0,30,50],[20,30,50],[30,20,50]],[[4,1,0],[1,4,2],[2,4,3],[4,0,5],[14,5,6],[5,0,6],[14,6,7],[6,0,7],[0,1,8],[7,0,8],[1,2,9],[8,1,9],[14,8,9],[2,3,10],[9,2,10],[14,9,10],[14,10,11],[3,4,11],[10,3,11],[4,5,12],[14,11,12],[11,4,12],[5,14,13],[14,12,13],[12,5,13],[14,7,15],[7,8,15],[8,14,15]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding=5), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-15,50],[29.8296291314,-16.2940952255,50],[29.3301270189,-17.5,50],[28.5355339059,-18.5355339059,50],[27.5,-19.3301270189,50],[26.2940952255,-19.8296291314,50],[25,-20,50],[-5,-20,50],[-6.29409522551,-19.8296291314,50],[-7.5,-19.3301270189,50],[-8.53553390593,-18.5355339059,50],[-9.33012701892,-17.5,50],[-9.82962913145,-16.2940952255,50],[-10,-15,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[25,30,50],[26.2940952255,29.8296291314,50],[27.5,29.3301270189,50],[28.5355339059,28.5355339059,50],[29.3301270189,27.5,50],[29.8296291314,26.2940952255,50],[30,25,50]],[[16,1,0],[1,16,2],[2,16,3],[3,16,4],[4,16,5],[5,16,6],[6,16,7],[7,16,8],[8,16,9],[9,16,10],[10,16,11],[11,16,12],[12,16,13],[13,16,14],[14,16,15],[16,0,17],[17,0,18],[18,0,19],[19,0,20],[20,0,21],[21,0,22],[51,22,23],[22,0,23],[51,23,24],[23,0,24],[24,0,25],[25,0,26],[26,0,27],[0,1,28],[27,0,28],[1,2,29],[28,1,29],[51,28,29],[2,3,30],[29,2,30],[51,29,30],[3,4,31],[30,3,31],[51,30,31],[4,5,32],[31,4,32],[51,31,32],[5,6,33],[32,5,33],[51,32,33],[51,33,34],[6,7,34],[33,6,34],[51,34,35],[7,8,35],[34,7,35],[51,35,36],[8,9,36],[35,8,36],[51,36,37],[9,10,37],[36,9,37],[51,37,38],[10,11,38],[37,10,38],[51,38,39],[11,12,39],[38,11,39],[51,39,40],[12,13,40],[39,12,40],[51,40,41],[13,14,41],[40,13,41],[51,41,42],[14,15,42],[41,14,42],[51,42,43],[15,16,43],[42,15,43],[51,43,44],[16,17,44],[43,16,44],[17,18,45],[44,17,45],[51,44,45],[18,19,46],[45,18,46],[51,45,46],[19,20,47],[46,19,47],[51,46,47],[20,21,48],[47,20,48],[51,47,48],[21,22,49],[51,48,49],[48,21,49],[22,51,50],[51,49,50],[49,22,50],[51,24,52],[24,25,52],[28,51,52],[25,26,53],[52,25,53],[28,52,53],[26,27,54],[53,26,54],[28,53,54],[27,28,55],[28,54,55],[54,27,55]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-20,50],[-10,-20,50],[-10,30,50],[30,30,50]],[[16,1,0],[1,16,2],[2,16,3],[3,16,4],[4,16,5],[5,16,6],[6,16,7],[7,16,8],[8,16,9],[9,16,10],[10,16,11],[11,16,12],[12,16,13],[13,16,14],[14,16,15],[16,0,17],[17,0,18],[18,0,19],[19,0,20],[31,20,21],[20,0,21],[31,21,22],[21,0,22],[31,22,23],[22,0,23],[31,23,24],[23,0,24],[31,24,25],[24,0,25],[31,25,26],[25,0,26],[0,31,27],[31,26,27],[26,0,27],[31,0,28],[0,1,28],[1,2,28],[2,3,28],[3,4,28],[4,5,28],[5,6,28],[6,7,28],[13,14,29],[7,8,29],[28,7,29],[8,9,29],[9,10,29],[10,11,29],[11,12,29],[12,13,29],[31,28,29],[17,18,30],[18,19,30],[19,20,30],[20,31,30],[15,16,30],[14,15,30],[29,14,30],[16,17,30],[31,29,30]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding2=5), [[[50,-40,0],[-50,-40,0],[-50,40,0],[50,40,0],[30,-15,50],[29.8296291314,-16.2940952255,50],[29.3301270189,-17.5,50],[28.5355339059,-18.5355339059,50],[27.5,-19.3301270189,50],[26.2940952255,-19.8296291314,50],[25,-20,50],[-5,-20,50],[-6.29409522551,-19.8296291314,50],[-7.5,-19.3301270189,50],[-8.53553390593,-18.5355339059,50],[-9.33012701892,-17.5,50],[-9.82962913145,-16.2940952255,50],[-10,-15,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[25,30,50],[26.2940952255,29.8296291314,50],[27.5,29.3301270189,50],[28.5355339059,28.5355339059,50],[29.3301270189,27.5,50],[29.8296291314,26.2940952255,50],[30,25,50]],[[2,1,0],[2,0,3],[3,0,4],[28,4,5],[4,0,5],[28,5,6],[5,0,6],[28,6,7],[6,0,7],[28,7,8],[7,0,8],[28,8,9],[8,0,9],[28,9,10],[0,1,10],[9,0,10],[10,1,11],[28,10,11],[11,1,12],[28,11,12],[12,1,13],[28,12,13],[13,1,14],[28,13,14],[14,1,15],[28,14,15],[15,1,16],[28,15,16],[1,2,17],[16,1,17],[28,16,17],[28,17,18],[17,2,18],[28,18,19],[18,2,19],[28,19,20],[19,2,20],[28,20,21],[20,2,21],[28,21,22],[21,2,22],[22,2,23],[28,22,23],[2,3,24],[23,2,24],[28,23,24],[28,24,25],[24,3,25],[28,25,26],[25,3,26],[3,28,27],[28,26,27],[26,3,27],[28,3,29],[4,28,29],[4,29,30],[29,3,30],[3,4,31],[4,30,31],[30,3,31]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5,rounding2=10), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-10,50],[29.6592582629,-12.588190451,50],[28.6602540378,-15,50],[27.0710678119,-17.0710678119,50],[25,-18.6602540378,50],[22.588190451,-19.6592582629,50],[20,-20,50],[0,-20,50],[-2.58819045103,-19.6592582629,50],[-5,-18.6602540378,50],[-7.07106781187,-17.0710678119,50],[-8.66025403784,-15,50],[-9.65925826289,-12.588190451,50],[-10,-10,50],[-10,20,50],[-9.65925826289,22.588190451,50],[-8.66025403784,25,50],[-7.07106781187,27.0710678119,50],[-5,28.6602540378,50],[-2.58819045103,29.6592582629,50],[0,30,50],[20,30,50],[22.588190451,29.6592582629,50],[25,28.6602540378,50],[27.0710678119,27.0710678119,50],[28.6602540378,25,50],[29.6592582629,22.588190451,50],[30,20,50]],[[16,1,0],[1,16,2],[2,16,3],[3,16,4],[4,16,5],[5,16,6],[6,16,7],[7,16,8],[8,16,9],[9,16,10],[10,16,11],[11,16,12],[12,16,13],[13,16,14],[14,16,15],[16,0,17],[17,0,18],[18,0,19],[19,0,20],[20,0,21],[21,0,22],[51,22,23],[22,0,23],[51,23,24],[23,0,24],[24,0,25],[25,0,26],[26,0,27],[0,1,28],[27,0,28],[1,2,29],[28,1,29],[51,28,29],[2,3,30],[29,2,30],[51,29,30],[3,4,31],[30,3,31],[51,30,31],[4,5,32],[31,4,32],[51,31,32],[5,6,33],[32,5,33],[51,32,33],[51,33,34],[6,7,34],[33,6,34],[51,34,35],[7,8,35],[34,7,35],[51,35,36],[8,9,36],[35,8,36],[51,36,37],[9,10,37],[36,9,37],[51,37,38],[10,11,38],[37,10,38],[51,38,39],[11,12,39],[38,11,39],[51,39,40],[12,13,40],[39,12,40],[51,40,41],[13,14,41],[40,13,41],[51,41,42],[14,15,42],[41,14,42],[51,42,43],[15,16,43],[42,15,43],[51,43,44],[16,17,44],[43,16,44],[51,44,45],[17,18,45],[44,17,45],[51,45,46],[18,19,46],[45,18,46],[19,20,47],[46,19,47],[51,46,47],[20,21,48],[47,20,48],[51,47,48],[21,22,49],[51,48,49],[48,21,49],[22,51,50],[51,49,50],[49,22,50],[51,24,52],[24,25,52],[28,51,52],[25,26,53],[52,25,53],[28,52,53],[26,27,54],[53,26,54],[28,53,54],[27,28,55],[28,54,55],[54,27,55]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5,chamfer2=10), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-10,50],[20,-20,50],[0,-20,50],[-10,-10,50],[-10,20,50],[0,30,50],[20,30,50],[30,20,50]],[[0,16,9],[28,0,1],[0,9,1],[28,1,2],[1,9,2],[28,2,3],[2,9,3],[3,9,4],[4,9,5],[5,9,6],[6,9,7],[7,9,8],[9,16,10],[10,16,11],[11,16,12],[12,16,13],[13,16,14],[14,16,15],[16,0,17],[17,0,18],[18,0,19],[19,0,20],[20,0,21],[21,0,22],[22,0,23],[23,0,24],[24,0,25],[25,0,26],[0,28,27],[26,0,27],[28,3,29],[3,4,29],[4,5,29],[5,6,29],[6,7,29],[8,9,30],[7,8,30],[29,7,30],[9,10,30],[28,29,30],[28,30,31],[10,11,31],[30,10,31],[11,12,31],[12,13,31],[13,14,31],[28,31,32],[15,16,32],[14,15,32],[31,14,32],[16,17,32],[20,21,33],[28,32,33],[19,20,33],[17,18,33],[32,17,33],[18,19,33],[23,24,34],[28,33,34],[21,22,34],[33,21,34],[22,23,34],[26,27,35],[27,28,35],[25,26,35],[28,34,35],[24,25,35],[34,24,35]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer=[0,5,10,15]), [[[50,-25,0],[35,-40,0],[-40,-40,0],[-50,-30,0],[-50,35,0],[-45,40,0],[50,40,0],[30,-5,50],[15,-20,50],[0,-20,50],[-10,-10,50],[-10,25,50],[-5,30,50],[30,30,50]],[[4,1,0],[1,4,2],[2,4,3],[4,0,5],[0,13,6],[13,5,6],[5,0,6],[13,0,7],[0,1,7],[1,2,8],[7,1,8],[13,7,8],[13,8,9],[2,3,9],[8,2,9],[13,9,10],[3,4,10],[9,3,10],[4,5,11],[13,10,11],[10,4,11],[5,13,12],[13,11,12],[11,5,12]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=[15,10,5,0], rounding2=[0,5,10,15]), [[[50,-40,0],[-45,-40,0],[-50,-35,0],[-50,30,0],[-40,40,0],[35,40,0],[50,25,0],[30,-5,50],[29.4888873943,-8.88228567654,50],[27.9903810568,-12.5,50],[25.6066017178,-15.6066017178,50],[22.5,-17.9903810568,50],[18.8822856765,-19.4888873943,50],[15,-20,50],[0,-20,50],[-2.58819045103,-19.6592582629,50],[-5,-18.6602540378,50],[-7.07106781187,-17.0710678119,50],[-8.66025403784,-15,50],[-9.65925826289,-12.588190451,50],[-10,-10,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[30,30,50]],[[3,1,0],[1,3,2],[3,0,4],[28,4,5],[4,0,5],[0,28,6],[28,5,6],[5,0,6],[28,0,7],[7,0,8],[28,7,8],[28,8,9],[8,0,9],[28,9,10],[9,0,10],[28,10,11],[10,0,11],[28,11,12],[11,0,12],[28,12,13],[0,1,13],[12,0,13],[28,13,14],[13,1,14],[28,14,15],[14,1,15],[28,15,16],[15,1,16],[28,16,17],[1,2,17],[16,1,17],[28,17,18],[17,2,18],[28,18,19],[18,2,19],[28,19,20],[2,3,20],[19,2,20],[28,20,21],[20,3,21],[21,3,22],[28,21,22],[22,3,23],[28,22,23],[3,4,24],[23,3,24],[28,23,24],[28,24,25],[24,4,25],[28,25,26],[25,4,26],[4,28,27],[28,26,27],[26,4,27]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer=5), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-15,50],[25,-20,50],[-5,-20,50],[-10,-15,50],[-10,25,50],[-5,30,50],[25,30,50],[30,25,50]],[[14,7,15],[7,8,15],[8,14,15],[5,14,13],[14,12,13],[12,5,13],[14,11,12],[11,4,12],[4,5,12],[14,10,11],[3,4,11],[10,3,11],[2,3,10],[9,2,10],[14,9,10],[14,8,9],[1,2,9],[8,1,9],[7,0,8],[0,1,8],[14,6,7],[6,0,7],[14,5,6],[5,0,6],[4,0,5],[2,4,3],[1,4,2],[4,1,0]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=5), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-20,50],[-10,-20,50],[-10,30,50],[30,30,50]],[[11,9,10],[9,4,10],[4,5,10],[5,11,10],[2,3,9],[8,2,9],[11,8,9],[3,4,9],[1,2,8],[11,0,8],[0,1,8],[0,11,7],[11,6,7],[6,0,7],[11,5,6],[5,0,6],[4,0,5],[2,4,3],[1,4,2],[4,1,0]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer2=5), [[[50,-40,0],[-50,-40,0],[-50,40,0],[50,40,0],[30,-15,50],[25,-20,50],[-5,-20,50],[-10,-15,50],[-10,25,50],[-5,30,50],[25,30,50],[30,25,50]],[[10,3,11],[3,4,11],[4,10,11],[2,10,9],[10,8,9],[8,2,9],[10,7,8],[7,2,8],[1,2,7],[6,1,7],[10,6,7],[10,5,6],[5,1,6],[10,4,5],[0,1,5],[4,0,5],[3,0,4],[10,2,3],[2,0,3],[2,1,0]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=5,chamfer2=10), [[[50,-35,0],[45,-40,0],[-45,-40,0],[-50,-35,0],[-50,35,0],[-45,40,0],[45,40,0],[50,35,0],[30,-10,50],[20,-20,50],[0,-20,50],[-10,-10,50],[-10,20,50],[0,30,50],[20,30,50],[30,20,50]],[[14,7,15],[7,8,15],[8,14,15],[5,14,13],[14,12,13],[12,5,13],[14,11,12],[11,4,12],[4,5,12],[14,10,11],[3,4,11],[10,3,11],[2,3,10],[9,2,10],[14,9,10],[14,8,9],[1,2,9],[8,1,9],[7,0,8],[0,1,8],[14,6,7],[6,0,7],[14,5,6],[5,0,6],[4,0,5],[2,4,3],[1,4,2],[4,1,0]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding=5), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-15,50],[29.8296291314,-16.2940952255,50],[29.3301270189,-17.5,50],[28.5355339059,-18.5355339059,50],[27.5,-19.3301270189,50],[26.2940952255,-19.8296291314,50],[25,-20,50],[-5,-20,50],[-6.29409522551,-19.8296291314,50],[-7.5,-19.3301270189,50],[-8.53553390593,-18.5355339059,50],[-9.33012701892,-17.5,50],[-9.82962913145,-16.2940952255,50],[-10,-15,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[25,30,50],[26.2940952255,29.8296291314,50],[27.5,29.3301270189,50],[28.5355339059,28.5355339059,50],[29.3301270189,27.5,50],[29.8296291314,26.2940952255,50],[30,25,50]],[[27,28,55],[28,54,55],[54,27,55],[28,53,54],[26,27,54],[53,26,54],[28,52,53],[25,26,53],[52,25,53],[28,51,52],[51,24,52],[24,25,52],[22,51,50],[51,49,50],[49,22,50],[51,48,49],[48,21,49],[21,22,49],[20,21,48],[47,20,48],[51,47,48],[19,20,47],[46,19,47],[51,46,47],[51,45,46],[18,19,46],[45,18,46],[51,44,45],[17,18,45],[44,17,45],[16,17,44],[43,16,44],[51,43,44],[15,16,43],[42,15,43],[51,42,43],[14,15,42],[41,14,42],[51,41,42],[13,14,41],[40,13,41],[51,40,41],[12,13,40],[39,12,40],[51,39,40],[11,12,39],[38,11,39],[51,38,39],[10,11,38],[37,10,38],[51,37,38],[9,10,37],[36,9,37],[51,36,37],[8,9,36],[35,8,36],[51,35,36],[7,8,35],[34,7,35],[51,34,35],[6,7,34],[33,6,34],[51,33,34],[51,32,33],[5,6,33],[32,5,33],[51,31,32],[4,5,32],[31,4,32],[51,30,31],[3,4,31],[30,3,31],[51,29,30],[2,3,30],[29,2,30],[51,28,29],[1,2,29],[28,1,29],[27,0,28],[0,1,28],[26,0,27],[25,0,26],[24,0,25],[51,23,24],[23,0,24],[51,22,23],[22,0,23],[21,0,22],[20,0,21],[19,0,20],[18,0,19],[17,0,18],[16,0,17],[14,16,15],[13,16,14],[12,16,13],[11,16,12],[10,16,11],[9,16,10],[8,16,9],[7,16,8],[6,16,7],[5,16,6],[4,16,5],[3,16,4],[2,16,3],[1,16,2],[16,1,0]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-20,50],[-10,-20,50],[-10,30,50],[30,30,50]],[[16,17,30],[31,29,30],[15,16,30],[14,15,30],[29,14,30],[19,20,30],[20,31,30],[18,19,30],[17,18,30],[12,13,29],[31,28,29],[11,12,29],[10,11,29],[9,10,29],[8,9,29],[7,8,29],[28,7,29],[13,14,29],[6,7,28],[5,6,28],[4,5,28],[3,4,28],[2,3,28],[1,2,28],[31,0,28],[0,1,28],[0,31,27],[31,26,27],[26,0,27],[31,25,26],[25,0,26],[31,24,25],[24,0,25],[31,23,24],[23,0,24],[31,22,23],[22,0,23],[31,21,22],[21,0,22],[31,20,21],[20,0,21],[19,0,20],[18,0,19],[17,0,18],[16,0,17],[14,16,15],[13,16,14],[12,16,13],[11,16,12],[10,16,11],[9,16,10],[8,16,9],[7,16,8],[6,16,7],[5,16,6],[4,16,5],[3,16,4],[2,16,3],[1,16,2],[16,1,0]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding2=5), [[[50,-40,0],[-50,-40,0],[-50,40,0],[50,40,0],[30,-15,50],[29.8296291314,-16.2940952255,50],[29.3301270189,-17.5,50],[28.5355339059,-18.5355339059,50],[27.5,-19.3301270189,50],[26.2940952255,-19.8296291314,50],[25,-20,50],[-5,-20,50],[-6.29409522551,-19.8296291314,50],[-7.5,-19.3301270189,50],[-8.53553390593,-18.5355339059,50],[-9.33012701892,-17.5,50],[-9.82962913145,-16.2940952255,50],[-10,-15,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[25,30,50],[26.2940952255,29.8296291314,50],[27.5,29.3301270189,50],[28.5355339059,28.5355339059,50],[29.3301270189,27.5,50],[29.8296291314,26.2940952255,50],[30,25,50]],[[3,4,31],[4,30,31],[30,3,31],[4,29,30],[29,3,30],[28,3,29],[4,28,29],[3,28,27],[28,26,27],[26,3,27],[28,25,26],[25,3,26],[28,24,25],[24,3,25],[2,3,24],[23,2,24],[28,23,24],[22,2,23],[28,22,23],[28,21,22],[21,2,22],[28,20,21],[20,2,21],[28,19,20],[19,2,20],[28,18,19],[18,2,19],[28,17,18],[17,2,18],[1,2,17],[16,1,17],[28,16,17],[15,1,16],[28,15,16],[14,1,15],[28,14,15],[13,1,14],[28,13,14],[12,1,13],[28,12,13],[11,1,12],[28,11,12],[10,1,11],[28,10,11],[0,1,10],[9,0,10],[28,9,10],[8,0,9],[28,8,9],[28,7,8],[7,0,8],[28,6,7],[6,0,7],[28,5,6],[5,0,6],[28,4,5],[4,0,5],[3,0,4],[2,0,3],[2,1,0]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5,rounding2=10), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-10,50],[29.6592582629,-12.588190451,50],[28.6602540378,-15,50],[27.0710678119,-17.0710678119,50],[25,-18.6602540378,50],[22.588190451,-19.6592582629,50],[20,-20,50],[0,-20,50],[-2.58819045103,-19.6592582629,50],[-5,-18.6602540378,50],[-7.07106781187,-17.0710678119,50],[-8.66025403784,-15,50],[-9.65925826289,-12.588190451,50],[-10,-10,50],[-10,20,50],[-9.65925826289,22.588190451,50],[-8.66025403784,25,50],[-7.07106781187,27.0710678119,50],[-5,28.6602540378,50],[-2.58819045103,29.6592582629,50],[0,30,50],[20,30,50],[22.588190451,29.6592582629,50],[25,28.6602540378,50],[27.0710678119,27.0710678119,50],[28.6602540378,25,50],[29.6592582629,22.588190451,50],[30,20,50]],[[27,28,55],[28,54,55],[54,27,55],[28,53,54],[26,27,54],[53,26,54],[28,52,53],[25,26,53],[52,25,53],[28,51,52],[51,24,52],[24,25,52],[22,51,50],[51,49,50],[49,22,50],[51,48,49],[48,21,49],[21,22,49],[20,21,48],[47,20,48],[51,47,48],[19,20,47],[46,19,47],[51,46,47],[18,19,46],[45,18,46],[51,45,46],[17,18,45],[44,17,45],[51,44,45],[16,17,44],[43,16,44],[51,43,44],[15,16,43],[42,15,43],[51,42,43],[14,15,42],[41,14,42],[51,41,42],[13,14,41],[40,13,41],[51,40,41],[12,13,40],[39,12,40],[51,39,40],[11,12,39],[38,11,39],[51,38,39],[10,11,38],[37,10,38],[51,37,38],[9,10,37],[36,9,37],[51,36,37],[8,9,36],[35,8,36],[51,35,36],[7,8,35],[34,7,35],[51,34,35],[6,7,34],[33,6,34],[51,33,34],[51,32,33],[5,6,33],[32,5,33],[51,31,32],[4,5,32],[31,4,32],[51,30,31],[3,4,31],[30,3,31],[51,29,30],[2,3,30],[29,2,30],[51,28,29],[1,2,29],[28,1,29],[27,0,28],[0,1,28],[26,0,27],[25,0,26],[24,0,25],[51,23,24],[23,0,24],[51,22,23],[22,0,23],[21,0,22],[20,0,21],[19,0,20],[18,0,19],[17,0,18],[16,0,17],[14,16,15],[13,16,14],[12,16,13],[11,16,12],[10,16,11],[9,16,10],[8,16,9],[7,16,8],[6,16,7],[5,16,6],[4,16,5],[3,16,4],[2,16,3],[1,16,2],[16,1,0]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],rounding1=5,chamfer2=10), [[[50,-35,0],[49.8296291314,-36.2940952255,0],[49.3301270189,-37.5,0],[48.5355339059,-38.5355339059,0],[47.5,-39.3301270189,0],[46.2940952255,-39.8296291314,0],[45,-40,0],[-45,-40,0],[-46.2940952255,-39.8296291314,0],[-47.5,-39.3301270189,0],[-48.5355339059,-38.5355339059,0],[-49.3301270189,-37.5,0],[-49.8296291314,-36.2940952255,0],[-50,-35,0],[-50,35,0],[-49.8296291314,36.2940952255,0],[-49.3301270189,37.5,0],[-48.5355339059,38.5355339059,0],[-47.5,39.3301270189,0],[-46.2940952255,39.8296291314,0],[-45,40,0],[45,40,0],[46.2940952255,39.8296291314,0],[47.5,39.3301270189,0],[48.5355339059,38.5355339059,0],[49.3301270189,37.5,0],[49.8296291314,36.2940952255,0],[50,35,0],[30,-10,50],[20,-20,50],[0,-20,50],[-10,-10,50],[-10,20,50],[0,30,50],[20,30,50],[30,20,50]],[[24,25,35],[34,24,35],[25,26,35],[28,34,35],[26,27,35],[27,28,35],[22,23,34],[21,22,34],[33,21,34],[28,33,34],[23,24,34],[18,19,33],[17,18,33],[32,17,33],[19,20,33],[28,32,33],[20,21,33],[16,17,32],[15,16,32],[14,15,32],[31,14,32],[28,31,32],[13,14,31],[12,13,31],[11,12,31],[10,11,31],[30,10,31],[28,30,31],[28,29,30],[9,10,30],[8,9,30],[7,8,30],[29,7,30],[6,7,29],[5,6,29],[4,5,29],[28,3,29],[3,4,29],[0,28,27],[26,0,27],[25,0,26],[24,0,25],[23,0,24],[22,0,23],[21,0,22],[20,0,21],[19,0,20],[18,0,19],[17,0,18],[16,0,17],[14,16,15],[13,16,14],[12,16,13],[11,16,12],[10,16,11],[9,16,10],[7,9,8],[6,9,7],[5,9,6],[4,9,5],[3,9,4],[28,2,3],[2,9,3],[28,1,2],[1,9,2],[28,0,1],[0,9,1],[0,16,9]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer=[0,5,10,15]), [[[50,-25,0],[35,-40,0],[-40,-40,0],[-50,-30,0],[-50,35,0],[-45,40,0],[50,40,0],[30,-5,50],[15,-20,50],[0,-20,50],[-10,-10,50],[-10,25,50],[-5,30,50],[30,30,50]],[[5,13,12],[13,11,12],[11,5,12],[13,10,11],[10,4,11],[4,5,11],[13,9,10],[3,4,10],[9,3,10],[2,3,9],[8,2,9],[13,8,9],[13,7,8],[1,2,8],[7,1,8],[13,0,7],[0,1,7],[0,13,6],[13,5,6],[5,0,6],[4,0,5],[2,4,3],[1,4,2],[4,1,0]]]);
|
||||
assert_approx(prismoid([100,80],[40,50],h=50,shift=[10,5],chamfer1=[15,10,5,0], rounding2=[0,5,10,15]), [[[50,-40,0],[-45,-40,0],[-50,-35,0],[-50,30,0],[-40,40,0],[35,40,0],[50,25,0],[30,-5,50],[29.4888873943,-8.88228567654,50],[27.9903810568,-12.5,50],[25.6066017178,-15.6066017178,50],[22.5,-17.9903810568,50],[18.8822856765,-19.4888873943,50],[15,-20,50],[0,-20,50],[-2.58819045103,-19.6592582629,50],[-5,-18.6602540378,50],[-7.07106781187,-17.0710678119,50],[-8.66025403784,-15,50],[-9.65925826289,-12.588190451,50],[-10,-10,50],[-10,25,50],[-9.82962913145,26.2940952255,50],[-9.33012701892,27.5,50],[-8.53553390593,28.5355339059,50],[-7.5,29.3301270189,50],[-6.29409522551,29.8296291314,50],[-5,30,50],[30,30,50]],[[4,28,27],[28,26,27],[26,4,27],[28,25,26],[25,4,26],[28,24,25],[24,4,25],[3,4,24],[23,3,24],[28,23,24],[22,3,23],[28,22,23],[21,3,22],[28,21,22],[28,20,21],[20,3,21],[28,19,20],[2,3,20],[19,2,20],[28,18,19],[18,2,19],[28,17,18],[17,2,18],[1,2,17],[16,1,17],[28,16,17],[15,1,16],[28,15,16],[14,1,15],[28,14,15],[13,1,14],[28,13,14],[0,1,13],[12,0,13],[28,12,13],[11,0,12],[28,11,12],[10,0,11],[28,10,11],[9,0,10],[28,9,10],[8,0,9],[28,8,9],[28,7,8],[7,0,8],[28,0,7],[0,28,6],[28,5,6],[5,0,6],[28,4,5],[4,0,5],[3,0,4],[1,3,2],[3,1,0]]]);
|
||||
}
|
||||
test_prismoid();
|
||||
|
||||
|
|
|
@ -111,7 +111,7 @@ module test_scale() {
|
|||
for (val=vals) {
|
||||
assert_equal(scale(point2d(val)), [[val.x,0,0],[0,val.y,0],[0,0,1]]);
|
||||
assert_equal(scale(val), [[val.x,0,0,0],[0,val.y,0,0],[0,0,val.z,0],[0,0,0,1]]);
|
||||
assert_equal(scale(val, p=[1,2,3]), vmul([1,2,3], val));
|
||||
assert_equal(scale(val, p=[1,2,3]), v_mul([1,2,3], val));
|
||||
scale(val) nil();
|
||||
}
|
||||
assert_equal(scale(3), [[3,0,0,0],[0,3,0,0],[0,0,3,0],[0,0,0,1]]);
|
||||
|
@ -122,7 +122,7 @@ module test_scale() {
|
|||
assert_equal(scale([2,3], p=square(1)), square([2,3]));
|
||||
assert_equal(scale([2,2], cp=[0.5,0.5], p=square(1)), move([-0.5,-0.5], p=square([2,2])));
|
||||
assert_equal(scale([2,3,4], p=cb), cube([2,3,4]));
|
||||
assert_equal(scale([-2,-3,-4], p=cb), [[for (p=cb[0]) vmul(p,[-2,-3,-4])], [for (f=cb[1]) reverse(f)]]);
|
||||
assert_equal(scale([-2,-3,-4], p=cb), [[for (p=cb[0]) v_mul(p,[-2,-3,-4])], [for (f=cb[1]) reverse(f)]]);
|
||||
// Verify that module at least doesn't crash.
|
||||
scale(-5) scale(5) nil();
|
||||
}
|
||||
|
@ -289,7 +289,7 @@ module test_rot() {
|
|||
for (vec2 = vecs2d) {
|
||||
assert_equal(
|
||||
rot(from=vec1, to=vec2, p=pts2d, planar=true),
|
||||
apply(affine2d_zrot(vang(vec2)-vang(vec1)), pts2d),
|
||||
apply(affine2d_zrot(v_theta(vec2)-v_theta(vec1)), pts2d),
|
||||
info=str(
|
||||
"from = ", vec1, ", ",
|
||||
"to = ", vec2, ", ",
|
||||
|
|
|
@ -32,66 +32,67 @@ module test_is_vector() {
|
|||
test_is_vector();
|
||||
|
||||
|
||||
module test_vfloor() {
|
||||
assert_equal(vfloor([2.0, 3.14, 18.9, 7]), [2,3,18,7]);
|
||||
assert_equal(vfloor([-2.0, -3.14, -18.9, -7]), [-2,-4,-19,-7]);
|
||||
module test_v_floor() {
|
||||
assert_equal(v_floor([2.0, 3.14, 18.9, 7]), [2,3,18,7]);
|
||||
assert_equal(v_floor([-2.0, -3.14, -18.9, -7]), [-2,-4,-19,-7]);
|
||||
}
|
||||
test_vfloor();
|
||||
test_v_floor();
|
||||
|
||||
|
||||
module test_vceil() {
|
||||
assert_equal(vceil([2.0, 3.14, 18.9, 7]), [2,4,19,7]);
|
||||
assert_equal(vceil([-2.0, -3.14, -18.9, -7]), [-2,-3,-18,-7]);
|
||||
module test_v_ceil() {
|
||||
assert_equal(v_ceil([2.0, 3.14, 18.9, 7]), [2,4,19,7]);
|
||||
assert_equal(v_ceil([-2.0, -3.14, -18.9, -7]), [-2,-3,-18,-7]);
|
||||
}
|
||||
test_vceil();
|
||||
test_v_ceil();
|
||||
|
||||
|
||||
module test_vmul() {
|
||||
assert_equal(vmul([3,4,5], [8,7,6]), [24,28,30]);
|
||||
assert_equal(vmul([1,2,3], [4,5,6]), [4,10,18]);
|
||||
assert_equal(vmul([[1,2,3],[4,5,6],[7,8,9]], [[4,5,6],[3,2,1],[5,9,3]]), [32,28,134]);
|
||||
module test_v_mul() {
|
||||
assert_equal(v_mul([3,4,5], [8,7,6]), [24,28,30]);
|
||||
assert_equal(v_mul([1,2,3], [4,5,6]), [4,10,18]);
|
||||
assert_equal(v_mul([[1,2,3],[4,5,6],[7,8,9]], [[4,5,6],[3,2,1],[5,9,3]]), [32,28,134]);
|
||||
}
|
||||
test_vmul();
|
||||
test_v_mul();
|
||||
|
||||
|
||||
module test_vdiv() {
|
||||
assert(vdiv([24,28,30], [8,7,6]) == [3, 4, 5]);
|
||||
module test_v_div() {
|
||||
assert(v_div([24,28,30], [8,7,6]) == [3, 4, 5]);
|
||||
}
|
||||
test_vdiv();
|
||||
test_v_div();
|
||||
|
||||
|
||||
module test_vabs() {
|
||||
assert(vabs([2,4,8]) == [2,4,8]);
|
||||
assert(vabs([-2,-4,-8]) == [2,4,8]);
|
||||
assert(vabs([-2,4,8]) == [2,4,8]);
|
||||
assert(vabs([2,-4,8]) == [2,4,8]);
|
||||
assert(vabs([2,4,-8]) == [2,4,8]);
|
||||
module test_v_abs() {
|
||||
assert(v_abs([2,4,8]) == [2,4,8]);
|
||||
assert(v_abs([-2,-4,-8]) == [2,4,8]);
|
||||
assert(v_abs([-2,4,8]) == [2,4,8]);
|
||||
assert(v_abs([2,-4,8]) == [2,4,8]);
|
||||
assert(v_abs([2,4,-8]) == [2,4,8]);
|
||||
}
|
||||
test_vabs();
|
||||
test_v_abs();
|
||||
|
||||
include <../strings.scad>
|
||||
module test_vang() {
|
||||
assert(vang([1,0])==0);
|
||||
assert(vang([0,1])==90);
|
||||
assert(vang([-1,0])==180);
|
||||
assert(vang([0,-1])==-90);
|
||||
assert(vang([1,1])==45);
|
||||
assert(vang([-1,1])==135);
|
||||
assert(vang([1,-1])==-45);
|
||||
assert(vang([-1,-1])==-135);
|
||||
assert(vang([0,0,1])==[0,90]);
|
||||
assert(vang([0,1,1])==[90,45]);
|
||||
assert(vang([0,1,-1])==[90,-45]);
|
||||
assert(vang([1,0,0])==[0,0]);
|
||||
assert(vang([0,1,0])==[90,0]);
|
||||
assert(vang([0,-1,0])==[-90,0]);
|
||||
assert(vang([-1,0,0])==[180,0]);
|
||||
assert(vang([1,0,1])==[0,45]);
|
||||
assert(vang([0,1,1])==[90,45]);
|
||||
assert(vang([0,-1,1])==[-90,45]);
|
||||
assert(approx(vang([1,1,1]),[45, 35.2643896828]));
|
||||
module test_v_theta() {
|
||||
assert_approx(v_theta([0,0]), 0);
|
||||
assert_approx(v_theta([1,0]), 0);
|
||||
assert_approx(v_theta([0,1]), 90);
|
||||
assert_approx(v_theta([-1,0]), 180);
|
||||
assert_approx(v_theta([0,-1]), -90);
|
||||
assert_approx(v_theta([1,1]), 45);
|
||||
assert_approx(v_theta([-1,1]), 135);
|
||||
assert_approx(v_theta([1,-1]), -45);
|
||||
assert_approx(v_theta([-1,-1]), -135);
|
||||
assert_approx(v_theta([0,0,1]), 0);
|
||||
assert_approx(v_theta([0,1,1]), 90);
|
||||
assert_approx(v_theta([0,1,-1]), 90);
|
||||
assert_approx(v_theta([1,0,0]), 0);
|
||||
assert_approx(v_theta([0,1,0]), 90);
|
||||
assert_approx(v_theta([0,-1,0]), -90);
|
||||
assert_approx(v_theta([-1,0,0]), 180);
|
||||
assert_approx(v_theta([1,0,1]), 0);
|
||||
assert_approx(v_theta([0,1,1]), 90);
|
||||
assert_approx(v_theta([0,-1,1]), -90);
|
||||
assert_approx(v_theta([1,1,1]), 45);
|
||||
}
|
||||
test_vang();
|
||||
test_v_theta();
|
||||
|
||||
|
||||
module test_unit() {
|
||||
|
|
|
@ -183,7 +183,7 @@ module trapezoidal_threaded_rod(
|
|||
higthr1 = ceil(higang1 / 360);
|
||||
higthr2 = ceil(higang2 / 360);
|
||||
pdepth = -min(subindex(profile,1));
|
||||
dummy1 = assert(_r1>2*pdepth) assert(_r2>2*pdepth);
|
||||
dummy1 = assert(_r1>pdepth) assert(_r2>pdepth);
|
||||
skew_mat = affine3d_skew(sxz=(_r2-_r1)/l);
|
||||
side_mat = affine3d_xrot(90) *
|
||||
affine3d_mirror([-1,1,0]) *
|
||||
|
@ -208,7 +208,7 @@ module trapezoidal_threaded_rod(
|
|||
prof = apply(side_mat, [
|
||||
for (thread = [-threads/2:1:threads/2-1]) let(
|
||||
tang = (thread/starts) * 360 + ang,
|
||||
hsc =
|
||||
hsc = internal? 1 :
|
||||
abs(tang) > twist/2? 0 :
|
||||
(higang1==0 && higang2==0)? 1 :
|
||||
lookup(tang, hig_table),
|
||||
|
@ -225,7 +225,7 @@ module trapezoidal_threaded_rod(
|
|||
];
|
||||
thread_vnfs = vnf_merge([
|
||||
for (i=[0:1:starts-1])
|
||||
zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed)),
|
||||
zrot(i*360/starts, p=vnf_vertex_array(thread_verts, reverse=left_handed, style="min_edge")),
|
||||
for (i=[0:1:starts-1]) let(
|
||||
rmat = zrot(i*360/starts),
|
||||
pts = deduplicate(list_head(thread_verts[0], len(prof3d)+1)),
|
||||
|
@ -447,11 +447,15 @@ module threaded_nut(
|
|||
|
||||
// Module: npt_threaded_rod()
|
||||
// Description:
|
||||
// Constructs a standard NPT pipe threading.
|
||||
// Constructs a standard NPT pipe end threading. If `internal=true`, creates a mask for making
|
||||
// internal pipe threads. Tapers smaller upwards if `internal=false`. Tapers smaller downwards
|
||||
// if `internal=true`. If `hollow=true` and `internal=false`, then the pipe threads will be
|
||||
// hollowed out into a pipe with the apropriate internal diameter.
|
||||
// Arguments:
|
||||
// d = Outer diameter of threaded rod.
|
||||
// left_handed = if true, create left-handed threads. Default = false
|
||||
// bevel = if true, bevel the thread ends. Default: false
|
||||
// size = NPT standard pipe size in inches. 1/16", 1/8", 1/4", 3/8", 1/2", 3/4", 1", 1+1/4", 1+1/2", or 2". Default: 1/2"
|
||||
// left_handed = If true, create left-handed threads. Default = false
|
||||
// bevel = If true, bevel the thread ends. Default: false
|
||||
// hollow = If true, create a pipe with the correct internal diameter.
|
||||
// internal = If true, make this a mask for making internal threads.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
|
@ -467,9 +471,16 @@ module npt_threaded_rod(
|
|||
size=1/2,
|
||||
left_handed=false,
|
||||
bevel=false,
|
||||
hollow=false,
|
||||
internal=false,
|
||||
anchor, spin, orient
|
||||
) {
|
||||
assert(is_finite(size));
|
||||
assert(is_bool(left_handed));
|
||||
assert(is_bool(bevel));
|
||||
assert(is_bool(hollow));
|
||||
assert(is_bool(internal));
|
||||
assert(!(internal&&hollow), "Cannot created a hollow internal threads mask.");
|
||||
info_table = [
|
||||
// Size OD len TPI
|
||||
[ 1/16, [ 0.3896, 0.308, 27 ]],
|
||||
|
@ -488,8 +499,10 @@ module npt_threaded_rod(
|
|||
l = 25.4 * info[0];
|
||||
d = 25.4 * info[1];
|
||||
pitch = 25.4 / info[2];
|
||||
r1 = get_radius(d=d, dflt=0.84 * 25.4 / 2);
|
||||
r2 = r1 - l/32;
|
||||
rr = get_radius(d=d, dflt=0.84 * 25.4 / 2);
|
||||
rr2 = rr - l/32;
|
||||
r1 = internal? rr2 : rr;
|
||||
r2 = internal? rr : rr2;
|
||||
depth = pitch * cos(30) * 5/8;
|
||||
profile = internal? [
|
||||
[-6/16, -depth/pitch],
|
||||
|
@ -506,20 +519,28 @@ module npt_threaded_rod(
|
|||
[ 6/16, -depth/pitch],
|
||||
[ 7/16, -depth/pitch*1.07]
|
||||
];
|
||||
trapezoidal_threaded_rod(
|
||||
d1=2*r1, d2=2*r2, l=l,
|
||||
pitch=pitch,
|
||||
thread_depth=depth,
|
||||
thread_angle=30,
|
||||
profile=profile,
|
||||
left_handed=left_handed,
|
||||
bevel=bevel,
|
||||
internal=internal,
|
||||
higbee=r1*PI/2,
|
||||
anchor=anchor,
|
||||
spin=spin,
|
||||
orient=orient
|
||||
) children();
|
||||
attachable(anchor,spin,orient, l=l, r1=r1, r2=r2) {
|
||||
difference() {
|
||||
trapezoidal_threaded_rod(
|
||||
d1=2*r1, d2=2*r2, l=l,
|
||||
pitch=pitch,
|
||||
thread_depth=depth,
|
||||
thread_angle=30,
|
||||
profile=profile,
|
||||
left_handed=left_handed,
|
||||
bevel=bevel,
|
||||
internal=internal,
|
||||
higbee=r1*PI/2,
|
||||
anchor=anchor,
|
||||
spin=spin,
|
||||
orient=orient
|
||||
);
|
||||
if (hollow) {
|
||||
cylinder(l=l+1, d=size*INCH, center=true);
|
||||
} else nil();
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -414,8 +414,8 @@ function rot(a=0, v, cp, from, to, reverse=false, planar=false, p, _m) =
|
|||
assert(approx(point3d(from).z, 0), "'from' must be a 2D vector when 'planar' is true.")
|
||||
assert(approx(point3d(to).z, 0), "'to' must be a 2D vector when 'planar' is true.")
|
||||
affine2d_zrot(
|
||||
vang(point2d(to)) -
|
||||
vang(point2d(from))
|
||||
v_theta(to) -
|
||||
v_theta(from)
|
||||
),
|
||||
m2 = is_undef(cp)? m1 : (move(cp) * m1 * move(-cp)),
|
||||
m3 = reverse? matrix_inverse(m2) : m2
|
||||
|
@ -946,8 +946,8 @@ function yscale(y=1, p, cp=0, planar=false) =
|
|||
assert(is_bool(planar))
|
||||
let( cp = is_num(cp)? [0,cp,0] : cp )
|
||||
(planar || (!is_undef(p) && len(p)==2))
|
||||
? scale([1,y],p=p)
|
||||
: scale([1,y,1],p=p);
|
||||
? scale([1,y], cp=cp, p=p)
|
||||
: scale([1,y,1], cp=cp, p=p);
|
||||
|
||||
|
||||
// Function&Module: zscale()
|
||||
|
|
|
@ -90,7 +90,7 @@ function _rotpart(T) = [for(i=[0:3]) [for(j=[0:3]) j<3 || i==3 ? T[i][j] : 0]];
|
|||
// "xjump" | | x | Move the turtle's x position to the specified value
|
||||
// "yjump | | y | Move the turtle's y position to the specified value
|
||||
// "zjump | | y | Move the turtle's y position to the specified value
|
||||
// "left" | [angle] | Turn turtle left by specified angle or default angle
|
||||
// "left" | | [angle] | Turn turtle left by specified angle or default angle
|
||||
// "right" | | [angle] | Turn turtle to the right by specified angle or default angle
|
||||
// "up" | | [angle] | Turn turtle up by specified angle or default angle
|
||||
// "down" | | [angle] | Turn turtle down by specified angle or default angle
|
||||
|
@ -693,7 +693,7 @@ function _turtle3d_list_command(command,arcsteps,movescale, lastT,lastPre,index)
|
|||
assert(is_vector(grow,2), str("Parameter to \"grow\" must be a scalar or 2d vector at index ",index))
|
||||
assert(is_vector(shrink,2), str("Parameter to \"shrink\" must be a scalar or 2d vector at index ",index))
|
||||
let(
|
||||
scaling = point3d(vdiv(grow,shrink),1),
|
||||
scaling = point3d(v_div(grow,shrink),1),
|
||||
usersteps = struct_val(keys,"steps"),
|
||||
roll = struct_val(keys,"roll"),
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -91,6 +91,23 @@ overrides the `anchor=` argument. A `center=true` argument is the same as `anch
|
|||
A `center=false` argument can mean `anchor=[-1,-1,-1]` for a cube, or `anchor=BOTTOM` for a
|
||||
cylinder.
|
||||
|
||||
Many 2D shapes provided by BOSL2 are also anchorable. Due to technical limitations of OpenSCAD,
|
||||
however, `square()` and `circle()` are *not*. BOSL2 provides `rect()` and `oval()` as attachable
|
||||
and anchorable equivalents. You can only anchor on the XY plane, of course, but you can use the
|
||||
same `FRONT`, `BACK`, `LEFT`, `RIGHT`, and `CENTER` anchor constants.
|
||||
|
||||
```openscad-2D
|
||||
rect([40,30], anchor=BACK+LEFT);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
oval(d=50, anchor=FRONT);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
hexagon(d=50, anchor=BACK);
|
||||
```
|
||||
|
||||
|
||||
## Spin
|
||||
Attachable shapes also can be spun in place as you create them. You can do this by passing in
|
||||
|
@ -107,6 +124,17 @@ vector, like [Xang,Yang,Zang]:
|
|||
cube([20,20,40], center=true, spin=[10,20,30]);
|
||||
```
|
||||
|
||||
You can also apply spin to 2D shapes from BOSL2. Again, you should use `rect()` and `oval()`
|
||||
instead of `square()` and `circle()`:
|
||||
|
||||
```openscad-2D
|
||||
rect([40,30], spin=30);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
oval(d=[40,30], spin=30);
|
||||
```
|
||||
|
||||
|
||||
## Orientation
|
||||
Another way to specify a rotation for an attachable shape, is to pass a 3D vector via the
|
||||
|
@ -117,6 +145,9 @@ For example, you can make a cone that is tilted up and to the right like this:
|
|||
cylinder(h=100, r1=50, r2=20, orient=UP+RIGHT);
|
||||
```
|
||||
|
||||
You can *not* use `orient=` with 2D shapes.
|
||||
|
||||
|
||||
## Mixing Anchoring, Spin, and Orientation
|
||||
When giving `anchor=`, `spin=`, and `orient=`, they are applied anchoring first, spin second,
|
||||
then orient last. For example, here's a cube:
|
||||
|
@ -150,8 +181,14 @@ However, since spin is applied *after* anchoring, it can actually have a signifi
|
|||
cylinder(d=50, l=40, anchor=FWD, spin=-30);
|
||||
```
|
||||
|
||||
For 2D shapes, you can mix `anchor=` with `spin=`, but not with `orient=`.
|
||||
|
||||
## Attaching Children
|
||||
```openscad-2D
|
||||
rect([40,30], anchor=BACK+LEFT, spin=30);
|
||||
```
|
||||
|
||||
|
||||
## Attaching 3D Children
|
||||
The reason attachables are called that, is because they can be attached to each other.
|
||||
You can do that by making one attachable shape be a child of another attachable shape.
|
||||
By default, the child of an attachable is attached to the center of the parent shape.
|
||||
|
@ -178,13 +215,21 @@ cube(50,center=true)
|
|||
attach(TOP,TOP) cylinder(d1=50,d2=20,l=20);
|
||||
```
|
||||
|
||||
By default, `attach()` causes the child to overlap the parent by 0.01, to let CGAL correctly
|
||||
join the parts. If you need the child to have no overlap, or a different overlap, you can use
|
||||
the `overlap=` argument:
|
||||
By default, `attach()` places the child exactly flush with the surface of the parent. Sometimes
|
||||
it's useful to have the child overlap the parent by insetting a bit. You can do this with the
|
||||
`overlap=` argument to `attach()`. A positive value will inset the child into the parent, and
|
||||
a negative value will outset out from the parent:
|
||||
|
||||
```openscad
|
||||
cube(50,center=true)
|
||||
attach(TOP,TOP,overlap=0) cylinder(d1=50,d2=20,l=20);
|
||||
attach(TOP,overlap=10)
|
||||
cylinder(d=20,l=20);
|
||||
```
|
||||
|
||||
```openscad
|
||||
cube(50,center=true)
|
||||
attach(TOP,overlap=-20)
|
||||
cylinder(d=20,l=20);
|
||||
```
|
||||
|
||||
If you want to position the child at the parent's anchorpoint, without re-orienting, you can
|
||||
|
@ -217,6 +262,23 @@ cube(50, center=true)
|
|||
position([TOP,RIGHT,FRONT]) cylinder(d1=50,d2=20,l=20);
|
||||
```
|
||||
|
||||
## Attaching 2D Children
|
||||
You can use attachments in 2D as well, but only in the XY plane. Also, the built-in `square()`
|
||||
and `circle()` 2D modules do not support attachments. Instead, you should use the `rect()` and
|
||||
`oval()` modules:
|
||||
|
||||
```openscad-2D
|
||||
rect(50,center=true)
|
||||
attach(RIGHT,FRONT)
|
||||
trapezoid(w1=30,w2=0,h=30);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
oval(d=50)
|
||||
attach(BACK,FRONT,overlap=5)
|
||||
trapezoid(w1=30,w2=0,h=30);
|
||||
```
|
||||
|
||||
## Anchor Arrows
|
||||
One way that is useful to show the position and orientation of an anchorpoint is by attaching
|
||||
an anchor arrow to that anchor.
|
||||
|
@ -259,6 +321,7 @@ cylinder(h=100, d=100, center=true)
|
|||
show_anchors(s=30);
|
||||
```
|
||||
|
||||
|
||||
## Tagged Operations
|
||||
BOSL2 introduces the concept of tags. Tags are names that can be given to attachables, so that
|
||||
you can refer to them when performing `diff()`, `intersect()`, and `hulling()` operations.
|
||||
|
@ -372,12 +435,171 @@ cube(50, center=true, $tags="hull") {
|
|||
```
|
||||
|
||||
|
||||
## Masking Children
|
||||
TBW
|
||||
## 3D Masking Attachments
|
||||
To make it easier to mask away shapes from various edges of an attachable parent shape, there
|
||||
are a few specialized alternatives to the `attach()` and `position()` modules.
|
||||
|
||||
### `edge_mask()`
|
||||
If you have a 3D mask shape that you want to difference away from various edges, you can use
|
||||
the `edge_mask()` module. This module will take a vertically oriented shape, and will rotate
|
||||
and move it such that the BACK, RIGHT (X+,Y+) side of the shape will be aligned with the given
|
||||
edges. The shape will be tagged as a "mask" so that you can use `diff("mask")`. For example,
|
||||
here's a shape for rounding an edge:
|
||||
|
||||
```openscad
|
||||
module round_edge(l,r) difference() {
|
||||
translate([-1,-1,-l/2])
|
||||
cube([r+1,r+1,l]);
|
||||
translate([r,r])
|
||||
cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
|
||||
}
|
||||
round_edge(l=30, r=19);
|
||||
```
|
||||
|
||||
You can use that mask to round various edges of a cube:
|
||||
|
||||
```openscad
|
||||
module round_edge(l,r) difference() {
|
||||
translate([-1,-1,-l/2])
|
||||
cube([r+1,r+1,l]);
|
||||
translate([r,r])
|
||||
cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
|
||||
}
|
||||
diff("mask")
|
||||
cube([50,60,70],center=true)
|
||||
edge_mask([TOP,"Z"],except=[BACK,TOP+LEFT])
|
||||
round_edge(l=71,r=10);
|
||||
```
|
||||
|
||||
### `corner_mask()`
|
||||
If you have a 3D mask shape that you want to difference away from various corners, you can use
|
||||
the `corner_mask()` module. This module will take a shape and rotate and move it such that the
|
||||
BACK RIGHT TOP (X+,Y+,Z+) side of the shape will be aligned with the given corner. The shape
|
||||
will be tagged as a "mask" so that you can use `diff("mask")`. For example, here's a shape for
|
||||
rounding a corner:
|
||||
|
||||
```openscad
|
||||
module round_corner(r) difference() {
|
||||
translate(-[1,1,1])
|
||||
cube(r+1);
|
||||
translate([r,r,r])
|
||||
sphere(r=r, style="aligned", $fn=quantup(segs(r),4));
|
||||
}
|
||||
round_corner(r=10);
|
||||
```
|
||||
|
||||
You can use that mask to round various corners of a cube:
|
||||
|
||||
```openscad
|
||||
module round_corner(r) difference() {
|
||||
translate(-[1,1,1])
|
||||
cube(r+1);
|
||||
translate([r,r,r])
|
||||
sphere(r=r, style="aligned", $fn=quantup(segs(r),4));
|
||||
}
|
||||
diff("mask")
|
||||
cube([50,60,70],center=true)
|
||||
corner_mask([TOP,FRONT],LEFT+FRONT+TOP)
|
||||
round_corner(r=10);
|
||||
```
|
||||
|
||||
### Mix and Match Masks
|
||||
You can use `edge_mask()` and `corner_mask()` together as well:
|
||||
|
||||
```openscad
|
||||
module round_corner(r) difference() {
|
||||
translate(-[1,1,1])
|
||||
cube(r+1);
|
||||
translate([r,r,r])
|
||||
sphere(r=r, style="aligned", $fn=quantup(segs(r),4));
|
||||
}
|
||||
module round_edge(l,r) difference() {
|
||||
translate([-1,-1,-l/2])
|
||||
cube([r+1,r+1,l]);
|
||||
translate([r,r])
|
||||
cylinder(h=l+1,r=r,center=true, $fn=quantup(segs(r),4));
|
||||
}
|
||||
diff("mask")
|
||||
cube([50,60,70],center=true) {
|
||||
edge_mask("ALL") round_edge(l=71,r=10);
|
||||
corner_mask("ALL") round_corner(r=10);
|
||||
}
|
||||
```
|
||||
|
||||
## 2D Profile Mask Attachments
|
||||
While 3D mask shapes give you a great deal of control, you need to make sure they are correctly
|
||||
sized, and you need to provide separate mask shapes for corners and edges. Often, a single 2D
|
||||
profile could be used to describe the edge mask shape (via `linear_extrude()`), and the corner
|
||||
mask shape (via `rotate_extrude()`). This is where `edge_profile()`, `corner_profile()`, and
|
||||
`face_profile()` come in.
|
||||
|
||||
### `edge_profile()`
|
||||
Using the `edge_profile()` module, you can provide a 2D profile shape and it will be linearly
|
||||
extruded to a mask of the apropriate length for each given edge. The resultant mask will be
|
||||
tagged with "mask" so that you can difference it away with `diff("mask")`. The 2D profile is
|
||||
assumed to be oriented with the BACK, RIGHT (X+,Y+) quadrant as the "cutter edge" that gets
|
||||
re-oriented towards the edges of the parent shape. A typical mask profile for chamfering an
|
||||
edge may look like:
|
||||
|
||||
```openscad-2D
|
||||
mask2d_roundover(10);
|
||||
```
|
||||
|
||||
Using that mask profile, you can mask the edges of a cube like:
|
||||
|
||||
```openscad
|
||||
diff("mask")
|
||||
cube([50,60,70],center=true)
|
||||
edge_profile("ALL")
|
||||
mask2d_roundover(10);
|
||||
```
|
||||
|
||||
### `corner_profile()`
|
||||
You can use the same profile to make a rounded corner mask as well:
|
||||
|
||||
```openscad
|
||||
diff("mask")
|
||||
cube([50,60,70],center=true)
|
||||
corner_profile("ALL", r=10)
|
||||
mask2d_roundover(10);
|
||||
```
|
||||
|
||||
### `face_profile()`
|
||||
As a simple shortcut to apply a profile mask to all edges and corners of a face, you can use the
|
||||
`face_profile()` module:
|
||||
|
||||
```openscad
|
||||
diff("mask")
|
||||
cube([50,60,70],center=true)
|
||||
face_profile(TOP, r=10)
|
||||
mask2d_roundover(10);
|
||||
```
|
||||
|
||||
|
||||
## Coloring Attachables
|
||||
TBW
|
||||
Usually, when coloring a shape with the `color()` module, the parent color overrides the colors of
|
||||
all children. This is often not what you want:
|
||||
|
||||
```openscad
|
||||
$fn = 24;
|
||||
color("red") spheroid(d=3) {
|
||||
attach(CENTER,BOT) color("white") cyl(h=10, d=1) {
|
||||
attach(TOP,BOT) color("green") cyl(h=5, d1=3, d2=0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you use the `recolor()` module, however, the child's color overrides the color of the parent.
|
||||
This is probably easier to understand by example:
|
||||
|
||||
```openscad
|
||||
$fn = 24;
|
||||
recolor("red") spheroid(d=3) {
|
||||
attach(CENTER,BOT) recolor("white") cyl(h=10, d=1) {
|
||||
attach(TOP,BOT) recolor("green") cyl(h=5, d1=3, d2=0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Making Attachables
|
||||
|
@ -683,6 +905,74 @@ stellate_cube() show_anchors(50);
|
|||
|
||||
|
||||
## Making Named Anchors
|
||||
TBW
|
||||
While vector anchors are often useful, sometimes there are logically extra attachment points that
|
||||
aren't on the perimeter of the shape. This is what named string anchors are for. For example,
|
||||
the `teardrop()` shape uses a cylindrical geometry for it's vector anchors, but it also provides
|
||||
a named anchor "cap" that is at the tip of the hat of the teardrop shape.
|
||||
|
||||
Named anchors are passed as an array of `anchorpt()`s to the `anchors=` argument of `attachable()`.
|
||||
The `anchorpt()` call takes a name string, a positional point, an orientation vector, and a spin.
|
||||
The name is the name of the anchor. The positional point is where the anchorpoint is at. The
|
||||
orientation vector is the direction that a child attached at that anchorpoint should be oriented.
|
||||
The spin is the number of degrees that an attached child should be rotated counter-clockwise around
|
||||
the orientation vector. Spin is optional, and defaults to 0.
|
||||
|
||||
To make a simple attachable shape similar to a `teardrop()` that provides a "cap" anchor, you may
|
||||
define it like this:
|
||||
|
||||
```openscad
|
||||
module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) {
|
||||
anchors = [
|
||||
anchorpt("cap", [0,r/sin(45),0], BACK, 0)
|
||||
];
|
||||
attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) {
|
||||
linear_extrude(height=thick, center=true) {
|
||||
circle(r=r);
|
||||
back(r*sin(45)) zrot(45) square(r, center=true);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
raindrop(r=25, thick=20, anchor="cap");
|
||||
```
|
||||
|
||||
If you want multiple named anchors, just add them to the list of anchors:
|
||||
|
||||
```openscad-FlatSpin,VPD=150
|
||||
module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) {
|
||||
anchors = [
|
||||
anchorpt("captop", [0,r/sin(45), thick/2], BACK+UP, 0),
|
||||
anchorpt("cap", [0,r/sin(45), 0 ], BACK, 0),
|
||||
anchorpt("capbot", [0,r/sin(45),-thick/2], BACK+DOWN, 0)
|
||||
];
|
||||
attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) {
|
||||
linear_extrude(height=thick, center=true) {
|
||||
circle(r=r);
|
||||
back(r*sin(45)) zrot(45) square(r, center=true);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
raindrop(r=15, thick=10) show_anchors();
|
||||
```
|
||||
|
||||
Sometimes the named anchor you want to add may be at a point that is reached through a complicated
|
||||
set of translations and rotations. One quick way to calculate that point is to reproduce those
|
||||
transformations in a transformation matrix chain. This is simplified by how you can use the
|
||||
function forms of almost all the transformation modules to get the transformation matrices, and
|
||||
chain them together with matrix multiplication. For example, if you have:
|
||||
|
||||
```
|
||||
scale([1.1, 1.2, 1.3]) xrot(15) zrot(25) right(20) sphere(d=1);
|
||||
```
|
||||
|
||||
and you want to calculate the centerpoint of the sphere, you can do it like:
|
||||
|
||||
```
|
||||
sphere_pt = apply(
|
||||
scale([1.1, 1.2, 1.3]) * xrot(15) * zrot(25) * right(20),
|
||||
[0,0,0]
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
|
|
69
vectors.scad
69
vectors.scad
|
@ -11,7 +11,7 @@
|
|||
|
||||
// Function: is_vector()
|
||||
// Usage:
|
||||
// is_vector(v, [length]);
|
||||
// is_vector(v, <length>, ...);
|
||||
// Description:
|
||||
// Returns true if v is a list of finite numbers.
|
||||
// Arguments:
|
||||
|
@ -42,20 +42,17 @@ function is_vector(v, length, zero, all_nonzero=false, eps=EPSILON) =
|
|||
&& (!all_nonzero || all_nonzero(v)) ;
|
||||
|
||||
|
||||
// Function: vang()
|
||||
// Function: v_theta()
|
||||
// Usage:
|
||||
// theta = vang([X,Y]);
|
||||
// theta_phi = vang([X,Y,Z]);
|
||||
// theta = v_theta([X,Y]);
|
||||
// Description:
|
||||
// Given a 2D vector, returns the angle in degrees counter-clockwise from X+ on the XY plane.
|
||||
// Given a 3D vector, returns [THETA,PHI] where THETA is the number of degrees counter-clockwise from X+ on the XY plane, and PHI is the number of degrees up from the X+ axis along the XZ plane.
|
||||
function vang(v) =
|
||||
// Given a vector, returns the angle in degrees counter-clockwise from X+ on the XY plane.
|
||||
function v_theta(v) =
|
||||
assert( is_vector(v,2) || is_vector(v,3) , "Invalid vector")
|
||||
len(v)==2? atan2(v.y,v.x) :
|
||||
let(res=xyz_to_spherical(v)) [res[1], 90-res[2]];
|
||||
atan2(v.y,v.x);
|
||||
|
||||
|
||||
// Function: vmul()
|
||||
// Function: v_mul()
|
||||
// Description:
|
||||
// Element-wise multiplication. Multiplies each element of `v1` by the corresponding element of `v2`.
|
||||
// Both `v1` and `v2` must be the same length. Returns a vector of the products.
|
||||
|
@ -63,13 +60,13 @@ function vang(v) =
|
|||
// v1 = The first vector.
|
||||
// v2 = The second vector.
|
||||
// Example:
|
||||
// vmul([3,4,5], [8,7,6]); // Returns [24, 28, 30]
|
||||
function vmul(v1, v2) =
|
||||
// v_mul([3,4,5], [8,7,6]); // Returns [24, 28, 30]
|
||||
function v_mul(v1, v2) =
|
||||
assert( is_list(v1) && is_list(v2) && len(v1)==len(v2), "Incompatible input")
|
||||
[for (i = [0:1:len(v1)-1]) v1[i]*v2[i]];
|
||||
|
||||
|
||||
// Function: vdiv()
|
||||
// Function: v_div()
|
||||
// Description:
|
||||
// Element-wise vector division. Divides each element of vector `v1` by
|
||||
// the corresponding element of vector `v2`. Returns a vector of the quotients.
|
||||
|
@ -77,35 +74,35 @@ function vmul(v1, v2) =
|
|||
// v1 = The first vector.
|
||||
// v2 = The second vector.
|
||||
// Example:
|
||||
// vdiv([24,28,30], [8,7,6]); // Returns [3, 4, 5]
|
||||
function vdiv(v1, v2) =
|
||||
// v_div([24,28,30], [8,7,6]); // Returns [3, 4, 5]
|
||||
function v_div(v1, v2) =
|
||||
assert( is_vector(v1) && is_vector(v2,len(v1)), "Incompatible vectors")
|
||||
[for (i = [0:1:len(v1)-1]) v1[i]/v2[i]];
|
||||
|
||||
|
||||
// Function: vabs()
|
||||
// Function: v_abs()
|
||||
// Description: Returns a vector of the absolute value of each element of vector `v`.
|
||||
// Arguments:
|
||||
// v = The vector to get the absolute values of.
|
||||
// Example:
|
||||
// vabs([-1,3,-9]); // Returns: [1,3,9]
|
||||
function vabs(v) =
|
||||
// v_abs([-1,3,-9]); // Returns: [1,3,9]
|
||||
function v_abs(v) =
|
||||
assert( is_vector(v), "Invalid vector" )
|
||||
[for (x=v) abs(x)];
|
||||
|
||||
|
||||
// Function: vfloor()
|
||||
// Function: v_floor()
|
||||
// Description:
|
||||
// Returns the given vector after performing a `floor()` on all items.
|
||||
function vfloor(v) =
|
||||
function v_floor(v) =
|
||||
assert( is_vector(v), "Invalid vector" )
|
||||
[for (x=v) floor(x)];
|
||||
|
||||
|
||||
// Function: vceil()
|
||||
// Function: v_ceil()
|
||||
// Description:
|
||||
// Returns the given vector after performing a `ceil()` on all items.
|
||||
function vceil(v) =
|
||||
function v_ceil(v) =
|
||||
assert( is_vector(v), "Invalid vector" )
|
||||
[for (x=v) ceil(x)];
|
||||
|
||||
|
@ -213,7 +210,7 @@ function vector_axis(v1,v2=undef,v3=undef) =
|
|||
w1 = point3d(v1/norm(v1)),
|
||||
w2 = point3d(v2/norm(v2)),
|
||||
w3 = (norm(w1-w2) > eps && norm(w1+w2) > eps) ? w2
|
||||
: (norm(vabs(w2)-UP) > eps)? UP
|
||||
: (norm(v_abs(w2)-UP) > eps)? UP
|
||||
: RIGHT
|
||||
) unit(cross(w1,w3));
|
||||
|
||||
|
@ -291,6 +288,18 @@ function _vp_tree(ptlist, ind, leafsize) =
|
|||
// tree = vantage point tree from vp_tree
|
||||
// p = point to search for
|
||||
// r = search radius
|
||||
// Example: A set of four queries to find points within 1 unit of the query. The circles show the search region and all have radius 1.
|
||||
// $fn=32;
|
||||
// k = 2000;
|
||||
// points = array_group(rands(0,10,k*2,seed=13333),2);
|
||||
// vp = vp_tree(points);
|
||||
// queries = [for(i=[3,7],j=[3,7]) [i,j]];
|
||||
// search_ind = [for(q=queries) vp_search(points, vp, q, 1)];
|
||||
// move_copies(points) circle(r=.08);
|
||||
// for(i=idx(queries)){
|
||||
// color("blue")stroke(move(queries[i],circle(r=1)), closed=true, width=.08);
|
||||
// color("red")move_copies(select(points, search_ind[i])) circle(r=.08);
|
||||
// }
|
||||
function _vp_search(points, tree, p, r) =
|
||||
is_list(tree[0]) ? [for(i=tree[0]) if (norm(points[i]-p)<=r) i]
|
||||
:
|
||||
|
@ -324,6 +333,20 @@ function vp_search(points, tree, p, r) =
|
|||
// tree = vantage point tree from vp_tree
|
||||
// p = point to search for
|
||||
// k = number of neighbors to return
|
||||
// Example: Four queries to find the 15 nearest points. The circles show the radius defined by the most distant query result. Note they are different for each query.
|
||||
// $fn=32;
|
||||
// k = 2000;
|
||||
// points = array_group(rands(0,10,k*2,seed=13333),2);
|
||||
// vp = vp_tree(points);
|
||||
// queries = [for(i=[3,7],j=[3,7]) [i,j]];
|
||||
// search_ind = [for(q=queries) vp_nearest(points, vp, q, 15)];
|
||||
// move_copies(points) circle(r=.08);
|
||||
// for(i=idx(queries)){
|
||||
// color("red")move_copies(select(points, search_ind[i])) circle(r=.08);
|
||||
// color("blue")stroke(move(queries[i],
|
||||
// circle(r=norm(points[last(search_ind[i])]-queries[i]))),
|
||||
// closed=true, width=.08);
|
||||
// }
|
||||
function _insert_sorted(list, k, new) =
|
||||
len(list)==k && new[1]>= last(list)[1] ? list
|
||||
: [
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BOSL_VERSION = [2,0,628];
|
||||
BOSL_VERSION = [2,0,652];
|
||||
|
||||
|
||||
// Section: BOSL Library Version Functions
|
||||
|
|
Loading…
Reference in a new issue