diff --git a/beziers.scad b/beziers.scad index 0b9232f..432c150 100644 --- a/beziers.scad +++ b/beziers.scad @@ -43,11 +43,37 @@ include // bezier_points(curve, u) // Description: // Computes bezier points for bezier with control points specified by `curve` at parameter values specified by `u`, which can be a scalar or a list. -// This function uses an optimized method which is best when `u` is a long list and the bezier degree is 10 or less. +// This function uses an optimized method which is best when `u` is a long list and the bezier degree is 10 or less. +// The degree of the bezier curve given is `len(curve)-1`. +// Arguments: +// curve = The list of endpoints and control points for this bezier segment. +// u = The proportion of the way along the curve to find the point of. 0<=`u`<=1 If given as a list or range, returns a list of point, one for each u value. +// Example(2D): Quadratic (Degree 2) Bezier. +// bez = [[0,0], [30,30], [80,0]]; +// trace_bezier(bez, N=len(bez)-1); +// translate(bezier_points(bez, 0.3)) color("red") sphere(1); +// Example(2D): Cubic (Degree 3) Bezier +// bez = [[0,0], [5,35], [60,-25], [80,0]]; +// trace_bezier(bez, N=len(bez)-1); +// translate(bezier_points(bez, 0.4)) color("red") sphere(1); +// Example(2D): Degree 4 Bezier. +// bez = [[0,0], [5,15], [40,20], [60,-15], [80,0]]; +// trace_bezier(bez, N=len(bez)-1); +// translate(bezier_points(bez, 0.8)) color("red") sphere(1); +// Example(2D): Giving a List of `u` +// bez = [[0,0], [5,35], [60,-25], [80,0]]; +// trace_bezier(bez, N=len(bez)-1); +// pts = bezier_points(bez, [0, 0.2, 0.3, 0.7, 0.8, 1]); +// rainbow(pts) move($item) sphere(1.5, $fn=12); +// Example(2D): Giving a Range of `u` +// bez = [[0,0], [5,35], [60,-25], [80,0]]; +// trace_bezier(bez, N=len(bez)-1); +// pts = bezier_points(bez, [0:0.2:1]); +// rainbow(pts) move($item) sphere(1.5, $fn=12); // Ugly but speed optimized code for computing bezier curves using the matrix representation // See https://pomax.github.io/bezierinfo/#matrix for explanation. -// +// // All of the loop unrolling makes and the use of the matrix lookup table make a big difference // in the speed of execution. For orders 10 and below this code is 10-20 times faster than // the recursive code using the de Casteljau method depending on the bezier order and the @@ -55,137 +81,109 @@ include // lookup table or hard coded powers list the code is about twice as fast as the recursive method. // Note that everything I tried to simplify or tidy this code made is slower, sometimes a lot slower. function bezier_points(curve, u) = - is_num(u) ? bezier_points(pts,[u])[0] : - let( - N = len(curve)-1, - M = _bezier_matrix(N)*curve - ) - N==0 ? [for(uval=u)[1]*M] : - N==1 ? [for(uval=u)[1, uval]*M] : - N==2 ? [for(uval=u)[1, uval, uval*uval]*M] : - N==3 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval]*M] : // It appears that pow() is as fast or faster for powers 5 or above - N==4 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval]*M] : - N==5 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5)]*M] : - N==6 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6)]*M] : - N==7 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7)]*M] : - N==8 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8)]*M] : - N==9 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8), pow(uval,9)]*M] : - N==10? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8), pow(uval,9), pow(uval,10)]*M] : - /* N>=11 */ [for(uval=u)[for (i=[0:1:N]) pow(uval,i)]*M]; + is_num(u) ? bezier_points(curve,[u])[0] : + let( + N = len(curve)-1, + M = _bezier_matrix(N)*curve + ) + N==0 ? [for(uval=u)[1]*M] : + N==1 ? [for(uval=u)[1, uval]*M] : + N==2 ? [for(uval=u)[1, uval, uval*uval]*M] : + N==3 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval]*M] : // It appears that pow() is as fast or faster for powers 5 or above + N==4 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval]*M] : + N==5 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5)]*M] : + N==6 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6)]*M] : + N==7 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7)]*M] : + N==8 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8)]*M] : + N==9 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8), pow(uval,9)]*M] : + N==10? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8), pow(uval,9), pow(uval,10)]*M] : + /* N>=11 */ [for(uval=u)[for (i=[0:1:N]) pow(uval,i)]*M]; + +// Not public. function _signed_pascals_triangle(N,tri=[[-1]]) = - len(tri)==N+1 ? tri : - let(last=tri[len(tri)-1]) - _signed_pascals_triangle(N,concat(tri,[[-1, for(i=[0:1:len(tri)-2]) (i%2==1?-1:1)*(abs(last[i])+abs(last[i+1])),len(last)%2==0? -1:1]])); + len(tri)==N+1 ? tri : + let(last=tri[len(tri)-1]) + _signed_pascals_triangle(N,concat(tri,[[-1, for(i=[0:1:len(tri)-2]) (i%2==1?-1:1)*(abs(last[i])+abs(last[i+1])),len(last)%2==0? -1:1]])); -function _compute_bez_matrix(N) = - let(tri = _signed_pascals_triangle(N)) - [for(i=[0:N]) concat(tri[N][i]*tri[i], repeat(0,N-i))]; + +// Not public. +function _compute_bezier_matrix(N) = + let(tri = _signed_pascals_triangle(N)) + [for(i=[0:N]) concat(tri[N][i]*tri[i], repeat(0,N-i))]; // The bezier matrix, which is related to Pascal's triangle, enables nonrecursive computation // of bezier points. This method is much faster than the recursive de Casteljau method -// in OpenScad, but we have to precompute the matrices to reap the full benefit. +// in OpenScad, but we have to precompute the matrices to reap the full benefit. +// Not public. _bezier_matrix_table = [ - [[1]], - [[ 1, 0], - [-1, 1]], - [[1, 0, 0], - [-2, 2, 0], - [1, -2, 1]], - [[ 1, 0, 0, 0], - [-3, 3, 0, 0], - [ 3,-6, 3, 0], - [-1, 3,-3, 1]], - [[ 1, 0, 0, 0, 0], - [-4, 4, 0, 0, 0], - [ 6,-12, 6, 0, 0], - [-4, 12,-12, 4, 0], - [ 1, -4, 6,-4, 1]], - [[ 1, 0, 0, 0, 0, 0], - [ -5, 5, 0, 0, 0, 0], - [ 10,-20, 10, 0, 0, 0], - [-10, 30,-30, 10, 0, 0], - [ 5,-20, 30,-20, 5, 0], - [ -1, 5,-10, 10,-5, 1]], - [[ 1, 0, 0, 0, 0, 0, 0], - [ -6, 6, 0, 0, 0, 0, 0], - [ 15,-30, 15, 0, 0, 0, 0], - [-20, 60,-60, 20, 0, 0, 0], - [ 15,-60, 90,-60, 15, 0, 0], - [ -6, 30,-60, 60,-30, 6, 0], - [ 1, -6, 15,-20, 15,-6, 1]], - [[ 1, 0, 0, 0, 0, 0, 0, 0], - [ -7, 7, 0, 0, 0, 0, 0, 0], - [ 21, -42, 21, 0, 0, 0, 0, 0], - [-35, 105,-105, 35, 0, 0, 0, 0], - [ 35,-140, 210,-140, 35, 0, 0, 0], - [-21, 105,-210, 210,-105, 21, 0, 0], - [ 7, -42, 105,-140, 105,-42, 7, 0], - [ -1, 7, -21, 35, -35, 21,-7, 1]], - [[ 1, 0, 0, 0, 0, 0, 0, 0, 0], - [ -8, 8, 0, 0, 0, 0, 0, 0, 0], - [ 28, -56, 28, 0, 0, 0, 0, 0, 0], - [-56, 168,-168, 56, 0, 0, 0, 0, 0], - [ 70,-280, 420,-280, 70, 0, 0, 0, 0], - [-56, 280,-560, 560,-280, 56, 0, 0, 0], - [ 28,-168, 420,-560, 420,-168, 28, 0, 0], - [ -8, 56,-168, 280,-280, 168,-56, 8, 0], - [ 1, -8, 28, -56, 70, -56, 28,-8, 1]], - [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [-9, 9, 0, 0, 0, 0, 0, 0, 0, 0], [36, -72, 36, 0, 0, 0, 0, 0, 0, 0], [-84, 252, -252, 84, 0, 0, 0, 0, 0, 0], - [126, -504, 756, -504, 126, 0, 0, 0, 0, 0], [-126, 630, -1260, 1260, -630, 126, 0, 0, 0, 0], [84, -504, 1260, -1680, 1260, -504, 84, 0, 0, 0], - [-36, 252, -756, 1260, -1260, 756, -252, 36, 0, 0], [9, -72, 252, -504, 630, -504, 252, -72, 9, 0], [-1, 9, -36, 84, -126, 126, -84, 36, -9, 1]], - [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [-10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0], [45, -90, 45, 0, 0, 0, 0, 0, 0, 0, 0], [-120, 360, -360, 120, 0, 0, 0, 0, 0, 0, 0], - [210, -840, 1260, -840, 210, 0, 0, 0, 0, 0, 0], [-252, 1260, -2520, 2520, -1260, 252, 0, 0, 0, 0, 0], - [210, -1260, 3150, -4200, 3150, -1260, 210, 0, 0, 0, 0], [-120, 840, -2520, 4200, -4200, 2520, -840, 120, 0, 0, 0], - [45, -360, 1260, -2520, 3150, -2520, 1260, -360, 45, 0, 0], [-10, 90, -360, 840, -1260, 1260, -840, 360, -90, 10, 0], - [1, -10, 45, -120, 210, -252, 210, -120, 45, -10, 1]] + [[1]], + [[ 1, 0], + [-1, 1]], + [[1, 0, 0], + [-2, 2, 0], + [1, -2, 1]], + [[ 1, 0, 0, 0], + [-3, 3, 0, 0], + [ 3,-6, 3, 0], + [-1, 3,-3, 1]], + [[ 1, 0, 0, 0, 0], + [-4, 4, 0, 0, 0], + [ 6,-12, 6, 0, 0], + [-4, 12,-12, 4, 0], + [ 1, -4, 6,-4, 1]], + [[ 1, 0, 0, 0, 0, 0], + [ -5, 5, 0, 0, 0, 0], + [ 10,-20, 10, 0, 0, 0], + [-10, 30,-30, 10, 0, 0], + [ 5,-20, 30,-20, 5, 0], + [ -1, 5,-10, 10,-5, 1]], + [[ 1, 0, 0, 0, 0, 0, 0], + [ -6, 6, 0, 0, 0, 0, 0], + [ 15,-30, 15, 0, 0, 0, 0], + [-20, 60,-60, 20, 0, 0, 0], + [ 15,-60, 90,-60, 15, 0, 0], + [ -6, 30,-60, 60,-30, 6, 0], + [ 1, -6, 15,-20, 15,-6, 1]], + [[ 1, 0, 0, 0, 0, 0, 0, 0], + [ -7, 7, 0, 0, 0, 0, 0, 0], + [ 21, -42, 21, 0, 0, 0, 0, 0], + [-35, 105,-105, 35, 0, 0, 0, 0], + [ 35,-140, 210,-140, 35, 0, 0, 0], + [-21, 105,-210, 210,-105, 21, 0, 0], + [ 7, -42, 105,-140, 105,-42, 7, 0], + [ -1, 7, -21, 35, -35, 21,-7, 1]], + [[ 1, 0, 0, 0, 0, 0, 0, 0, 0], + [ -8, 8, 0, 0, 0, 0, 0, 0, 0], + [ 28, -56, 28, 0, 0, 0, 0, 0, 0], + [-56, 168,-168, 56, 0, 0, 0, 0, 0], + [ 70,-280, 420,-280, 70, 0, 0, 0, 0], + [-56, 280,-560, 560,-280, 56, 0, 0, 0], + [ 28,-168, 420,-560, 420,-168, 28, 0, 0], + [ -8, 56,-168, 280,-280, 168,-56, 8, 0], + [ 1, -8, 28, -56, 70, -56, 28,-8, 1]], + [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [-9, 9, 0, 0, 0, 0, 0, 0, 0, 0], [36, -72, 36, 0, 0, 0, 0, 0, 0, 0], [-84, 252, -252, 84, 0, 0, 0, 0, 0, 0], + [126, -504, 756, -504, 126, 0, 0, 0, 0, 0], [-126, 630, -1260, 1260, -630, 126, 0, 0, 0, 0], [84, -504, 1260, -1680, 1260, -504, 84, 0, 0, 0], + [-36, 252, -756, 1260, -1260, 756, -252, 36, 0, 0], [9, -72, 252, -504, 630, -504, 252, -72, 9, 0], [-1, 9, -36, 84, -126, 126, -84, 36, -9, 1]], + [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [-10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0], [45, -90, 45, 0, 0, 0, 0, 0, 0, 0, 0], [-120, 360, -360, 120, 0, 0, 0, 0, 0, 0, 0], + [210, -840, 1260, -840, 210, 0, 0, 0, 0, 0, 0], [-252, 1260, -2520, 2520, -1260, 252, 0, 0, 0, 0, 0], + [210, -1260, 3150, -4200, 3150, -1260, 210, 0, 0, 0, 0], [-120, 840, -2520, 4200, -4200, 2520, -840, 120, 0, 0, 0], + [45, -360, 1260, -2520, 3150, -2520, 1260, -360, 45, 0, 0], [-10, 90, -360, 840, -1260, 1260, -840, 360, -90, 10, 0], + [1, -10, 45, -120, 210, -252, 210, -120, 45, -10, 1]] ]; +// Not public. function _bezier_matrix(N) = - N>10 ? _compute_bez_matrix(N) - : _bezier_matrix_table[N]; + N>10 ? _compute_bezier_matrix(N) : + _bezier_matrix_table[N]; -// Function: bez_point() +// Function: bezier_derivative() // Usage: -// bez_point(curve, u) -// Description: -// Formula to calculate points on a bezier curve. The degree of -// the curve, N, is one less than the number of points in `curve`. -// Arguments: -// curve = The list of endpoints and control points for this bezier segment. -// u = The proportion of the way along the curve to find the point of. 0<=`u`<=1 If given as a list or range, returns a list of points for each u value. -// Example(2D): Quadratic (Degree 2) Bezier. -// bez = [[0,0], [30,30], [80,0]]; -// trace_bezier(bez, N=len(bez)-1); -// translate(bez_point(bez, 0.3)) color("red") sphere(1); -// Example(2D): Cubic (Degree 3) Bezier -// bez = [[0,0], [5,35], [60,-25], [80,0]]; -// trace_bezier(bez, N=len(bez)-1); -// translate(bez_point(bez, 0.4)) color("red") sphere(1); -// Example(2D): Degree 4 Bezier. -// bez = [[0,0], [5,15], [40,20], [60,-15], [80,0]]; -// trace_bezier(bez, N=len(bez)-1); -// translate(bez_point(bez, 0.8)) color("red") sphere(1); -function bez_point(curve, u)= - is_num(u)? ( - (len(curve) <= 1)? curve[0] : - bez_point( - [ - for(i=[0:1:len(curve)-2]) - curve[i]*(1-u) + curve[i+1]*u - ], u - ) - ) : [for (uu = u) bez_point(curve, uu)]; - - - -// Function: bez_deriv() -// Usage: -// d = bez_deriv(curve, u, [order]); +// d = bezier_derivative(curve, u, [order]); // Description: // Finds the `order`th derivative of the bezier segment at the given position `u`. // The degree of the bezier segment is one less than the number of points in `curve`. @@ -193,13 +191,13 @@ function bez_point(curve, u)= // curve = The list of endpoints and control points for this bezier segment. // u = The proportion of the way along the curve to find the derivative of. 0<=`u`<=1 If given as a list or range, returns a list of derivatives, one for each u value. // order = The order of the derivative to return. Default: 1 (for the first derivative) -function bez_deriv(curve, u, order=1) = +function bezier_derivative(curve, u, order=1) = assert(is_int(order) && order>=0) - order==0? bez_point(curve, u) : let( + order==0? bezier_points(curve, u) : let( N = len(curve) - 1, dpts = N * deltas(curve) - ) order==1? bez_point(dpts, u) : - bez_deriv(dpts, u, order-1); + ) order==1? bezier_points(dpts, u) : + bezier_derivative(dpts, u, order-1); @@ -213,7 +211,7 @@ function bez_deriv(curve, u, order=1) = // u = The proportion of the way along the curve to find the tangent vector of. 0<=`u`<=1 If given as a list or range, returns a list of tangent vectors, one for each u value. function bezier_tangent(curve, u) = let( - res = bez_deriv(curve, u) + res = bezier_derivative(curve, u) ) is_vector(res)? unit(res) : [for (v=res) unit(v)]; @@ -231,10 +229,10 @@ function bezier_tangent(curve, u) = // curve = The list of endpoints and control points for this bezier segment. // u = The proportion of the way along the curve to find the curvature of. 0<=`u`<=1 If given as a list or range, returns a list of curvature values, one for each u value. function bezier_curvature(curve, u) = - is_num(u) ? bezier_curvature(curve,[u])[0] : + is_num(u) ? bezier_curvature(curve,[u])[0] : let( - d1 = bez_deriv(curve, u, 1), - d2 = bez_deriv(curve, u, 2) + d1 = bezier_derivative(curve, u, 1), + d2 = bezier_derivative(curve, u, 2) ) [ for(i=idx(d1)) sqrt( @@ -259,17 +257,17 @@ function bezier_curvature(curve, u) = // n = The number of points to generate along the bezier curve. // Example(2D): Quadratic (Degree 2) Bezier. // bez = [[0,0], [30,30], [80,0]]; -// move_copies(bezier_curve(bez, 16)) sphere(r=1); +// move_copies(bezier_curve(bez, 8)) sphere(r=1.5, $fn=12); // trace_bezier(bez, N=len(bez)-1); // Example(2D): Cubic (Degree 3) Bezier // bez = [[0,0], [5,35], [60,-25], [80,0]]; -// move_copies(bezier_curve(bez, 16)) sphere(r=1); +// move_copies(bezier_curve(bez, 8)) sphere(r=1.5, $fn=12); // trace_bezier(bez, N=len(bez)-1); // Example(2D): Degree 4 Bezier. // bez = [[0,0], [5,15], [40,20], [60,-15], [80,0]]; -// move_copies(bezier_curve(bez, 16)) sphere(r=1); +// move_copies(bezier_curve(bez, 8)) sphere(r=1.5, $fn=12); // trace_bezier(bez, N=len(bez)-1); -function bezier_curve(curve,n) = [for(i=[0:1:n-1]) bez_point(curve, i/(n-1))]; +function bezier_curve(curve,n) = bezier_points(curve, [0:1/n:(n-0.5)/n]); // Function: bezier_segment_closest_point() @@ -289,18 +287,18 @@ function bezier_curve(curve,n) = [for(i=[0:1:n-1]) bez_point(curve, i/(n-1))]; // u = bezier_segment_closest_point(bez, pt); // trace_bezier(bez, N=len(bez)-1); // color("red") translate(pt) sphere(r=1); -// color("blue") translate(bez_point(bez,u)) sphere(r=1); -function bezier_segment_closest_point(curve, pt, max_err=0.01, u=0, end_u=1) = +// color("blue") translate(bezier_points(bez,u)) sphere(r=1); +function bezier_segment_closest_point(curve, pt, max_err=0.01, u=0, end_u=1) = let( steps = len(curve)*3, - path = [for (i=[0:1:steps]) let(v=(end_u-u)*(i/steps)+u) [v, bez_point(curve, v)]], - bracketed = concat([path[0]], path, [path[len(path)-1]]), + uvals = [u, for (i=[0:1:steps]) (end_u-u)*(i/steps)+u, end_u], + path = bezier_points(curve,uvals), minima_ranges = [ - for (pts = triplet(bracketed)) let( - d1=norm(pts.x.y-pt), - d2=norm(pts.y.y-pt), - d3=norm(pts.z.y-pt) - ) if(d2<=d1 && d2<=d3) [pts.x.x,pts.z.x] + for (i = [1:1:len(uvals)-2]) let( + d1 = norm(path[i-1]-pt), + d2 = norm(path[i ]-pt), + d3 = norm(path[i+1]-pt) + ) if (d2<=d1 && d2<=d3) [uvals[i-1],uvals[i+1]] ] ) len(minima_ranges)>1? ( let( @@ -308,16 +306,15 @@ function bezier_segment_closest_point(curve, pt, max_err=0.01, u=0, end_u=1) = for (minima = minima_ranges) bezier_segment_closest_point(curve, pt, max_err=max_err, u=minima.x, end_u=minima.y) ], - dists = [for (v=min_us) norm(bez_point(curve,v)-pt)], + dists = [for (v=min_us) norm(bezier_points(curve,v)-pt)], min_i = min_index(dists) ) min_us[min_i] ) : let( minima = minima_ranges[0], - p1 = bez_point(curve, minima.x), - p2 = bez_point(curve, minima.y), - err = norm(p2-p1) + pp = bezier_points(curve, minima), + err = norm(pp[1]-pp[0]) ) err -include include @@ -28,24 +27,15 @@ function CR_corner(size, spin=0, orient=UP, trans=[0,0,0]) = function CR_edge(size, spin=0, orient=UP, trans=[0,0,0]) = let ( - // This patch might not yet correct for continuous rounding, + // This patch might not be correct for continuous rounding, // but it's a first approximation proof of concept. - a = 0.68, - c = 0.24, - m = -1/2, - n = -3/10, - o = -1/10, - p = 1/10, - q = 3/10, - r = 1/2, - patch = [ - [[1,0,m], [1,0,n], [1,0,o], [1,0,p], [1,0,q], [1,0,r]], - [[a,0,m], [a,0,n], [a,0,o], [a,0,p], [a,0,q], [a,0,r]], - [[c,0,m], [c,0,n], [c,0,o], [c,0,p], [c,0,q], [c,0,r]], - [[0,c,m], [0,c,n], [0,c,o], [0,c,p], [0,c,q], [0,c,r]], - [[0,a,m], [0,a,n], [0,a,o], [0,a,p], [0,a,q], [0,a,r]], - [[0,1,m], [0,1,n], [0,1,o], [0,1,p], [0,1,q], [0,1,r]], - ] + vvals = [1.00, 0.68, 0.24], + xyvals = [ + for (x=vvals) [x,0], + for (y=reverse(vvals)) [0,y] + ], + zvals = [-0.5:0.2:0.5], + patch = [for (xy=xyvals) [for (z=zvals) [each xy, z]]] ) translate(trans, p=rot(a=spin, from=UP, to=orient, @@ -58,44 +48,23 @@ module CR_cube(size=[100,100,100], r=10, splinesteps=8, debug=false) { s = size-2*[r,r,r]; h = size/2; + corner_pat = CR_corner([r,r,r], trans=[-size.x/2, -size.y/2, -size.z/2]); + edge_pat = CR_edge([r, r, s.z], trans=[-h.x, -h.y, 0]); + face_pat = bezier_patch_flat([s.x, s.z], N=1, orient=FRONT, trans=[0, -h.y, 0]); corners = bezier_surface([ - CR_corner([r,r,r], spin=0, orient=UP, trans=[-size.x/2, -size.y/2, -size.z/2]), - CR_corner([r,r,r], spin=90, orient=UP, trans=[ size.x/2, -size.y/2, -size.z/2]), - CR_corner([r,r,r], spin=180, orient=UP, trans=[ size.x/2, size.y/2, -size.z/2]), - CR_corner([r,r,r], spin=270, orient=UP, trans=[-size.x/2, size.y/2, -size.z/2]), - - CR_corner([r,r,r], spin=0, orient=DOWN, trans=[ size.x/2, -size.y/2, size.z/2]), - CR_corner([r,r,r], spin=90, orient=DOWN, trans=[-size.x/2, -size.y/2, size.z/2]), - CR_corner([r,r,r], spin=180, orient=DOWN, trans=[-size.x/2, size.y/2, size.z/2]), - CR_corner([r,r,r], spin=270, orient=DOWN, trans=[ size.x/2, size.y/2, size.z/2]), + for (yr=[0,180], zr=[0:90:270]) let( + m = yrot(yr) * zrot(zr) + ) [for (row=corner_pat) apply(m, row)] ], splinesteps=splinesteps); edges = bezier_surface([ - CR_edge([r, r, s.x], spin=0, orient=RIGHT, trans=[ 0, -h.y, h.z]), - CR_edge([r, r, s.x], spin=90, orient=RIGHT, trans=[ 0, -h.y, -h.z]), - CR_edge([r, r, s.x], spin=180, orient=RIGHT, trans=[ 0, h.y, -h.z]), - CR_edge([r, r, s.x], spin=270, orient=RIGHT, trans=[ 0, h.y, h.z]), - - CR_edge([r, r, s.y], spin=0, orient=BACK, trans=[-h.x, 0, h.z]), - CR_edge([r, r, s.y], spin=90, orient=BACK, trans=[ h.x, 0, h.z]), - CR_edge([r, r, s.y], spin=180, orient=BACK, trans=[ h.x, 0, -h.z]), - CR_edge([r, r, s.y], spin=270, orient=BACK, trans=[-h.x, 0, -h.z]), - - CR_edge([r, r, s.z], spin=0, orient=UP, trans=[-h.x, -h.y, 0]), - CR_edge([r, r, s.z], spin=90, orient=UP, trans=[ h.x, -h.y, 0]), - CR_edge([r, r, s.z], spin=180, orient=UP, trans=[ h.x, h.y, 0]), - CR_edge([r, r, s.z], spin=270, orient=UP, trans=[-h.x, h.y, 0]) - ], splinesteps=[1,splinesteps]); + for (axr=[[0,0,0],[90,0,0],[0,90,0]],zr=[0:90:270]) let( + m = rot(axr) * zrot(zr) + ) [for (row=edge_pat) apply(m, row)] + ], splinesteps=[splinesteps,1]); faces = bezier_surface([ - // Yes, these are degree 1 bezier patches. That means just the four corner points. - // Since these are flat, it doesn't matter what degree they are, and this will reduce calculation overhead. - bezier_patch_flat([s.y, s.z], N=1, orient=RIGHT, trans=[ h.x, 0, 0]), - bezier_patch_flat([s.y, s.z], N=1, orient=LEFT, trans=[-h.x, 0, 0]), - - bezier_patch_flat([s.x, s.z], N=1, orient=BACK, trans=[ 0, h.y, 0]), - bezier_patch_flat([s.x, s.z], N=1, orient=FRONT, trans=[ 0, -h.y, 0]), - - bezier_patch_flat([s.x, s.y], N=1, orient=UP, trans=[ 0, 0, h.z]), - bezier_patch_flat([s.x, s.y], N=1, orient=DOWN, trans=[ 0, 0, -h.z]) + for (axr=[0,90,180,270,[-90,0,0],[90,0,0]]) let( + m = rot(axr) + ) [for (row=face_pat) apply(m, row)] ], splinesteps=1); if (debug) { diff --git a/examples/cubic_sphere_packing.scad b/examples/cubic_sphere_packing.scad index 41221b3..a529250 100644 --- a/examples/cubic_sphere_packing.scad +++ b/examples/cubic_sphere_packing.scad @@ -1,5 +1,4 @@ include -include //$fa=2; //$fs=2; diff --git a/examples/hex_sphere_packing.scad b/examples/hex_sphere_packing.scad index 23182b5..baa3983 100644 --- a/examples/hex_sphere_packing.scad +++ b/examples/hex_sphere_packing.scad @@ -1,5 +1,4 @@ include -include //$fa=2; //$fs=2; diff --git a/examples/spherical_patch.scad b/examples/spherical_patch.scad index 364b41d..de07293 100644 --- a/examples/spherical_patch.scad +++ b/examples/spherical_patch.scad @@ -8,10 +8,10 @@ p = s * d; q = s * 0.55 * d; u = s * 2.5 * UP; patch1 = [ - [p[1], p[1]+q[0], p[0]+q[1], p[0] ], - [p[1]+q[2], p[1]+q[1]+u, p[0]+q[0]+u, p[0]+q[3]], - [p[2]+q[1], p[2]+q[2]+u, p[3]+q[3]+u, p[3]+q[0]], [p[2], p[2]+q[3], p[3]+q[2], p[3] ], + [p[2]+q[1], p[2]+q[2]+u, p[3]+q[3]+u, p[3]+q[0]], + [p[1]+q[2], p[1]+q[1]+u, p[0]+q[0]+u, p[0]+q[3]], + [p[1], p[1]+q[0], p[0]+q[1], p[0] ], ]; patch2 = patch_reverse(zflip(p=patch1)); trace_bezier_patches([patch1, patch2], splinesteps=16, style="quincunx"); diff --git a/version.scad b/version.scad index 87ea523..0525317 100644 --- a/version.scad +++ b/version.scad @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,299]; +BOSL_VERSION = [2,0,300]; // Section: BOSL Library Version Functions