Merge pull request #2 from revarbat/master

My repository update
This commit is contained in:
RonaldoCMP 2020-08-30 11:51:21 +01:00 committed by GitHub
commit 14b0f264ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 193 additions and 44 deletions

View file

@ -1286,10 +1286,12 @@ function array_dim(v, depth=undef) =
// Function: transpose() // Function: transpose()
// Description: Returns the transposition of the given array. // Usage:
// When reverse=true, the transposition is done in respect to the secondary diagonal, that is: // transpose(arr, [reverse])
// . // Description:
// reverse(transpose(reverse(arr))) == transpose(arr, reverse=true) // Returns the transpose of the given input array. The input should be a list of lists that are
// all the same length. If you give a vector then transpose returns it unchanged.
// When reverse=true, the transpose is done across to the secondary diagonal. (See example below.)
// By default, reverse=false. // By default, reverse=false.
// Example: // Example:
// arr = [ // arr = [
@ -1329,19 +1331,19 @@ function array_dim(v, depth=undef) =
// // ["h", "e", "b"], // // ["h", "e", "b"],
// // ["g", "d", "a"] // // ["g", "d", "a"]
// // ] // // ]
// Example: // Example: Transpose on a list of numbers returns the list unchanged
// transpose([3,4,5]); // Returns: [3,4,5] // transpose([3,4,5]); // Returns: [3,4,5]
function transpose(arr, reverse=false) = function transpose(arr, reverse=false) =
assert( is_list(arr) && len(arr)>0, "The array is not a vector neither a matrix." ) assert( is_list(arr) && len(arr)>0, "Input to transpose must be a nonempty list.")
is_list(arr[0]) is_list(arr[0])
? let( l0 = len(arr[0]) ) ? let( len0 = len(arr[0]) )
assert([for(a=arr) if(!is_list(a) || len(a)!=l0) 1 ]==[], "The array is not a vector neither a matrix." ) assert([for(a=arr) if(!is_list(a) || len(a)!=len0) 1 ]==[], "Input to transpose has inconsistent row lengths." )
reverse reverse
? [for (i=[0:1:l0-1]) ? [for (i=[0:1:len0-1])
[ for (j=[0:1:len(arr)-1]) arr[len(arr)-1-j][l0-1-i] ] ] [ for (j=[0:1:len(arr)-1]) arr[len(arr)-1-j][len0-1-i] ] ]
: [for (i=[0:1:l0-1]) : [for (i=[0:1:len0-1])
[ for (j=[0:1:len(arr)-1]) arr[j][i] ] ] [ for (j=[0:1:len(arr)-1]) arr[j][i] ] ]
: assert( is_vector(arr), "The array is not a vector neither a matrix." ) : assert( is_vector(arr), "Input to transpose must be a vector or list of lists.")
arr; arr;

View file

