diff --git a/paths.scad b/paths.scad index a14adc6..c870b62 100644 --- a/paths.scad +++ b/paths.scad @@ -351,22 +351,32 @@ function path_tangents(path, closed=false, uniform=true) = // norms = path_normals(path, , ); // Description: // Compute the normal vector to the input path. This vector is perpendicular to the -// path tangent and lies in the plane of the curve. When there are collinear points, -// the curve does not define a unique plane and the normal is not uniquely defined. +// path tangent and lies in the plane of the curve. For 3d paths we define the plane of the curve +// at path point i to be the plane defined by point i and its two neighbors. At the endpoints of open paths +// we use the three end points. The computed normal is the one lying in this plane and pointing to the +// right of the direction of the path. If points are collinear then the path does not define a unique plane +// and hence the (right pointing) normal is not uniquely defined. In this case the function issues an error. +// For 2d paths the plane is always defined so the normal fails to exist only +// when the derivative is zero (in the case of repeated points). function path_normals(path, tangents, closed=false) = - assert(is_path(path)) + assert(is_path(path,[2,3])) assert(is_bool(closed)) - let( tangents = default(tangents, path_tangents(path,closed)) ) - assert(is_path(tangents)) + let( + tangents = default(tangents, path_tangents(path,closed)), + dim=len(path[0]) + ) + assert(is_path(tangents) && len(tangents[0])==dim,"Dimensions of path and tangents must match") [ - for(i=idx(path)) let( - pts = i==0? (closed? select(path,-1,1) : select(path,0,2)) : - i==len(path)-1? (closed? select(path,i-1,i+1) : select(path,i-2,i)) : - select(path,i-1,i+1) - ) unit(cross( - cross(pts[1]-pts[0], pts[2]-pts[0]), - tangents[i] - )) + for(i=idx(path)) + let( + pts = i==0 ? (closed? select(path,-1,1) : select(path,0,2)) + : i==len(path)-1 ? (closed? select(path,i-1,i+1) : select(path,i-2,i)) + : select(path,i-1,i+1) + ) + dim == 2 ? [tangents[i].y,-tangents[i].x] + : let(v=cross(cross(pts[1]-pts[0], pts[2]-pts[0]),tangents[i])) + assert(norm(v)>EPSILON, "3D path contains collinear points") + v ]; diff --git a/shapes2d.scad b/shapes2d.scad index 6755f6a..2d07dcf 100644 --- a/shapes2d.scad +++ b/shapes2d.scad @@ -108,7 +108,8 @@ module stroke( cap=="tail"? [[0,0], [w/2,l2], [w/2,l2-l], [0,-l], [-w/2,l2-l], [-w/2,l2]] : cap=="tail2"? [[w/2,0], [w/2,-l], [0,-l-l2], [-w/2,-l], [-w/2,0]] : is_path(cap)? cap : - [] + is_undef(cap)? [] : + assert(false, str("Invalid endcap: ",cap)) ) * linewidth; assert(is_bool(closed)); diff --git a/vectors.scad b/vectors.scad index 146bdbd..cd45624 100644 --- a/vectors.scad +++ b/vectors.scad @@ -128,7 +128,7 @@ function vceil(v) = // unit([0,0,0]); // Asserts an error. function unit(v, error=[[["ASSERT"]]]) = assert(is_vector(v), str("Expected a vector. Got: ",v)) - norm(v)=EPSILON) : error) : + norm(v)=EPSILON,"Tried to normalize a zero vector") : error) : v/norm(v);