diff --git a/rounding.scad b/rounding.scad index 986c0b8..bb88dc1 100644 --- a/rounding.scad +++ b/rounding.scad @@ -2026,6 +2026,11 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) = // joint_top[1] is negative the shape will flare upward. At least one value must be non-negative. The same rules apply for joint_bot. // The joint_sides parameter must be entirely nonnegative. // . +// If the roundings at two adjacent side edges exceed the width of the face then the polyhedron will have self-intersecting faces, so it will be invalid. +// Similarly, if the roundings on the top or bottom edges cross the top face and intersect with each other, the resulting polyhedron is invalid: +// the top face after the roundings are applied must be a valid, non-degenerate polyhedron. There are two exceptions: it is permissible to +// construct a top that is a single point or two points. This means you can completely round a cube by setting the joint to half of +// the cube's width. // If you set `debug` to true the module version will display the polyhedron even when it is invalid and it will show the bezier patches at the corners. // This can help troubleshoot problems with your parameters. With the function form setting debug to true causes it to return [patches,vnf] where // patches is a list of the bezier control points for the corner patches. @@ -2210,23 +2215,44 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b bot_patch = _rp_compute_patches(bottom, top, joint_bot, joint_sides_vec, k_bot, k_sides_vec, concave), vertbad = [for(i=[0:N-1]) - if (norm(top[i]-top_patch[i][4][2]) + norm(bottom[i]-bot_patch[i][4][2]) > norm(bottom[i]-top[i])) i], + if (norm(top[i]-top_patch[i][4][2]) + norm(bottom[i]-bot_patch[i][4][2]) > EPSILON + norm(bottom[i]-top[i])) i], + // Check that the patch fits on the polygon edge topbad = [for(i=[0:N-1]) if (norm(top_patch[i][2][4]-top_patch[i][2][2]) + norm(select(top_patch,i+1)[2][0]-select(top_patch,i+1)[2][2]) - > norm(top_patch[i][2][2] - select(top_patch,i+1)[2][2])) [i,(i+1)%N]], + > EPSILON + norm(top_patch[i][2][2] - select(top_patch,i+1)[2][2])) [i,(i+1)%N]], botbad = [for(i=[0:N-1]) if (norm(bot_patch[i][2][4]-bot_patch[i][2][2]) + norm(select(bot_patch,i+1)[2][0]-select(bot_patch,i+1)[2][2]) - > norm(bot_patch[i][2][2] - select(bot_patch,i+1)[2][2])) [i,(i+1)%N]], - topinbad = [for(i=[0:N-1]) + > EPSILON + norm(bot_patch[i][2][2] - select(bot_patch,i+1)[2][2])) [i,(i+1)%N]], + // If top/bot is L-shaped, check that arms of L from adjacent patches don't cross + topLbad = [for(i=[0:N-1]) if (norm(top_patch[i][0][2]-top_patch[i][0][4]) + norm(select(top_patch,i+1)[0][0]-select(top_patch,i+1)[0][2]) - > norm(top_patch[i][0][2]-select(top_patch,i+1)[0][2])) [i,(i+1)%N]], - botinbad = [for(i=[0:N-1]) + > EPSILON + norm(top_patch[i][0][2]-select(top_patch,i+1)[0][2])) [i,(i+1)%N]], + botLbad = [for(i=[0:N-1]) if (norm(bot_patch[i][0][2]-bot_patch[i][0][4]) + norm(select(bot_patch,i+1)[0][0]-select(bot_patch,i+1)[0][2]) - > norm(bot_patch[i][0][2]-select(bot_patch,i+1)[0][2])) [i,(i+1)%N]] + > EPSILON + norm(bot_patch[i][0][2]-select(bot_patch,i+1)[0][2])) [i,(i+1)%N]], + // Check that the inner edges of the patch don't cross + topinbad = [for(i=[0:N-1]) + let( + line1 = project_plane(top,[top_patch[i][2][0],top_patch[i][0][0]]), + line2 = project_plane(top,[select(top_patch,i+1)[2][4],select(top_patch,i+1)[0][4]]) + ) + if (!approx(line1[0],line1[1]) && !approx(line2[0],line2[1]) && + line_intersection(line1,line2, SEGMENT,SEGMENT)) + [i,(i+1)%N]], + botinbad = [for(i=[0:N-1]) + let( + line1 = project_plane(bottom,[bot_patch[i][2][0],bot_patch[i][0][0]]), + line2 = project_plane(bottom,[select(bot_patch,i+1)[2][4],select(bot_patch,i+1)[0][4]]) + ) + if (!approx(line1[0],line1[1]) && !approx(line2[0],line2[1]) && + line_intersection(line1,line2, SEGMENT,SEGMENT)) + [i,(i+1)%N]] ) assert(debug || vertbad==[], str("Top and bottom joint lengths are too large; they interfere with each other at vertices: ",vertbad)) - assert(debug || topbad==[], str("Joint lengths too large at top edges: ",topbad)) - assert(debug || botbad==[], str("Joint lengths too large at bottom edges: ",botbad)) + assert(debug || topbad==[], str("Joint lengths too large at top or side edges: ",topbad)) + assert(debug || botbad==[], str("Joint lengths too large at bottom or side edges: ",botbad)) + assert(debug || topLbad==[], str("Joint length too large on the top face or side at edges: ", topLbad)) + assert(debug || botLbad==[], str("Joint length too large on the bottom face or side at edges: ", botLbad)) assert(debug || topinbad==[], str("Joint length too large on the top face at edges: ", topinbad)) assert(debug || botinbad==[], str("Joint length too large on the bottom face at edges: ", botinbad)) let( @@ -2256,8 +2282,12 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b top_patch[i][4][4] ] ], - top_simple = is_path_simple(project_plane(faces[0],faces[0]),closed=true), - bot_simple = is_path_simple(project_plane(faces[1],faces[1]),closed=true), + top_collinear = is_collinear(faces[0]), + bot_collinear = is_collinear(faces[1]), + top_degen_ok = top_collinear && len(deduplicate(faces[0]))<=2, + bot_degen_ok = bot_collinear && len(deduplicate(faces[1]))<=2, + top_simple = top_degen_ok || (!top_collinear && is_path_simple(project_plane(faces[0],faces[0]),closed=true)), + bot_simple = bot_degen_ok || (!bot_collinear && is_path_simple(project_plane(faces[1],faces[1]),closed=true)), // verify vertical edges verify_vert = [for(i=[0:N-1],j=[0:4])