function vsub(a, b) = [a[0]-b[0], a[1]-b[1]]; function vadd(a, b) = [a[0]+b[0], a[1]+b[1]]; function vlen(v) = sqrt(v[0]*v[0] + v[1]*v[1]); function vang(v) = atan2(v[1], v[0]); function norm_angle(a) = a < 0 ? a + 360 : a; function arc_points(c, p1, p2, steps=24, ccw=true) = let( a1 = norm_angle(vang(vsub(p1, c))), a2 = norm_angle(vang(vsub(p2, c))), r = vlen(vsub(p1, c)), delta = ccw ? (a2 >= a1 ? a2 - a1 : a2 + 360 - a1) : (a1 >= a2 ? a1 - a2 : a1 + 360 - a2), dir = ccw ? 1 : -1 ) [ for (i = [0:steps]) let(a = a1 + dir * delta * i / steps) [ c[0] + r * cos(a), c[1] + r * sin(a) ] ]; module loft_between(polya, polyb, z0=0, z1=20) { n = len(polya); assert(n == len(polyb), "Polygons must have same number of vertices"); pts = concat( [ for (p = polya) [p[0], p[1], z0] ], [ for (p = polyb) [p[0], p[1], z1] ] ); side_faces = [ for (i = [0:n-1]) [i, (i+1)%n, n+(i+1)%n, n+i] ]; bottom_cap = [for (i = [n-1:-1:0]) i]; top_cap = [for (i = [0:n-1]) n + i]; polyhedron( points = pts, faces = concat(side_faces, [bottom_cap], [top_cap]), convexity = 10 ); }