/** * function_grapher.scad * * @copyright Justin Lin, 2017 * @license https://opensource.org/licenses/lgpl-3.0.html * * @see https://openhome.cc/eGossip/OpenSCAD/lib-function_grapher.html * **/ include ; module function_grapher(points, thickness, style = "FACES", slicing = "SLASH") { rows = len(points); columns = len(points[0]); yi_range = [0:rows - 2]; xi_range = [0:columns - 2]; // Increasing $fn will be slow when you use "LINES", "HULL_FACES" or "HULL_LINES". module faces() { function xy_to_index(x, y, columns) = y * columns + x; top_pts = [ for(row_pts = points) for(pt = row_pts) pt ]; base_pts = [ for(pt = top_pts) [pt[0], pt[1], pt[2] - thickness] ]; leng_pts = len(top_pts); top_tri_faces1 = slicing == "SLASH" ? [ for(yi = yi_range) for(xi = xi_range) [ xy_to_index(xi, yi, columns), xy_to_index(xi + 1, yi + 1, columns), xy_to_index(xi + 1, yi, columns) ] ] : [ for(yi = yi_range) for(xi = xi_range) [ xy_to_index(xi, yi, columns), xy_to_index(xi, yi + 1, columns), xy_to_index(xi + 1, yi, columns) ] ]; top_tri_faces2 = slicing == "SLASH" ? [ for(yi = yi_range) for(xi = xi_range) [ xy_to_index(xi, yi, columns), xy_to_index(xi, yi + 1, columns), xy_to_index(xi + 1, yi + 1, columns) ] ] : [ for(yi = yi_range) for(xi = xi_range) [ xy_to_index(xi, yi + 1, columns), xy_to_index(xi + 1, yi + 1, columns), xy_to_index(xi + 1, yi, columns) ] ]; offset_v = [leng_pts, leng_pts, leng_pts]; base_tri_faces1 = [ for(face = top_tri_faces1) __reverse(face) + offset_v ]; base_tri_faces2 = [ for(face = top_tri_faces2) __reverse(face) + offset_v ]; side_faces1 = [ for(xi = xi_range) let( idx1 = xy_to_index(xi, 0, columns), idx2 = xy_to_index(xi + 1, 0, columns) ) [ idx1, idx2, idx2 + leng_pts, idx1 + leng_pts ] ]; side_faces2 = [ for(yi = yi_range) let( xi = columns - 1, idx1 = xy_to_index(xi, yi, columns), idx2 = xy_to_index(xi, yi + 1, columns) ) [ idx1, idx2, idx2 + leng_pts, idx1 + leng_pts ] ]; side_faces3 = [ for(xi = xi_range) let( yi = rows - 1, idx1 = xy_to_index(xi, yi, columns), idx2 = xy_to_index(xi + 1, yi, columns) ) [ idx2, idx1, idx1 + leng_pts, idx2 + leng_pts ] ]; side_faces4 = [ for(yi = yi_range) let( idx1 = xy_to_index(0, yi, columns), idx2 = xy_to_index(0, yi + 1, columns) ) [ idx2, idx1, idx1 + leng_pts, idx2 + leng_pts ] ]; pts = concat(top_pts, base_pts); face_idxs = concat( top_tri_faces1, top_tri_faces2, base_tri_faces1, base_tri_faces2, side_faces1, side_faces2, side_faces3, side_faces4 ); polyhedron( points = pts, faces = face_idxs ); // hook for testing test_function_grapher_faces(pts, face_idxs); } module tri_to_lines(tri1, tri2) { polyline3d(concat(tri1, [tri1[0]]), thickness); polyline3d(concat(tri2, [tri2[0]]), thickness); } module tri_to_hull_lines(tri1, tri2) { hull_polyline3d(concat(tri1, [tri1[0]]), thickness); hull_polyline3d(concat(tri2, [tri2[0]]), thickness); } module hull_pts(tri) { half_thickness = thickness / 2; hull() { translate(tri[0]) sphere(half_thickness); translate(tri[1]) sphere(half_thickness); translate(tri[2]) sphere(half_thickness); } } module tri_to_hull_faces(tri1, tri2) { hull_pts(tri1); hull_pts(tri2); } module tri_to_graph(tri1, tri2) { if(style == "LINES") { tri_to_lines(tri1, tri2); } else if(style == "HULL_FACES") { // Warning: May be very slow!! tri_to_hull_faces(tri1, tri2); } else if(style == "HULL_LINES") { // Warning: very very slow!! tri_to_hull_lines(tri1, tri2); } } if(style == "FACES") { faces(); } else { for(yi = yi_range) { for(xi = xi_range) { if(slicing == "SLASH") { tri_to_graph([ points[yi][xi], points[yi][xi + 1], points[yi + 1][xi + 1] ], [ points[yi][xi], points[yi + 1][xi + 1], points[yi + 1][xi] ]); } else { tri_to_graph([ points[yi][xi], points[yi][xi + 1], points[yi + 1][xi] ], [ points[yi + 1][xi], points[yi][xi + 1], points[yi + 1][xi + 1] ]); } } } } } // override it to test module test_function_grapher_faces(points, faces) { }