mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-01 09:49:45 +00:00
Merge branch 'master' of https://github.com/revarbat/BOSL2
This commit is contained in:
commit
ee41bb859e
15 changed files with 1266 additions and 415 deletions
42
affine.scad
42
affine.scad
|
@ -467,6 +467,48 @@ function is_2d_transform(t) = // z-parameters are zero, except we allow t[2][
|
|||
|
||||
|
||||
|
||||
// Function: rot_decode()
|
||||
// Usage:
|
||||
// [angle,axis,cp,translation] = rot_decode(rotation)
|
||||
// Description:
|
||||
// Given an input 3d rigid transformation operator (one composed of just rotations and translations)
|
||||
// represented as a 4x4 matrix, compute the rotation and translation parameters of the operator.
|
||||
// Returns a list of the four parameters, the angle, in the interval [0,180], the rotation axis
|
||||
// as a unit vector, a centerpoint for the rotation, and a translation. If you set `parms=rot_decode(rotation)`
|
||||
// then the transformation can be reconstructed from parms as `move(parms[3])*rot(a=parms[0],v=parms[1],cp=parms[2])`.
|
||||
// This decomposition makes it possible to perform interpolation. If you construct a transformation using `rot`
|
||||
// the decoding may flip the axis (if you gave an angle outside of [0,180]). The returned axis will be a unit vector,
|
||||
// and the centerpoint lies on the plane through the origin that is perpendicular to the axis. It may be different
|
||||
// than the centerpoint you used to construct the transformation.
|
||||
// Example:
|
||||
// rot_decode(rot(45)); // Returns [45,[0,0,1], [0,0,0], [0,0,0]]
|
||||
// rot_decode(rot(a=37, v=[1,2,3], cp=[4,3,-7]))); // Returns [37, [0.26, 0.53, 0.80], [4.8, 4.6, -4.6], [0,0,0]]
|
||||
// rot_decode(left(12)*xrot(-33)); // Returns [33, [-1,0,0], [0,0,0], [-12,0,0]]
|
||||
// rot_decode(translate([3,4,5])); // Returns [0, [0,0,1], [0,0,0], [3,4,5]]
|
||||
function rot_decode(M) =
|
||||
assert(is_matrix(M,4,4) && M[3]==[0,0,0,1], "Input matrix must be a 4x4 matrix representing a 3d transformation")
|
||||
let(R = submatrix(M,[0:2],[0:2]))
|
||||
assert(approx(det3(R),1) && approx(norm_fro(R * transpose(R)-ident(3)),0),"Input matrix is not a rotation")
|
||||
let(
|
||||
translation = [for(row=[0:2]) M[row][3]], // translation vector
|
||||
largest = max_index([R[0][0], R[1][1], R[2][2]]),
|
||||
axis_matrix = R + transpose(R) - (matrix_trace(R)-1)*ident(3), // Each row is on the rotational axis
|
||||
// Construct quaternion q = c * [x sin(theta/2), y sin(theta/2), z sin(theta/2), cos(theta/2)]
|
||||
q_im = axis_matrix[largest],
|
||||
q_re = R[(largest+2)%3][(largest+1)%3] - R[(largest+1)%3][(largest+2)%3],
|
||||
c_sin = norm(q_im), // c * sin(theta/2) for some c
|
||||
c_cos = abs(q_re) // c * cos(theta/2)
|
||||
)
|
||||
approx(c_sin,0) ? [0,[0,0,1],[0,0,0],translation] :
|
||||
let(
|
||||
angle = 2*atan2(c_sin, c_cos), // This is supposed to be more accurate than acos or asin
|
||||
axis = (q_re>=0 ? 1:-1)*q_im/c_sin,
|
||||
tproj = translation - (translation*axis)*axis, // Translation perpendicular to axis determines centerpoint
|
||||
cp = (tproj + cross(axis,tproj)*c_cos/c_sin)/2
|
||||
)
|
||||
[angle, axis, cp, (translation*axis)*axis];
|
||||
|
||||
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
@ -472,6 +472,73 @@ function line_from_points(points, fast=false, eps=EPSILON) =
|
|||
|
||||
// Section: 2D Triangles
|
||||
|
||||
|
||||
// Function: law_of_cosines()
|
||||
// Usage:
|
||||
// C = law_of_cosines(a, b, c);
|
||||
// c = law_of_cosines(a, b, C);
|
||||
// Description:
|
||||
// Applies the Law of Cosines for an arbitrary triangle.
|
||||
// Given three side lengths, returns the angle in degrees for the corner opposite of the third side.
|
||||
// Given two side lengths, and the angle between them, returns the length of the third side.
|
||||
// Figure(2D):
|
||||
// stroke([[-50,0], [10,60], [50,0]], closed=true);
|
||||
// color("black") {
|
||||
// translate([ 33,35]) text(text="a", size=8, halign="center", valign="center");
|
||||
// translate([ 0,-6]) text(text="b", size=8, halign="center", valign="center");
|
||||
// translate([-22,35]) text(text="c", size=8, halign="center", valign="center");
|
||||
// }
|
||||
// color("blue") {
|
||||
// translate([-37, 6]) text(text="A", size=8, halign="center", valign="center");
|
||||
// translate([ 9,51]) text(text="B", size=8, halign="center", valign="center");
|
||||
// translate([ 38, 6]) text(text="C", size=8, halign="center", valign="center");
|
||||
// }
|
||||
// Arguments:
|
||||
// a = The length of the first side.
|
||||
// b = The length of the second side.
|
||||
// c = The length of the third side.
|
||||
// C = The angle in degrees of the corner opposite of the third side.
|
||||
function law_of_cosines(a, b, c, C) =
|
||||
// Triangle Law of Cosines:
|
||||
// c^2 = a^2 + b^2 - 2*a*b*cos(C)
|
||||
assert(num_defined([c,C]) == 1, "Must give exactly one of c= or C=.")
|
||||
is_undef(c) ? sqrt(a*a + b*b - 2*a*b*cos(C)) :
|
||||
acos(constrain((a*a + b*b - c*c) / (2*a*b), -1, 1));
|
||||
|
||||
|
||||
// Function: law_of_sines()
|
||||
// Usage:
|
||||
// B = law_of_sines(a, A, b);
|
||||
// b = law_of_sines(a, A, B);
|
||||
// Description:
|
||||
// Applies the Law of Sines for an arbitrary triangle.
|
||||
// Given two triangle side lengths and the angle between them, returns the angle of the corner opposite of the second side.
|
||||
// Given a side length, the opposing angle, and a second angle, returns the length of the side opposite of the second angle.
|
||||
// Figure(2D):
|
||||
// stroke([[-50,0], [10,60], [50,0]], closed=true);
|
||||
// color("black") {
|
||||
// translate([ 33,35]) text(text="a", size=8, halign="center", valign="center");
|
||||
// translate([ 0,-6]) text(text="b", size=8, halign="center", valign="center");
|
||||
// translate([-22,35]) text(text="c", size=8, halign="center", valign="center");
|
||||
// }
|
||||
// color("blue") {
|
||||
// translate([-37, 6]) text(text="A", size=8, halign="center", valign="center");
|
||||
// translate([ 9,51]) text(text="B", size=8, halign="center", valign="center");
|
||||
// translate([ 38, 6]) text(text="C", size=8, halign="center", valign="center");
|
||||
// }
|
||||
// Arguments:
|
||||
// a = The length of the first side.
|
||||
// A = The angle in degrees of the corner opposite of the first side.
|
||||
// b = The length of the second side.
|
||||
// B = The angle in degrees of the corner opposite of the second side.
|
||||
function law_of_sines(a, A, b, B) =
|
||||
// Triangle Law of Sines:
|
||||
// a/sin(A) = b/sin(B) = c/sin(C)
|
||||
assert(num_defined([b,B]) == 1, "Must give exactly one of b= or B=.")
|
||||
let( r = a/sin(A) )
|
||||
is_undef(b) ? r*sin(B) : asin(constrain(b/r, -1, 1));
|
||||
|
||||
|
||||
// Function: tri_calc()
|
||||
// Usage:
|
||||
// tri_calc(ang,ang2,adj,opp,hyp);
|
||||
|
@ -750,8 +817,8 @@ function adj_opp_to_ang(adj,opp) =
|
|||
function triangle_area(a,b,c) =
|
||||
assert( is_path([a,b,c]), "Invalid points or incompatible dimensions." )
|
||||
len(a)==3
|
||||
? 0.5*norm(cross(c-a,c-b))
|
||||
: 0.5*cross(c-a,c-b);
|
||||
? 0.5*norm(cross(c-a,c-b))
|
||||
: 0.5*cross(c-a,c-b);
|
||||
|
||||
|
||||
|
||||
|
@ -816,7 +883,7 @@ function plane3pt_indexed(points, i1, i2, i3) =
|
|||
function plane_from_normal(normal, pt=[0,0,0]) =
|
||||
assert( is_matrix([normal,pt],2,3) && !approx(norm(normal),0),
|
||||
"Inputs `normal` and `pt` should 3d vectors/points and `normal` cannot be zero." )
|
||||
concat(normal, normal*pt)/norm(normal);
|
||||
concat(normal, normal*pt) / norm(normal);
|
||||
|
||||
|
||||
// Function: plane_from_points()
|
||||
|
|
1359
involute_gears.scad
1359
involute_gears.scad
File diff suppressed because it is too large
Load diff
10
math.scad
10
math.scad
|
@ -904,6 +904,16 @@ function norm_fro(A) =
|
|||
norm(flatten(A));
|
||||
|
||||
|
||||
// Function: matrix_trace()
|
||||
// Usage:
|
||||
// matrix_trace(M)
|
||||
// Description:
|
||||
// Computes the trace of a square matrix, the sum of the entries on the diagonal.
|
||||
function matrix_trace(M) =
|
||||
assert(is_matrix(M,square=true), "Input to trace must be a square matrix")
|
||||
[for(i=[0:1:len(M)-1])1] * [for(i=[0:1:len(M)-1]) M[i][i]];
|
||||
|
||||
|
||||
// Section: Comparisons and Logic
|
||||
|
||||
// Function: all_zero()
|
||||
|
|
|
@ -238,7 +238,7 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP)
|
|||
// r = Radius of the sphere.
|
||||
// d = Diameter of the sphere.
|
||||
// circum = If true, the sphere is made large enough to circumscribe the sphere of the ideal side. Otherwise inscribes. Default: false (inscribes)
|
||||
// style = The style of the sphere's construction. One of "orig", "aligned", "stagger", or "icosa". Default: "orig"
|
||||
// style = The style of the sphere's construction. One of "orig", "aligned", "stagger", "octa", or "icosa". Default: "orig"
|
||||
// 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`
|
||||
|
@ -267,11 +267,11 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP)
|
|||
// Example: Called as Function
|
||||
// vnf = sphere(d=100, style="icosa");
|
||||
// vnf_polyhedron(vnf);
|
||||
module sphere(r, d, circum=false, style="aligned", anchor=CENTER, spin=0, orient=UP)
|
||||
module sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP)
|
||||
spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient) children();
|
||||
|
||||
|
||||
function sphere(r, d, circum=false, style="aligned", anchor=CENTER, spin=0, orient=UP) =
|
||||
function sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) =
|
||||
spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient);
|
||||
|
||||
|
||||
|
|
63
regions.scad
63
regions.scad
|
@ -296,7 +296,7 @@ function region_faces(region, transform, reverse=false, vnf=EMPTY_VNF) =
|
|||
|
||||
// Function&Module: linear_sweep()
|
||||
// Usage:
|
||||
// linear_sweep(path, height, [center], [slices], [twist], [scale], [style], [convexity]);
|
||||
// linear_sweep(region, height, [center], [slices], [twist], [scale], [style], [convexity]);
|
||||
// Description:
|
||||
// If called as a module, creates a polyhedron that is the linear extrusion of the given 2D region or path.
|
||||
// If called as a function, returns a VNF that can be used to generate a polyhedron of the linear extrusion
|
||||
|
@ -304,19 +304,19 @@ function region_faces(region, transform, reverse=false, vnf=EMPTY_VNF) =
|
|||
// that you can use `anchor`, `spin`, `orient` and attachments with it. Also, you can make more refined
|
||||
// twisted extrusions by using `maxseg` to subsample flat faces.
|
||||
// Arguments:
|
||||
// region = The 2D [Region](regions.scad) that is to be extruded.
|
||||
// height = The height to extrude the path. Default: 1
|
||||
// region = The 2D [Region](regions.scad) or path that is to be extruded.
|
||||
// height = The height to extrude the region. Default: 1
|
||||
// center = If true, the created polyhedron will be vertically centered. If false, it will be extruded upwards from the origin. Default: `false`
|
||||
// slices = The number of slices to divide the shape into along the Z axis, to allow refinement of detail, especially when working with a twist. Default: `twist/5`
|
||||
// maxseg = If given, then any long segments of the region will be subdivided to be shorter than this length. This can refine twisting flat faces a lot. Default: `undef` (no subsampling)
|
||||
// twist = The number of degrees to rotate the shape clockwise around the Z axis, as it rises from bottom to top. Default: 0
|
||||
// scale = The amound to scale the shape, from bottom to top. Default: 1
|
||||
// scale = The amount to scale the shape, from bottom to top. Default: 1
|
||||
// style = The style to use when triangulating the surface of the object. Valid values are `"default"`, `"alt"`, or `"quincunx"`.
|
||||
// convexity = Max number of surfaces any single ray could pass through.
|
||||
// convexity = Max number of surfaces any single ray could pass through. Module use only.
|
||||
// anchor_isect = If true, anchoring it performed by finding where the anchor vector intersects the swept shape. Default: false
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Module use only. Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Module use only. Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Module use only. Default: `UP`
|
||||
// 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`
|
||||
// Example: Extruding a Compound Region.
|
||||
// rgn1 = [for (d=[10:10:60]) circle(d=d,$fn=8)];
|
||||
// rgn2 = [square(30,center=false)];
|
||||
|
@ -355,9 +355,11 @@ module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg,
|
|||
}
|
||||
|
||||
|
||||
function linear_sweep(region, height=1, twist=0, scale=1, slices, maxseg, style="default") =
|
||||
function linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, style="default", anchor_isect=false, anchor, spin=0, orient=UP) =
|
||||
let(
|
||||
anchor = get_anchor(anchor,center,BOT,BOT),
|
||||
region = is_path(region)? [region] : region,
|
||||
cp = mean(pointlist_bounds(flatten(region))),
|
||||
regions = split_nested_region(region),
|
||||
slices = default(slices, floor(twist/5+1)),
|
||||
step = twist/slices,
|
||||
|
@ -374,27 +376,28 @@ function linear_sweep(region, height=1, twist=0, scale=1, slices, maxseg, style=
|
|||
)
|
||||
rot(twist, p=scale([scale,scale],p=path))
|
||||
]
|
||||
]
|
||||
) vnf_merge([
|
||||
for (rgn = regions)
|
||||
for (pathnum = idx(rgn)) let(
|
||||
p = cleanup_path(rgn[pathnum]),
|
||||
path = is_undef(maxseg)? p : [
|
||||
for (seg=pair_wrap(p)) each
|
||||
let(steps=ceil(norm(seg.y-seg.x)/maxseg))
|
||||
lerp(seg.x, seg.y, [0:1/steps:1-EPSILON])
|
||||
],
|
||||
verts = [
|
||||
for (i=[0:1:slices]) let(
|
||||
sc = lerp(1, scale, i/slices),
|
||||
ang = i * step,
|
||||
h = i * hstep - height/2
|
||||
) scale([sc,sc,1], p=rot(ang, p=path3d(path,h)))
|
||||
]
|
||||
) vnf_vertex_array(verts, caps=false, col_wrap=true, style=style),
|
||||
for (rgn = regions) region_faces(rgn, move([0,0,-height/2]), reverse=true),
|
||||
for (rgn = trgns) region_faces(rgn, move([0,0, height/2]), reverse=false)
|
||||
]);
|
||||
],
|
||||
vnf = vnf_merge([
|
||||
for (rgn = regions)
|
||||
for (pathnum = idx(rgn)) let(
|
||||
p = cleanup_path(rgn[pathnum]),
|
||||
path = is_undef(maxseg)? p : [
|
||||
for (seg=pair_wrap(p)) each
|
||||
let(steps=ceil(norm(seg.y-seg.x)/maxseg))
|
||||
lerp(seg.x, seg.y, [0:1/steps:1-EPSILON])
|
||||
],
|
||||
verts = [
|
||||
for (i=[0:1:slices]) let(
|
||||
sc = lerp(1, scale, i/slices),
|
||||
ang = i * step,
|
||||
h = i * hstep - height/2
|
||||
) scale([sc,sc,1], p=rot(ang, p=path3d(path,h)))
|
||||
]
|
||||
) vnf_vertex_array(verts, caps=false, col_wrap=true, style=style),
|
||||
for (rgn = regions) region_faces(rgn, move([0,0,-height/2]), reverse=true),
|
||||
for (rgn = trgns) region_faces(rgn, move([0,0, height/2]), reverse=false)
|
||||
])
|
||||
) reorient(anchor,spin,orient, cp=cp, vnf=vnf, extent=!anchor_isect, p=vnf);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -294,11 +294,11 @@ def mkdn_esc(txt):
|
|||
while txt:
|
||||
m = quotpat.match(txt)
|
||||
if m:
|
||||
out += m.group(1).replace(r'_', r'\_')
|
||||
out += m.group(1).replace(r'_', r'\_').replace(r'&',r'&').replace(r'<', r'<').replace(r'>',r'>')
|
||||
out += m.group(2)
|
||||
txt = m.group(3)
|
||||
else:
|
||||
out += txt.replace(r'_', r'\_')
|
||||
out += txt.replace(r'_', r'\_').replace(r'&',r'&').replace(r'<', r'<').replace(r'>',r'>')
|
||||
txt = ""
|
||||
return out
|
||||
|
||||
|
|
17
scripts/linecount.sh
Executable file
17
scripts/linecount.sh
Executable file
|
@ -0,0 +1,17 @@
|
|||
#!/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)
|
||||
|
||||
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
|
||||
|
33
shapes.scad
33
shapes.scad
|
@ -77,29 +77,30 @@ module cuboid(
|
|||
e = corner_edges(edges, corner);
|
||||
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)];
|
||||
$fn = is_finite(chamfer)? 4 : segs(r);
|
||||
translate(vmul(corner,size/2-[r,r,r])) {
|
||||
translate(vmul(corner,size/2-c)) {
|
||||
if (cnt == 0) {
|
||||
cube(r*2, center=true);
|
||||
cube(c*2, center=true);
|
||||
} else if (cnt == 1) {
|
||||
if (e.x) xcyl(l=r*2, r=r);
|
||||
if (e.y) ycyl(l=r*2, r=r);
|
||||
if (e.z) zcyl(l=r*2, r=r);
|
||||
if (e.x) xcyl(l=c.x*2, r=r);
|
||||
if (e.y) ycyl(l=c.y*2, r=r);
|
||||
if (e.z) zcyl(l=c.z*2, r=r);
|
||||
} else if (cnt == 2) {
|
||||
if (!e.x) {
|
||||
intersection() {
|
||||
ycyl(l=r*2, r=r);
|
||||
zcyl(l=r*2, r=r);
|
||||
ycyl(l=c.y*2, r=r);
|
||||
zcyl(l=c.z*2, r=r);
|
||||
}
|
||||
} else if (!e.y) {
|
||||
intersection() {
|
||||
xcyl(l=r*2, r=r);
|
||||
zcyl(l=r*2, r=r);
|
||||
xcyl(l=c.x*2, r=r);
|
||||
zcyl(l=c.z*2, r=r);
|
||||
}
|
||||
} else {
|
||||
intersection() {
|
||||
xcyl(l=r*2, r=r);
|
||||
ycyl(l=r*2, r=r);
|
||||
xcyl(l=c.x*2, r=r);
|
||||
ycyl(l=c.y*2, r=r);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -107,9 +108,9 @@ module cuboid(
|
|||
spheroid(r=r, style="octa");
|
||||
} else {
|
||||
intersection() {
|
||||
xcyl(l=r*2, r=r);
|
||||
ycyl(l=r*2, r=r);
|
||||
zcyl(l=r*2, r=r);
|
||||
xcyl(l=c.x*2, r=r);
|
||||
ycyl(l=c.y*2, r=r);
|
||||
zcyl(l=c.z*2, r=r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1181,14 +1182,14 @@ module spheroid(r, d, circum=false, style="aligned", anchor=CENTER, spin=0, orie
|
|||
if (style=="orig") {
|
||||
rotate_extrude(convexity=2,$fn=sides) {
|
||||
difference() {
|
||||
oval(r=r, circum=circum, $fn=sides);
|
||||
oval(r=r, circum=circum, realign=true, $fn=sides);
|
||||
left(r) square(2*r,center=true);
|
||||
}
|
||||
}
|
||||
} else if (style=="aligned") {
|
||||
rotate_extrude(convexity=2,$fn=sides) {
|
||||
difference() {
|
||||
zrot(180/sides) oval(r=r, circum=circum, $fn=sides);
|
||||
oval(r=r, circum=circum, $fn=sides);
|
||||
left(r) square(2*r,center=true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -269,18 +269,18 @@ module stroke(
|
|||
if (hull) {
|
||||
hull(){
|
||||
multmatrix(rotmats[i]) {
|
||||
sphere(d=widths[i]);
|
||||
sphere(d=widths[i],style="aligned");
|
||||
}
|
||||
multmatrix(rotmats[i-1]) {
|
||||
sphere(d=widths[i]);
|
||||
sphere(d=widths[i],style="aligned");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
multmatrix(rotmats[i]) {
|
||||
sphere(d=widths[i]);
|
||||
sphere(d=widths[i],style="aligned");
|
||||
}
|
||||
multmatrix(rotmats[i-1]) {
|
||||
sphere(d=widths[i]);
|
||||
sphere(d=widths[i],style="aligned");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
26
skin.scad
26
skin.scad
|
@ -298,6 +298,32 @@ include <vnf.scad>
|
|||
// for (ang = [0:5:360])
|
||||
// rot([0,ang,0], cp=[100,0,0], p=rot(ang/2, p=path3d(square([1,30],center=true))))
|
||||
// ], caps=false, slices=0, refine=20);
|
||||
// Example: This model of two scutoids packed together is based on https://www.thingiverse.com/thing:3024272 by mathgrrl
|
||||
// sidelen = 10; // Side length of scutoid
|
||||
// height = 25; // Height of scutoid
|
||||
// angle = -15; // Angle (twists the entire form)
|
||||
// push = -5; // Push (translates the base away from the top)
|
||||
// flare = 1; // Flare (the two pieces will be different unless this is 1)
|
||||
// midpoint = .5; // Height of the extra vertex (as a fraction of total height); the two pieces will be different unless this is .5)
|
||||
// pushvec = rot(angle/2,p=push*RIGHT); // Push direction is the the average of the top and bottom mating edges
|
||||
// pent = path3d(apply(move(pushvec)*rot(angle),pentagon(side=sidelen,align_side=RIGHT,anchor="side0")));
|
||||
// hex = path3d(hexagon(side=flare*sidelen, align_side=RIGHT, anchor="side0"),height);
|
||||
// pentmate = path3d(pentagon(side=flare*sidelen,align_side=LEFT,anchor="side0"),height);
|
||||
// // Native index would require mapping first and last vertices together, which is not allowed, so shift
|
||||
// hexmate = polygon_shift(
|
||||
// path3d(apply(move(pushvec)*rot(angle),hexagon(side=sidelen,align_side=LEFT,anchor="side0"))),
|
||||
// -1);
|
||||
// join_vertex = lerp(
|
||||
// mean(select(hex,1,2)), // midpoint of "extra" hex edge
|
||||
// mean(select(hexmate,0,1)), // midpoint of "extra" hexmate edge
|
||||
// midpoint);
|
||||
// augpent = repeat_entries(pent, [1,2,1,1,1]); // Vertex 1 will split at the top forming a triangular face with the hexagon
|
||||
// augpent_mate = repeat_entries(pentmate,[2,1,1,1,1]); // For mating pentagon it is vertex 0 that splits
|
||||
// // Middle is the interpolation between top and bottom except for the join vertex, which is doubled because it splits
|
||||
// middle = list_set(lerp(augpent,hex,midpoint),[1,2],[join_vertex,join_vertex]);
|
||||
// middle_mate = list_set(lerp(hexmate,augpent_mate,midpoint), [0,1], [join_vertex,join_vertex]);
|
||||
// skin([augpent,middle,hex], slices=10, refine=10, sampling="segment");
|
||||
// color("green")skin([augpent_mate,middle_mate,hexmate], slices=10,refine=10, sampling="segment");
|
||||
// Example: If you create a self-intersecting polyhedron the result is invalid. In some cases self-intersection may be obvous. Here is a more subtle example.
|
||||
// skin([
|
||||
// for (a = [0:30:180]) let(
|
||||
|
|
|
@ -252,5 +252,34 @@ module test_apply_list() {
|
|||
test_apply_list();
|
||||
|
||||
|
||||
module test_rot_decode() {
|
||||
Tlist = [
|
||||
rot(37),
|
||||
xrot(49),
|
||||
yrot(88),
|
||||
rot(37,v=[1,3,3]),
|
||||
rot(41,v=[2,-3,4]),
|
||||
rot(180),
|
||||
xrot(180),
|
||||
yrot(180),
|
||||
rot(180, v=[3,2,-5], cp=[3,5,18]),
|
||||
rot(0.1, v=[1,2,3]),
|
||||
rot(-47,v=[3,4,5],cp=[9,3,4]),
|
||||
rot(197,v=[13,4,5],cp=[9,-3,4]),
|
||||
move([3,4,5]),
|
||||
move([3,4,5]) * rot(a=56, v=[5,3,-3], cp=[2,3,4]),
|
||||
ident(4)
|
||||
];
|
||||
errlist = [for(T = Tlist)
|
||||
let(
|
||||
parm = rot_decode(T),
|
||||
restore = move(parm[3])*rot(a=parm[0],v=parm[1],cp=parm[2])
|
||||
)
|
||||
norm_fro(restore-T)];
|
||||
assert(max(errlist)<1e-13);
|
||||
}
|
||||
test_rot_decode();
|
||||
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
@ -550,6 +550,13 @@ module test_determinant() {
|
|||
test_determinant();
|
||||
|
||||
|
||||
module test_matrix_trace() {
|
||||
M = [ [6,4,-2,9], [1,-2,8,3], [1,5,7,6], [4,2,5,1] ];
|
||||
assert_equal(matrix_trace(M), 6-2+7+1);
|
||||
}
|
||||
test_matrix_trace();
|
||||
|
||||
|
||||
// Logic
|
||||
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ test_cylinder();
|
|||
|
||||
module test_sphere() {
|
||||
$fn=6;
|
||||
assert_approx(sphere(r=40), [[[0,0,40],[34.6410161514,0,20],[17.3205080757,30,20],[-17.3205080757,30,20],[-34.6410161514,0,20],[-17.3205080757,-30,20],[17.3205080757,-30,20],[34.6410161514,0,-20],[17.3205080757,30,-20],[-17.3205080757,30,-20],[-34.6410161514,0,-20],[-17.3205080757,-30,-20],[17.3205080757,-30,-20],[0,0,-40]],[[1,0,2],[13,7,8],[2,0,3],[13,8,9],[3,0,4],[13,9,10],[4,0,5],[13,10,11],[5,0,6],[13,11,12],[6,0,1],[13,12,7],[1,2,8],[1,8,7],[2,3,9],[2,9,8],[3,4,10],[3,10,9],[4,5,11],[4,11,10],[5,6,12],[5,12,11],[6,1,7],[6,7,12]]]);
|
||||
assert_approx(sphere(r=40), [[[20,0,34.6410161514],[10,17.3205080757,34.6410161514],[-10,17.3205080757,34.6410161514],[-20,0,34.6410161514],[-10,-17.3205080757,34.6410161514],[10,-17.3205080757,34.6410161514],[40,0,0],[20,34.6410161514,0],[-20,34.6410161514,0],[-40,0,0],[-20,-34.6410161514,0],[20,-34.6410161514,0],[20,0,-34.6410161514],[10,17.3205080757,-34.6410161514],[-10,17.3205080757,-34.6410161514],[-20,0,-34.6410161514],[-10,-17.3205080757,-34.6410161514],[10,-17.3205080757,-34.6410161514]],[[5,4,3,2,1,0],[12,13,14,15,16,17],[6,0,1],[6,1,7],[7,1,2],[7,2,8],[8,2,3],[8,3,9],[9,3,4],[9,4,10],[10,4,5],[10,5,11],[11,5,0],[11,0,6],[12,6,7],[12,7,13],[13,7,8],[13,8,14],[14,8,9],[14,9,15],[15,9,10],[15,10,16],[16,10,11],[16,11,17],[17,11,6],[17,6,12]]]);
|
||||
assert_approx(sphere(r=40,style="orig"), [[[20,0,34.6410161514],[10,17.3205080757,34.6410161514],[-10,17.3205080757,34.6410161514],[-20,0,34.6410161514],[-10,-17.3205080757,34.6410161514],[10,-17.3205080757,34.6410161514],[40,0,0],[20,34.6410161514,0],[-20,34.6410161514,0],[-40,0,0],[-20,-34.6410161514,0],[20,-34.6410161514,0],[20,0,-34.6410161514],[10,17.3205080757,-34.6410161514],[-10,17.3205080757,-34.6410161514],[-20,0,-34.6410161514],[-10,-17.3205080757,-34.6410161514],[10,-17.3205080757,-34.6410161514]],[[5,4,3,2,1,0],[12,13,14,15,16,17],[6,0,1],[6,1,7],[7,1,2],[7,2,8],[8,2,3],[8,3,9],[9,3,4],[9,4,10],[10,4,5],[10,5,11],[11,5,0],[11,0,6],[12,6,7],[12,7,13],[13,7,8],[13,8,14],[14,8,9],[14,9,15],[15,9,10],[15,10,16],[16,10,11],[16,11,17],[17,11,6],[17,6,12]]]);
|
||||
assert_approx(sphere(r=40,style="aligned"), [[[0,0,40],[34.6410161514,0,20],[17.3205080757,30,20],[-17.3205080757,30,20],[-34.6410161514,0,20],[-17.3205080757,-30,20],[17.3205080757,-30,20],[34.6410161514,0,-20],[17.3205080757,30,-20],[-17.3205080757,30,-20],[-34.6410161514,0,-20],[-17.3205080757,-30,-20],[17.3205080757,-30,-20],[0,0,-40]],[[1,0,2],[13,7,8],[2,0,3],[13,8,9],[3,0,4],[13,9,10],[4,0,5],[13,10,11],[5,0,6],[13,11,12],[6,0,1],[13,12,7],[1,2,8],[1,8,7],[2,3,9],[2,9,8],[3,4,10],[3,10,9],[4,5,11],[4,11,10],[5,6,12],[5,12,11],[6,1,7],[6,7,12]]]);
|
||||
assert_approx(sphere(r=40,style="stagger"), [[[0,0,40],[30,17.3205080757,20],[0,34.6410161514,20],[-30,17.3205080757,20],[-30,-17.3205080757,20],[0,-34.6410161514,20],[30,-17.3205080757,20],[34.6410161514,0,-20],[17.3205080757,30,-20],[-17.3205080757,30,-20],[-34.6410161514,0,-20],[-17.3205080757,-30,-20],[17.3205080757,-30,-20],[0,0,-40]],[[1,0,2],[13,7,8],[2,0,3],[13,8,9],[3,0,4],[13,9,10],[4,0,5],[13,10,11],[5,0,6],[13,11,12],[6,0,1],[13,12,7],[1,2,8],[1,8,7],[2,3,9],[2,9,8],[3,4,10],[3,10,9],[4,5,11],[4,11,10],[5,6,12],[5,12,11],[6,1,7],[6,7,12]]]);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BOSL_VERSION = [2,0,446];
|
||||
BOSL_VERSION = [2,0,459];
|
||||
|
||||
|
||||
// Section: BOSL Library Version Functions
|
||||
|
|
Loading…
Reference in a new issue