@ -461,13 +461,16 @@ function find_anchor(anchor, geom) =
rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), points), rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), points),
hits = [ hits = [
for (face = faces) let( for (face = faces) let(
verts = select(rpts, face) verts = select(rpts, face),
xs = subindex(verts,0),
ys = subindex(verts,1),
zs = subindex(verts,2)
) if ( ) if (
max(subindex(verts,0)) >= -eps && max(xs) >= -eps &&
max(subindex(verts,1)) >= -eps && max(ys) >= -eps &&
max(subindex(verts,2)) >= -eps && max(zs) >= -eps &&
min(subindex(verts,1)) <= eps && min(ys) <= eps &&
min(subindex(verts,2)) <= eps min(zs) <= eps
) let( ) let(
poly = select(points, face), poly = select(points, face),
pt = polygon_line_intersection(poly, [cp,cp+anchor], bounded=[true,false], eps=eps) pt = polygon_line_intersection(poly, [cp,cp+anchor], bounded=[true,false], eps=eps)

View file

@ -353,6 +353,7 @@ function segs(r) =
// Arguments: // Arguments:
// $children = number of children the module has. // $children = number of children the module has.
module no_children(count) { module no_children(count) {
assert($children==0, "Module no_children() does not support child modules");
assert(count==0, str("Module ",parent_module(1),"() does not support child modules")); assert(count==0, str("Module ",parent_module(1),"() does not support child modules"));
} }
@ -377,6 +378,7 @@ function _valstr(x) =
// expected = The value that was expected. // expected = The value that was expected.
// info = Extra info to print out to make the error clearer. // info = Extra info to print out to make the error clearer.
module assert_approx(got, expected, info) { module assert_approx(got, expected, info) {
no_children($children);
if (!approx(got, expected)) { if (!approx(got, expected)) {
echo(); echo();
echo(str("EXPECT: ", _valstr(expected))); echo(str("EXPECT: ", _valstr(expected)));
@ -404,6 +406,7 @@ module assert_approx(got, expected, info) {
// expected = The value that was expected. // expected = The value that was expected.
// info = Extra info to print out to make the error clearer. // info = Extra info to print out to make the error clearer.
module assert_equal(got, expected, info) { module assert_equal(got, expected, info) {
no_children($children);
if (got != expected || (is_nan(got) && is_nan(expected))) { if (got != expected || (is_nan(got) && is_nan(expected))) {
echo(); echo();
echo(str("EXPECT: ", _valstr(expected))); echo(str("EXPECT: ", _valstr(expected)));

View file

@ -1007,11 +1007,20 @@ function closest_point_on_plane(plane, point) =
// Returns [LINE, undef] if the line is on the plane. // Returns [LINE, undef] if the line is on the plane.
// Returns undef if line is parallel to, but not on the given plane. // Returns undef if line is parallel to, but not on the given plane.
function _general_plane_line_intersection(plane, line, eps=EPSILON) = function _general_plane_line_intersection(plane, line, eps=EPSILON) =
let( a = plane*[each line[0],-1], let(
b = plane*[each(line[1]-line[0]),-1] ) l0 = line[0], // Ray start point
approx(b,0,eps) u = line[1] - l0, // Ray direction vector
? points_on_plane(line[0],plane,eps)? [line,undef]: undef n = plane_normal(plane),
: [ line[0]+a/b*(line[1]-line[0]), a/b ]; p0 = n * plane[3], // A point on the plane
w = l0 - p0 // Vector from plane point to ray start
) approx(n*u, 0, eps=eps) ? (
// Line is parallel to plane.
approx(n*w, 0, eps=eps)
? [line, undef] // Line is on the plane.
: undef // Line never intersects the plane.
) : let(
t = (-n * w) / (n * u) // Distance ratio along ray
) [ l0 + u*t, t ];
// Function: plane_line_angle() // Function: plane_line_angle()
@ -1098,8 +1107,8 @@ function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) =
linevec = unit(line[1] - line[0]), linevec = unit(line[1] - line[0]),
lp1 = line[0] + (bounded[0]? 0 : -1000000) * linevec, lp1 = line[0] + (bounded[0]? 0 : -1000000) * linevec,
lp2 = line[1] + (bounded[1]? 0 : 1000000) * linevec, lp2 = line[1] + (bounded[1]? 0 : 1000000) * linevec,
poly2d = clockwise_polygon(project_plane(poly, p1, p2, p3)), poly2d = clockwise_polygon(project_plane(poly, plane)),
line2d = project_plane([lp1,lp2], p1, p2, p3), line2d = project_plane([lp1,lp2], plane),
parts = split_path_at_region_crossings(line2d, [poly2d], closed=false), parts = split_path_at_region_crossings(line2d, [poly2d], closed=false),
inside = [for (part = parts) inside = [for (part = parts)
if (point_in_polygon(mean(part), poly2d)>0) part if (point_in_polygon(mean(part), poly2d)>0) part
@ -1107,7 +1116,7 @@ function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) =
) )
!inside? undef : !inside? undef :
let( let(
isegs = [for (seg = inside) lift_plane(seg, p1, p2, p3) ] isegs = [for (seg = inside) lift_plane(seg, plane) ]
) )
isegs isegs
) )
@ -1264,7 +1273,6 @@ function find_circle_2tangents(pt1, pt2, pt3, r, d, tangents=false) =
x = hyp * cos(a/2), x = hyp * cos(a/2),
tp1 = pt2 + x * v1, tp1 = pt2 + x * v1,
tp2 = pt2 + x * v2, tp2 = pt2 + x * v2,
// fff=echo(tp1=tp1,cp=cp,pt2=pt2),
dang1 = vector_angle(tp1-cp,pt2-cp), dang1 = vector_angle(tp1-cp,pt2-cp),
dang2 = vector_angle(tp2-cp,pt2-cp) dang2 = vector_angle(tp2-cp,pt2-cp)
) )

View file

@ -652,7 +652,7 @@ function regular_polyhedron_info(
let( let(
entry = ( entry = (
name == "trapezohedron"? ( name == "trapezohedron"? (
trapezohedron(faces=faces, side=side, longside=longside, h=h, r=r) _trapezohedron(faces=faces, side=side, longside=longside, h=h, r=r)
) : ( ) : (
_polyhedra_[!is_undef(index)? _polyhedra_[!is_undef(index)?
indexlist[index] : indexlist[index] :
@ -671,7 +671,7 @@ function regular_polyhedron_info(
) / entry[edgelen] ) / entry[edgelen]
), ),
face_triangles = hull(entry[vertices]), face_triangles = hull(entry[vertices]),
faces_normals_vertices = stellate_faces( faces_normals_vertices = _stellate_faces(
entry[edgelen], stellate, entry[vertices], entry[edgelen], stellate, entry[vertices],
entry[facevertices]==[3]? entry[facevertices]==[3]?
[face_triangles, [for(face=face_triangles) _facenormal(entry[vertices],face)]] : [face_triangles, [for(face=face_triangles) _facenormal(entry[vertices],face)]] :
@ -713,11 +713,7 @@ function regular_polyhedron_info(
assert(false, str("Unknown info type '",info,"' requested")); assert(false, str("Unknown info type '",info,"' requested"));
function _stellate_faces(scalefactor,stellate,vertices,faces_normals) =
/// hull solution fails due to roundoff
/// either cross product or just rotate to
///
function stellate_faces(scalefactor,stellate,vertices,faces_normals) =
(stellate == false || stellate == 0)? concat(faces_normals,[vertices]) : (stellate == false || stellate == 0)? concat(faces_normals,[vertices]) :
let( let(
faces = [for(face=faces_normals[0]) select(face,hull(select(vertices,face)))], faces = [for(face=faces_normals[0]) select(face,hull(select(vertices,face)))],
@ -730,8 +726,8 @@ function stellate_faces(scalefactor,stellate,vertices,faces_normals) =
) [newfaces, normals, allpts]; ) [newfaces, normals, allpts];
function trapezohedron(faces, r, side, longside, h, d) = function _trapezohedron(faces, r, side, longside, h, d) =
assert(faces%2==0, "Number of faces must be even") assert(faces%2==0, "Must set 'faces' to an even number for trapezohedron")
let( let(
r = get_radius(r=r, d=d, dflt=1), r = get_radius(r=r, d=d, dflt=1),
N = faces/2, N = faces/2,

View file

@ -488,6 +488,7 @@ function smooth_path(path, tangents, size, relsize, splinesteps=10, uniform=fals
// - smooth: os_smooth(cut|joint). Define continuous curvature rounding, with `cut` and `joint` as for round_corners. // - smooth: os_smooth(cut|joint). Define continuous curvature rounding, with `cut` and `joint` as for round_corners.
// - teardrop: os_teardrop(r|cut). Rounding using a 1/8 circle that then changes to a 45 degree chamfer. The chamfer is at the end, and enables the object to be 3d printed without support. The radius gives the radius of the circular part. // - teardrop: os_teardrop(r|cut). Rounding using a 1/8 circle that then changes to a 45 degree chamfer. The chamfer is at the end, and enables the object to be 3d printed without support. The radius gives the radius of the circular part.
// - chamfer: os_chamfer([height], [width], [cut], [angle]). Chamfer the edge at desired angle or with desired height and width. You can specify height and width together and the angle will be ignored, or specify just one of height and width and the angle is used to determine the shape. Alternatively, specify "cut" along with angle to specify the cut back distance of the chamfer. // - chamfer: os_chamfer([height], [width], [cut], [angle]). Chamfer the edge at desired angle or with desired height and width. You can specify height and width together and the angle will be ignored, or specify just one of height and width and the angle is used to determine the shape. Alternatively, specify "cut" along with angle to specify the cut back distance of the chamfer.
// - mask: os_mask(mask, [out]). Create a profile from one of the [2d masking shapes](shapes2d.scad#5-2d-masking-shapes). The `out` parameter specifies that the mask should flare outward (like crown molding or baseboard). This is set false by default.
// . // .
// The general settings that you can use with all of the helper functions are mostly used to control how offset_sweep() calls the offset() function. // The general settings that you can use with all of the helper functions are mostly used to control how offset_sweep() calls the offset() function.
// - extra: Add an extra vertical step of the specified height, to be used for intersections or differences. This extra step will extend the resulting object beyond the height you specify. Default: 0 // - extra: Add an extra vertical step of the specified height, to be used for intersections or differences. This extra step will extend the resulting object beyond the height you specify. Default: 0
@ -654,6 +655,15 @@ function smooth_path(path, tangents, size, relsize, splinesteps=10, uniform=fals
// up(1) // up(1)
// offset_sweep(offset(rhex,r=-1), height=9.5, bottom=os_circle(r=2), top=os_teardrop(r=-4)); // offset_sweep(offset(rhex,r=-1), height=9.5, bottom=os_circle(r=2), top=os_teardrop(r=-4));
// } // }
// Example: Using os_mask to create ogee profiles:
// ogee = mask2d_ogee([
// "xstep",1, "ystep",1, // Starting shoulder.
// "fillet",5, "round",5, // S-curve.
// "ystep",1, // Ending shoulder.
// ]);
// star = star(5, r=220, ir=130);
// rounded_star = round_corners(star, cut=flatten(repeat([5,0],5)), $fn=24);
// offset_sweep(rounded_star, height=100, top=os_mask(ogee), bottom=os_mask(ogee,out=true));
// This function does the actual work of repeatedly calling offset() and concatenating the resulting face and vertex lists to produce // This function does the actual work of repeatedly calling offset() and concatenating the resulting face and vertex lists to produce
@ -880,6 +890,18 @@ function os_profile(points, extra,check_valid, quality, offset_maxstep, offset)
]); ]);
function os_mask(mask, out=false, extra,check_valid, quality, offset_maxstep, offset) =
let(
origin_index = [for(i=idx(mask)) if (mask[i].x<0 && mask[i].y<0) i],
xfactor = out ? -1 : 1
)
assert(len(origin_index)==1,"Cannot find origin in the mask")
let(
points = ([for(pt=polygon_shift(mask,origin_index[0])) [xfactor*max(pt.x,0),-max(pt.y,0)]])
)
os_profile(deduplicate(move(-points[1],p=select(points,1,-1))), extra,check_valid,quality,offset_maxstep,offset);
// Module: convex_offset_extrude() // Module: convex_offset_extrude()
// //
// Description: // Description:
@ -1994,4 +2016,4 @@ module bent_cutout_mask(r, thickness, path, convexity=10)
} }
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

91
scripts/gencheat.sh Executable file
View file

@ -0,0 +1,91 @@
#!/bin/bash
function ucase
{
echo "$1" | tr '[:lower:]' '[:upper:]'
}
function lcase
{
echo "$1" | tr '[:upper:]' '[:lower:]'
}
function columnize
{
cols=4
TMPFILE=$(mktemp -t $(basename $0).XXXXXX) || exit 1
cat >>$TMPFILE
totcnt=$(wc -l $TMPFILE | awk '{print $1}')
maxrows=$((($totcnt+$cols-1)/$cols))
maxcols=$cols
if [[ $maxcols -gt $totcnt ]] ; then
maxcols=$totcnt
fi
cnt=0
hdrln1="| $(ucase $1) "
hdrln2='|:-----'
n=1
while [[ $n < $maxcols ]] ; do
hdrln1+=' | &nbsp;'
hdrln2+=' |:------'
n=$(($n+1))
done
hdrln1+=' |'
hdrln2+=' |'
n=0
while [[ $n < $maxrows ]] ; do
lines[$n]=""
n=$(($n+1))
done
col=0
while IFS= read -r line; do
if [[ $col != 0 ]] ; then
lines[$cnt]+=" | "
fi
lines[$cnt]+="$line"
cnt=$(($cnt+1))
if [[ $cnt = $maxrows ]] ; then
cnt=0
col=$(($col+1))
fi
done <$TMPFILE
rm -f $TMPFILE
echo
echo $hdrln1
echo $hdrln2
n=0
while [[ $n < $maxrows ]] ; do
echo "| ${lines[$n]} |"
n=$(($n+1))
done
}
function mkconstindex
{
sed 's/([^)]*)//g' | sed 's/[^a-zA-Z0-9_.:$]//g' | awk -F ':' '{printf "[%s](%s#%s)\n", $3, $1, $3}'
}
function mkotherindex
{
sed 's/([^)]*)//g' | sed 's/[^a-zA-Z0-9_.:$]//g' | awk -F ':' '{printf "[%s()](%s#%s)\n", $3, $1, $3}'
}
CHEAT_FILES=$(grep '^include' std.scad | sed 's/^.*<\([a-zA-Z0-9.]*\)>/\1/'|grep -v 'version.scad')
(
echo '## Belfry OpenScad Library Cheat Sheet'
echo
echo '( [Alphabetic Index](Index) )'
echo
for f in $CHEAT_FILES ; do
#echo "### $f"
(
egrep -H 'Constant: ' $f | mkconstindex
egrep -H 'Function: |Function&Module: |Module: ' $f | mkotherindex
) | columnize $f
echo
done
) > BOSL2.wiki/CheatSheet.md

View file

@ -701,7 +701,9 @@ function str_format(fmt, vals, use_nbsp=false) =
// echofmt("{:-10s}{:.3f}", ["plecostamus",27.43982]); // ECHO: "plecostamus27.440" // echofmt("{:-10s}{:.3f}", ["plecostamus",27.43982]); // ECHO: "plecostamus27.440"
// echofmt("{:-10.9s}{:.3f}", ["plecostamus",27.43982]); // ECHO: "plecostam 27.440" // echofmt("{:-10.9s}{:.3f}", ["plecostamus",27.43982]); // ECHO: "plecostam 27.440"
function echofmt(fmt, vals, use_nbsp=false) = echo(str_format(fmt,vals,use_nbsp)); function echofmt(fmt, vals, use_nbsp=false) = echo(str_format(fmt,vals,use_nbsp));
module echofmt(fmt, vals, use_nbsp=false) echo(str_format(fmt,vals,use_nbsp)); module echofmt(fmt, vals, use_nbsp=false) {
no_children($children);
echo(str_format(fmt,vals,use_nbsp));
}
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -101,6 +101,7 @@ function struct_echo(struct,name="") =
undef; undef;
module struct_echo(struct,name="") { module struct_echo(struct,name="") {
no_children($children);
dummy = struct_echo(struct,name); dummy = struct_echo(struct,name);
} }

View file

@ -53,7 +53,7 @@ test_tri_functions();
//test__general_plane_line_intersection(); //test__general_plane_line_intersection();
//test_plane_line_angle(); //test_plane_line_angle();
//test_plane_line_intersection(); //test_plane_line_intersection();
//test_polygon_line_intersection(); test_polygon_line_intersection();
//test_plane_intersection(); //test_plane_intersection();
test_coplanar(); test_coplanar();
test_points_on_plane(); test_points_on_plane();
@ -542,6 +542,17 @@ module test_distance_from_plane() {
*test_distance_from_plane(); *test_distance_from_plane();
module test_polygon_line_intersection() {
poly1 = [[50,50,50], [50,-50,50], [-50,-50,50]];
assert_approx(polygon_line_intersection(poly1, [CENTER, UP]), [0,0,50]);
assert_approx(polygon_line_intersection(poly1, [CENTER, UP+RIGHT]), [50,0,50]);
assert_approx(polygon_line_intersection(poly1, [CENTER, UP+BACK+RIGHT]), [50,50,50]);
assert_approx(polygon_line_intersection(poly1, [[0,0,50], [1,0,50]]), [[[0,0,50], [50,0,50]]]);
assert_approx(polygon_line_intersection(poly1, [[0,0,0], [1,0,0]]), undef);
}
*test_polygon_line_intersection();
module test_coplanar() { module test_coplanar() {
assert(coplanar([ [5,5,1],[0,0,1],[-1,-1,1] ]) == false); assert(coplanar([ [5,5,1],[0,0,1],[-1,-1,1] ]) == false);
assert(coplanar([ [5,5,1],[0,0,0],[-1,-1,1] ]) == true); assert(coplanar([ [5,5,1],[0,0,0],[-1,-1,1] ]) == true);

View file

@ -232,7 +232,7 @@ module test_rot() {
assert_equal(rot(a,p=pts2d), pts2d, info=str("rot(",a,",p=...), 2D")); assert_equal(rot(a,p=pts2d), pts2d, info=str("rot(",a,",p=...), 2D"));
assert_equal(rot(a,p=pts3d), pts3d, info=str("rot(",a,",p=...), 3D")); assert_equal(rot(a,p=pts3d), pts3d, info=str("rot(",a,",p=...), 3D"));
} }
assert_equal(rot(90), [[0,-1,0,0],[1,0,0,0],[0,0,1,0],[0,0,0,1]]) assert_equal(rot(90), [[0,-1,0,0],[1,0,0,0],[0,0,1,0],[0,0,0,1]]);
for (a=angs) { for (a=angs) {
assert_equal(rot(a), affine3d_zrot(a), info=str("Z angle (only) = ",a)); assert_equal(rot(a), affine3d_zrot(a), info=str("Z angle (only) = ",a));
assert_equal(rot([a,0,0]), affine3d_xrot(a), info=str("X angle = ",a)); assert_equal(rot([a,0,0]), affine3d_xrot(a), info=str("X angle = ",a));

View file

@ -8,7 +8,7 @@
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
BOSL_VERSION = [2,0,410]; BOSL_VERSION = [2,0,415];
// Section: BOSL Library Version Functions // Section: BOSL Library Version Functions
@ -49,6 +49,7 @@ function bosl_version_str() = version_to_str(BOSL_VERSION);
// Description: // Description:
// Given a version as a list, number, or string, asserts that the currently installed BOSL library is at least the given version. // Given a version as a list, number, or string, asserts that the currently installed BOSL library is at least the given version.
module bosl_required(target) { module bosl_required(target) {
no_children($children);
assert( assert(
version_cmp(bosl_version(), target) >= 0, version_cmp(bosl_version(), target) >= 0,
str( str(

View file

@ -341,9 +341,18 @@ function vnf_vertex_array(
// Arguments: // Arguments:
// vnf = A VNF structure, or list of VNF structures. // vnf = A VNF structure, or list of VNF structures.
// convexity = Max number of times a line could intersect a wall of the shape. // convexity = Max number of times a line could intersect a wall of the shape.
module vnf_polyhedron(vnf, convexity=2) { // extent = If true, calculate anchors by extents, rather than intersection. Default: true.
// cp = Centerpoint of VNF to use for anchoring when `extent` is false. Default: `[0, 0, 0]`
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"origin"`
// 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`
module vnf_polyhedron(vnf, convexity=2, extent=true, cp=[0,0,0], anchor="origin", spin=0, orient=UP) {
vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf; vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf;
polyhedron(vnf[0], vnf[1], convexity=convexity); cp = is_def(cp) ? cp : vnf_centroid(vnf);
attachable(anchor,spin,orient, vnf=vnf, extent=extent, cp=cp) {
polyhedron(vnf[0], vnf[1], convexity=convexity);
children();
}
} }