diff --git a/attachments.scad b/attachments.scad index 48ed522..ae93412 100644 --- a/attachments.scad +++ b/attachments.scad @@ -461,13 +461,16 @@ function find_anchor(anchor, geom) = rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), points), hits = [ 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 ( - max(subindex(verts,0)) >= -eps && - max(subindex(verts,1)) >= -eps && - max(subindex(verts,2)) >= -eps && - min(subindex(verts,1)) <= eps && - min(subindex(verts,2)) <= eps + max(xs) >= -eps && + max(ys) >= -eps && + max(zs) >= -eps && + min(ys) <= eps && + min(zs) <= eps ) let( poly = select(points, face), pt = polygon_line_intersection(poly, [cp,cp+anchor], bounded=[true,false], eps=eps) diff --git a/geometry.scad b/geometry.scad index dc86187..0ab9f73 100644 --- a/geometry.scad +++ b/geometry.scad @@ -1007,11 +1007,20 @@ function closest_point_on_plane(plane, point) = // Returns [LINE, undef] if the line is on the plane. // Returns undef if line is parallel to, but not on the given plane. function _general_plane_line_intersection(plane, line, eps=EPSILON) = - let( a = plane*[each line[0],-1], - b = plane*[each(line[1]-line[0]),-1] ) - approx(b,0,eps) - ? points_on_plane(line[0],plane,eps)? [line,undef]: undef - : [ line[0]+a/b*(line[1]-line[0]), a/b ]; + let( + l0 = line[0], // Ray start point + u = line[1] - l0, // Ray direction vector + n = plane_normal(plane), + 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() @@ -1098,8 +1107,8 @@ function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) = linevec = unit(line[1] - line[0]), lp1 = line[0] + (bounded[0]? 0 : -1000000) * linevec, lp2 = line[1] + (bounded[1]? 0 : 1000000) * linevec, - poly2d = clockwise_polygon(project_plane(poly, p1, p2, p3)), - line2d = project_plane([lp1,lp2], p1, p2, p3), + poly2d = clockwise_polygon(project_plane(poly, plane)), + line2d = project_plane([lp1,lp2], plane), parts = split_path_at_region_crossings(line2d, [poly2d], closed=false), inside = [for (part = parts) 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 : let( - isegs = [for (seg = inside) lift_plane(seg, p1, p2, p3) ] + isegs = [for (seg = inside) lift_plane(seg, plane) ] ) isegs ) @@ -1264,7 +1273,6 @@ function find_circle_2tangents(pt1, pt2, pt3, r, d, tangents=false) = x = hyp * cos(a/2), tp1 = pt2 + x * v1, tp2 = pt2 + x * v2, -// fff=echo(tp1=tp1,cp=cp,pt2=pt2), dang1 = vector_angle(tp1-cp,pt2-cp), dang2 = vector_angle(tp2-cp,pt2-cp) ) diff --git a/scripts/gencheat.sh b/scripts/gencheat.sh new file mode 100755 index 0000000..6762f0a --- /dev/null +++ b/scripts/gencheat.sh @@ -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+=' |  ' + 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 + diff --git a/tests/test_geometry.scad b/tests/test_geometry.scad index 0950e1c..e69a8ac 100644 --- a/tests/test_geometry.scad +++ b/tests/test_geometry.scad @@ -53,7 +53,7 @@ test_tri_functions(); //test__general_plane_line_intersection(); //test_plane_line_angle(); //test_plane_line_intersection(); -//test_polygon_line_intersection(); +test_polygon_line_intersection(); //test_plane_intersection(); test_coplanar(); test_points_on_plane(); @@ -542,6 +542,17 @@ module 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() { assert(coplanar([ [5,5,1],[0,0,1],[-1,-1,1] ]) == false); assert(coplanar([ [5,5,1],[0,0,0],[-1,-1,1] ]) == true); diff --git a/version.scad b/version.scad index b37d43f..bdbdafa 100644 --- a/version.scad +++ b/version.scad @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,410]; +BOSL_VERSION = [2,0,415]; // Section: BOSL Library Version Functions diff --git a/vnf.scad b/vnf.scad index edf00e4..085c281 100644 --- a/vnf.scad +++ b/vnf.scad @@ -341,15 +341,17 @@ function vnf_vertex_array( // Arguments: // vnf = A VNF structure, or list of VNF structures. // convexity = Max number of times a line could intersect a wall of the shape. -module vnf_polyhedron(vnf, - convexity=10,anchor="origin",cp, - spin=0, orient=UP, extent=false -) -{ +// 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; - attachable(anchor=anchor, spin=spin, orient=orient, vnf=vnf, extent=extent, cp=is_def(cp) ? cp : vnf_centroid(vnf)){ - polyhedron(vnf[0], vnf[1], convexity=convexity); - children(); + 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(); } }