Added spin and anchor to various 2D shapes.

This commit is contained in:
Revar Desmera 2019-05-29 16:27:35 -07:00
parent c7d0be8ba5
commit 3b0a1a3383
4 changed files with 111 additions and 68 deletions

View file

@ -98,7 +98,9 @@ function scale_points(pts, v=[0,0,0], cp=[0,0,0]) = [for (pt = pts) [for (i = [0
// pts = List of 3D points to rotate.
// ang = Angle to rotate by.
// cp = 2D Centerpoint to rotate around. Default: `[0,0]`
function rotate_points2d(pts, ang, cp=[0,0]) = let(
function rotate_points2d(pts, ang, cp=[0,0]) =
approx(ang,0)? pts :
let(
m = affine2d_zrot(ang)
) [for (pt = pts) m*point3d(pt-cp)+cp];
@ -155,7 +157,9 @@ function rotate_points3d(pts, a=0, v=undef, cp=[0,0,0], from=undef, to=undef, re
)
),
m = affine3d_translate(cp) * mrot * affine3d_translate(-cp)
) [for (pt = pts) point3d(m*concat(point3d(pt),[1]))];
) (!is_undef(from) && approx(from,to))? pts :
(a==0 || a==[0,0,0])? pts :
[for (pt = pts) point3d(m*concat(point3d(pt),[1]))];

View file

@ -90,6 +90,8 @@ module stroke(path, width=1, endcaps=true, close=false)
// r = The radius of the circle to get a slice of.
// d = The diameter of the circle to get a slice of.
// ang = The angle of the arc of the pie slice.
// 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`
// Examples(2D):
// pie_slice2d(r=50,ang=30);
// pie_slice2d(d=100,ang=45);
@ -97,18 +99,19 @@ module stroke(path, width=1, endcaps=true, close=false)
// pie_slice2d(d=40,ang=240);
// Example(2D): Called as Function
// stroke(close=true, pie_slice2d(r=50,ang=30));
function pie_slice2d(r=undef, d=undef, ang=30) =
function pie_slice2d(r=undef, d=undef, ang=30, anchor=CENTER, spin=0) =
let(
r = get_radius(r=r, d=d, dflt=10),
sides = ceil(segs(r)*ang/360)
) concat(
[[0,0]],
[for (i=[0:1:sides]) let(a=i*ang/sides) r*[cos(a),sin(a)]]
);
sides = ceil(segs(r)*ang/360),
path = concat(
[[0,0]],
[for (i=[0:1:sides]) let(a=i*ang/sides) r*[cos(a),sin(a)]]
)
) echo(r=r, path=path, anchor=anchor, na=normalize(anchor)) rot(spin, p=move(-r*normalize(anchor), p=path));
module pie_slice2d(r=undef, d=undef, ang=30) {
pts = pie_slice2d(r=r, d=d, ang=ang);
module pie_slice2d(r=undef, d=undef, ang=30, anchor=CENTER, spin=0) {
pts = pie_slice2d(r=r, d=d, ang=ang, anchor=anchor, spin=spin);
polygon(pts);
}
@ -130,7 +133,7 @@ module pie_slice2d(r=undef, d=undef, ang=30) {
// If called as a function, returns a 2D or 3D path forming an arc.
// If called as a module, creates a 2D arc polygon or pie slice shape.
// Arguments:
// N = Number of line segments to form the arc curve from.
// N = Number of vertices to form the arc curve from.
// r = Radius of the arc.
// d = Diameter of the arc.
// angle = If a scalar, specifies the end angle in degrees. If a vector of two scalars, specifies start and end angles.
@ -141,26 +144,23 @@ module pie_slice2d(r=undef, d=undef, ang=30) {
// start = Start angle of arc.
// wedge = If true, include centerpoint `cp` in output to form pie slice shape.
// Examples(2D):
// arc(N=8, r=30, angle=30, wedge=true);
// arc(N=8, d=60, angle=30, wedge=true);
// arc(N=12, d=60, angle=120);
// arc(N=12, d=60, angle=120, wedge=true);
// arc(N=4, r=30, angle=30, wedge=true);
// arc(N=4, d=60, angle=30, wedge=true);
// arc(N=8, d=60, angle=120);
// arc(N=8, d=60, angle=120, wedge=true);
// arc(N=12, r=30, angle=[75,135], wedge=true);
// arc(N=12, r=30, start=45, angle=75, wedge=true);
// arc(N=24, width=60, thickness=20);
// arc(N=12, width=60, thickness=20);
// arc(N=12, cp=[-10,5], points=[[20,10],[0,35]], wedge=true);
// arc(N=12, points=[[30,-5],[20,10],[-10,20]], wedge=true);
// arc(N=12, points=[[5,30],[-10,-10],[30,5]], wedge=true);
// Example(2D):
// path = arc(N=12, points=[[5,30],[-10,-10],[30,5]], wedge=true);
// stroke(close=true, path);
// Example(FlatSpin):
// include <BOSL2/paths.scad>
// path = arc(N=12, points=[[0,30,0],[0,0,30],[30,0,0]]);
// trace_polyline(path, showpts=true, color="cyan");
module arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false)
{
path = arc(N=N, r=r, angle=angle, d=d, cp=cp, points=points, width=width, thickness=thickness, start=start, wedge=wedge);
polygon(path);
}
function arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false) =
// First try for 2d arc specified by angles
is_def(width) && is_def(thickness)? (
@ -172,7 +172,7 @@ function arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false) =
)
assert(parmok,"Invalid parameters in arc")
let(
cp = is_def(cp) ? cp : [0,0],
cp = is_def(cp) ? cp : [0,0],
start = is_def(start)? start : is_vector(angle) ? angle[0] : 0,
angle = is_vector(angle)? angle[1]-angle[0] : angle,
r = get_radius(r=r,d=d),
@ -222,6 +222,13 @@ function arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false) =
);
module arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false)
{
path = arc(N=N, r=r, angle=angle, d=d, cp=cp, points=points, width=width, thickness=thickness, start=start, wedge=wedge);
polygon(path);
}
function _normal_segment(p1,p2) =
let(center = (p1+p2)/2)
[center, center + norm(p1-p2)/2 * line_normal(p1,p2)];
@ -237,18 +244,24 @@ function _normal_segment(p1,p2) =
// h = The Y axis height of the trapezoid.
// w1 = The X axis width of the front end of the trapezoid.
// w2 = The X axis width of the back end of the trapezoid.
// 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`
// Examples(2D):
// trapezoid(h=30, w1=40, w2=20);
// trapezoid(h=25, w1=20, w2=35);
// trapezoid(h=20, w1=40, w2=0);
// Example(2D): Called as Function
// stroke(close=true, trapezoid(h=30, w1=40, w2=20));
function trapezoid(h, w1, w2) =
[[-w1/2,-h/2], [-w2/2,h/2], [w2/2,h/2], [w1/2,-h/2]];
function trapezoid(h, w1, w2, anchor=CENTER, spin=0) =
let(
s = anchor.y>0? [w2,h] : anchor.y<0? [w1,h] : [(w1+w2)/2,h],
path = [[-w1/2,-h/2], [-w2/2,h/2], [w2/2,h/2], [w1/2,-h/2]]
) rot(spin, p=move(-vmul(anchor,s/2), p=path));
module trapezoid(h, w1, w2)
polygon(trapezoid(h=h, w1=w1, w2=w2));
module trapezoid(h, w1, w2, anchor=CENTER, spin=0)
polygon(trapezoid(h=h, w1=w1, w2=w2, anchor=anchor, spin=spin));
// Function&Module: regular_ngon()
@ -267,6 +280,8 @@ module trapezoid(h, w1, w2)
// id = Inside diameter, at center of sides.
// side = Length of each side.
// realign = If false, a tip is aligned with the Y+ axis. If true, the midpoint of a side is aligned with the Y+ axis. Default: false
// 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`
// Example(2D): by Outer Size
// regular_ngon(n=5, or=30);
// regular_ngon(n=5, od=60);
@ -279,16 +294,17 @@ module trapezoid(h, w1, w2)
// regular_ngon(n=8, side=20, realign=true);
// Example(2D): Called as Function
// stroke(close=true, regular_ngon(n=6, or=30));
function regular_ngon(n=6, or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false) =
function regular_ngon(n=6, or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false, anchor=CENTER, spin=0) =
let(
sc = 1/cos(180/n),
r = get_radius(r1=ir*sc, r=or, d1=id*sc, d=od, dflt=side/2/sin(180/n)),
offset = 90 + (realign? (180/n) : 0)
) [for (a=[0:360/n:360-EPSILON]) r*[cos(a+offset),sin(a+offset)]];
offset = 90 + (realign? (180/n) : 0),
path = [for (a=[0:360/n:360-EPSILON]) r*[cos(a+offset),sin(a+offset)]]
) rot(spin, p=move(-r*normalize(anchor), p=path));
module regular_ngon(n=6, or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false)
polygon(regular_ngon(n=n,or=or,od=od,ir=ir,id=id,side=side,realign=realign));
module regular_ngon(n=6, or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false, anchor=CENTER, spin=0)
polygon(regular_ngon(n=n,or=or,od=od,ir=ir,id=id,side=side,realign=realign, anchor=anchor, spin=spin));
// Function&Module: pentagon()
@ -306,6 +322,8 @@ module regular_ngon(n=6, or=undef, od=undef, ir=undef, id=undef, side=undef, rea
// id = Inside diameter, at center of sides.
// side = Length of each side.
// realign = If false, a tip is aligned with the Y+ axis. If true, the midpoint of a side is aligned with the Y+ axis. Default: false
// 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`
// Example(2D): by Outer Size
// pentagon(or=30);
// pentagon(od=60);
@ -318,12 +336,12 @@ module regular_ngon(n=6, or=undef, od=undef, ir=undef, id=undef, side=undef, rea
// pentagon(side=20, realign=true);
// Example(2D): Called as Function
// stroke(close=true, pentagon(or=30));
function pentagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false) =
regular_ngon(n=5, or=or, od=od, ir=ir, id=id, side=side, realign=realign);
function pentagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false, anchor=CENTER, spin=0) =
regular_ngon(n=5, or=or, od=od, ir=ir, id=id, side=side, realign=realign, anchor=anchor, spin=spin);
module pentagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false)
polygon(pentagon(or=or, od=od, ir=ir, id=id, side=side, realign=realign));
module pentagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false, anchor=CENTER, spin=0)
polygon(pentagon(or=or, od=od, ir=ir, id=id, side=side, realign=realign, anchor=anchor, spin=spin));
// Function&Module: hexagon()
@ -339,6 +357,8 @@ module pentagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=fals
// id = Inside diameter, at center of sides.
// side = Length of each side.
// realign = If false, a tip is aligned with the Y+ axis. If true, the midpoint of a side is aligned with the Y+ axis. Default: false
// 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`
// Example(2D): by Outer Size
// hexagon(or=30);
// hexagon(od=60);
@ -351,12 +371,12 @@ module pentagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=fals
// hexagon(side=20, realign=true);
// Example(2D): Called as Function
// stroke(close=true, hexagon(or=30));
function hexagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false) =
regular_ngon(n=6, or=or, od=od, ir=ir, id=id, side=side, realign=realign);
function hexagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false, anchor=CENTER, spin=0) =
regular_ngon(n=6, or=or, od=od, ir=ir, id=id, side=side, realign=realign, anchor=anchor, spin=spin);
module hexagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false)
polygon(hexagon(or=or, od=od, ir=ir, id=id, side=side, realign=realign));
module hexagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false, anchor=CENTER, spin=0)
polygon(hexagon(or=or, od=od, ir=ir, id=id, side=side, realign=realign, anchor=anchor, spin=spin));
// Function&Module: octagon()
@ -372,6 +392,8 @@ module hexagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false
// id = Inside diameter, at center of sides.
// side = Length of each side.
// realign = If false, a tip is aligned with the Y+ axis. If true, the midpoint of a side is aligned with the Y+ axis. Default: false
// 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`
// Example(2D): by Outer Size
// octagon(or=30);
// octagon(od=60);
@ -384,12 +406,12 @@ module hexagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false
// octagon(side=20, realign=true);
// Example(2D): Called as Function
// stroke(close=true, octagon(or=30));
function octagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false) =
regular_ngon(n=8, or=or, od=od, ir=ir, id=id, side=side, realign=realign);
function octagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false, anchor=CENTER, spin=0) =
regular_ngon(n=8, or=or, od=od, ir=ir, id=id, side=side, realign=realign, anchor=anchor, spin=spin);
module octagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false)
polygon(octagon(or=or, od=od, ir=ir, id=id, side=side, realign=realign));
module octagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false, anchor=CENTER, spin=0)
polygon(octagon(or=or, od=od, ir=ir, id=id, side=side, realign=realign, anchor=anchor, spin=spin));
// Function&Module: glued_circles()
@ -403,6 +425,8 @@ module octagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false
// d = The diameter of the end circles.
// spread = The distance between the centers of the end circles.
// tangent = The angle in degrees of the tangent point for the joining arcs, measured away from the Y axis.
// 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`
// Examples(2D):
// glued_circles(r=15, spread=40, tangent=45);
// glued_circles(d=30, spread=30, tangent=30);
@ -410,7 +434,7 @@ module octagon(or=undef, od=undef, ir=undef, id=undef, side=undef, realign=false
// glued_circles(d=30, spread=30, tangent=-30);
// Example(2D): Called as Function
// stroke(close=true, glued_circles(r=15, spread=40, tangent=45));
function glued_circles(r=undef, d=undef, spread=10, tangent=30) =
function glued_circles(r=undef, d=undef, spread=10, tangent=30, anchor=CENTER, spin=0) =
let(
r = get_radius(r=r, d=d, dflt=10),
r2 = (spread/2 / sin(tangent)) - r,
@ -425,17 +449,19 @@ function glued_circles(r=undef, d=undef, spread=10, tangent=30) =
ea2 = 270+tangent,
subarc = ea2-sa2,
arcsegs = ceil(segs(r2)*abs(subarc)/360),
arcstep = subarc / arcsegs
) concat(
[for (i=[0:1:lobesegs]) let(a=sa1+i*lobestep) r * [cos(a),sin(a)] - cp1],
tangent==0? [] : [for (i=[0:1:arcsegs]) let(a=ea2-i*arcstep+180) r2 * [cos(a),sin(a)] - cp2],
[for (i=[0:1:lobesegs]) let(a=sa1+i*lobestep+180) r * [cos(a),sin(a)] + cp1],
tangent==0? [] : [for (i=[0:1:arcsegs]) let(a=ea2-i*arcstep) r2 * [cos(a),sin(a)] + cp2]
);
arcstep = subarc / arcsegs,
s = [spread/2+r, r],
path = concat(
[for (i=[0:1:lobesegs]) let(a=sa1+i*lobestep) r * [cos(a),sin(a)] - cp1],
tangent==0? [] : [for (i=[0:1:arcsegs]) let(a=ea2-i*arcstep+180) r2 * [cos(a),sin(a)] - cp2],
[for (i=[0:1:lobesegs]) let(a=sa1+i*lobestep+180) r * [cos(a),sin(a)] + cp1],
tangent==0? [] : [for (i=[0:1:arcsegs]) let(a=ea2-i*arcstep) r2 * [cos(a),sin(a)] + cp2]
)
) rot(spin, p=move(-vmul(anchor,s), p=path));
module glued_circles(r=undef, d=undef, spread=10, tangent=30)
polygon(glued_circles(r=r, d=d, spread=spread, tangent=tangent));
module glued_circles(r=undef, d=undef, spread=10, tangent=30, anchor=CENTER, spin=0)
polygon(glued_circles(r=r, d=d, spread=spread, tangent=tangent, anchor=anchor, spin=spin));
// Function&Module: star()
@ -452,6 +478,8 @@ module glued_circles(r=undef, d=undef, spread=10, tangent=30)
// id = The diameter to the inner corners of the star.
// step = Calculates the radius of the inner star corners by virtually drawing a straight line `step` tips around the star. 2 <= step < n/2
// realign = If false, a tip is aligned with the Y+ axis. If true, an inner corner is aligned with the Y+ axis. Default: false
// 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`
// Examples(2D):
// star(n=5, r=50, ir=25);
// star(n=5, r=50, step=2);
@ -461,7 +489,7 @@ module glued_circles(r=undef, d=undef, spread=10, tangent=30)
// star(n=7, r=50, step=3, realign=true);
// Example(2D): Called as Function
// stroke(close=true, star(n=5, r=50, ir=25));
function star(n, r, d, ir, id, step, realign=false) =
function star(n, r, d, ir, id, step, realign=false, anchor=CENTER, spin=0) =
let(
r = get_radius(r=r, d=d),
count = num_defined([ir,id,step]),
@ -472,13 +500,13 @@ function star(n, r, d, ir, id, step, realign=false) =
let(
stepr = is_undef(step)? r : r*cos(180*step/n)/cos(180*(step-1)/n),
ir = get_radius(r=ir, d=id, dflt=stepr),
offset = 90+(realign? 180/n : 0)
)
[for(i=[0:1:2*n-1]) let(theta=180*i/n+offset, radius=(i%2)?ir:r) radius*[cos(theta), sin(theta)]];
offset = 90+(realign? 180/n : 0),
path = [for(i=[0:1:2*n-1]) let(theta=180*i/n+offset, radius=(i%2)?ir:r) radius*[cos(theta), sin(theta)]]
) rot(spin, p=move(-r*normalize(anchor), p=path));
module star(n, r, d, ir, id, step, realign=false)
polygon(star(n=n, r=r, d=d, ir=ir, id=id, step=step, realign=realign));
module star(n, r, d, ir, id, step, realign=false, anchor=CENTER, spin=0)
polygon(star(n=n, r=r, d=d, ir=ir, id=id, step=step, realign=realign, anchor=anchor, spin=spin));
function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) =
@ -501,16 +529,24 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) =
// n3 = The n3 argument for the superformula.
// a = The a argument for the superformula.
// b = The b argument for the superformula.
// 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`
// Example(2D):
// superformula_shape(step=0.5,scale=100,m1=16,m2=16,n1=0.5,n2=0.5,n3=16);
// Example(2D): Called as Function
// stroke(close=true, superformula_shape(step=0.5,scale=100,m1=16,m2=16,n1=0.5,n2=0.5,n3=16));
function superformula_shape(step=0.5,scale=1,m1,m2,n1,n2=1,n3=1,a=1,b=1) =
[for (a=[0:step:360]) let(r=scale*_superformula(theta=a,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3)) r*[cos(a),sin(a)]];
function superformula_shape(step=0.5,scale=1,m1,m2,n1,n2=1,n3=1,a=1,b=1, anchor=CENTER, spin=0) =
let(
steps = ceil(360/step),
step = 360/steps,
angs = [for (i = [0:steps-1]) step*i],
rads = [for (a = angs) scale*_superformula(theta=a,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3)],
path = [for (i = [0:steps-1]) let(a=angs[i]) rads[i]*[cos(a), sin(a)]]
) rot(spin, p=move(-max(rads)*normalize(anchor), p=path));
module superformula_shape(step=0.5,scale=1,m1,m2,n1,n2=1,n3=1,a=1,b=1)
polygon(superformula_shape(step=step,scale=scale,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b));
module superformula_shape(step=0.5,scale=1,m1,m2,n1,n2=1,n3=1,a=1,b=1, anchor=CENTER, spin=0)
polygon(superformula_shape(step=step,scale=scale,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b, anchor=anchor, spin=spin));
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -284,8 +284,10 @@ function rot(a=0, v=undef, cp=undef, from=undef, to=undef, reverse=false, p=unde
rot(a=a, v=v, cp=cp, from=from, to=to, reverse=reverse, p=[p], planar=planar)[0]
) : (
planar? (
is_undef(from)? rotate_points2d(p, a=ang*rev, cp=cp) :
rotate_points2d(p, ang=vector_angle(from,to)*sign(vector_axis(from,to)[2])*rev, cp=cp)
is_undef(from)? rotate_points2d(p, a=ang*rev, cp=cp) : (
approx(from,to)? p :
rotate_points2d(p, ang=vector_angle(from,to)*sign(vector_axis(from,to)[2])*rev, cp=cp)
)
) : (
rotate_points3d(p, a=a, v=v, cp=(is_undef(cp)? [0,0,0] : cp), from=from, to=to, reverse=reverse)
)

View file

@ -53,9 +53,10 @@ function vabs(v) = [for (x=v) abs(x)];
// Function: normalize()
// Description:
// Returns unit length normalized version of vector v.
// If passed a zero-length vector, returns the unchanged vector.
// Arguments:
// v = The vector to normalize.
function normalize(v) = v/norm(v);
function normalize(v) = v==[0,0,0]? v : v/norm(v);
// Function: vquant()