mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-28 15:59:45 +00:00
Refactored various bezier routines to use fast bezier point generation.
This commit is contained in:
parent
4cacebadef
commit
a5fb810738
6 changed files with 252 additions and 267 deletions
436
beziers.scad
436
beziers.scad
|
@ -43,11 +43,37 @@ include <skin.scad>
|
|||
// 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 <skin.scad>
|
|||
// 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<max_err? mean(minima) :
|
||||
bezier_segment_closest_point(curve, pt, max_err=max_err, u=minima.x, end_u=minima.y);
|
||||
bezier_segment_closest_point(curve, pt, max_err=max_err, u=minima[0], end_u=minima[1]);
|
||||
|
||||
|
||||
|
||||
|
@ -338,11 +335,8 @@ function bezier_segment_closest_point(curve, pt, max_err=0.01, u=0, end_u=1) =
|
|||
function bezier_segment_length(curve, start_u=0, end_u=1, max_deflect=0.01) =
|
||||
let(
|
||||
segs = len(curve) * 2,
|
||||
path = [
|
||||
for (i=[0:1:segs])
|
||||
let(u=lerp(start_u, end_u, i/segs))
|
||||
bez_point(curve,u)
|
||||
],
|
||||
uvals = [for (i=[0:1:segs]) lerp(start_u, end_u, i/segs)],
|
||||
path = bezier_points(curve,uvals),
|
||||
defl = max([
|
||||
for (i=idx(path,end=-3)) let(
|
||||
mp = (path[i] + path[i+2]) / 2
|
||||
|
@ -394,7 +388,7 @@ function fillet3pts(p0, p1, p2, r, maxerr=0.1, w=0.5, dw=0.25) = let(
|
|||
cp0 = lerp(tp0, p1, w),
|
||||
cp1 = lerp(tp1, p1, w),
|
||||
cpr = norm(cp-tp0),
|
||||
bp = bez_point([tp0, cp0, cp1, tp1], 0.5),
|
||||
bp = bezier_points([tp0, cp0, cp1, tp1], 0.5),
|
||||
tdist = norm(cp-bp)
|
||||
) (abs(tdist-cpr) <= maxerr)? [tp0, tp0, cp0, cp1, tp1, tp1] :
|
||||
(tdist<cpr)? fillet3pts(p0, p1, p2, r, maxerr=maxerr, w=w+dw, dw=dw/2) :
|
||||
|
@ -408,13 +402,15 @@ function fillet3pts(p0, p1, p2, r, maxerr=0.1, w=0.5, dw=0.25) = let(
|
|||
// Function: bezier_path_point()
|
||||
// Usage:
|
||||
// bezier_path_point(path, seg, u, [N])
|
||||
// Description: Returns the coordinates of bezier path segment `seg` at position `u`.
|
||||
// Description:
|
||||
// Returns the coordinates of bezier path segment `seg` at position `u`.
|
||||
// Arguments:
|
||||
// path = A bezier path to approximate.
|
||||
// seg = Segment number along the path. Each segment is N points long.
|
||||
// u = The proportion of the way along the segment to find the point of. 0<=`u`<=1
|
||||
// u = The proportion of the way along the segment to find the point of. 0<=`u`<=1 If given as a list or range, returns a list of points, one for each value in `u`.
|
||||
// N = The degree of the bezier curves. Cubic beziers have N=3. Default: 3
|
||||
function bezier_path_point(path, seg, u, N=3) = bez_point(select(path,seg*N,(seg+1)*N), u);
|
||||
function bezier_path_point(path, seg, u, N=3) =
|
||||
bezier_points(select(path,seg*N,(seg+1)*N), u);
|
||||
|
||||
|
||||
|
||||
|
@ -450,7 +446,7 @@ function bezier_path_closest_point(path, pt, N=3, max_err=0.01, seg=0, min_seg=u
|
|||
let(
|
||||
curve = select(path,seg*N,(seg+1)*N),
|
||||
u = bezier_segment_closest_point(curve, pt, max_err=0.05),
|
||||
dist = norm(bez_point(curve, u)-pt),
|
||||
dist = norm(bezier_points(curve, u)-pt),
|
||||
mseg = (min_dist==undef || dist<min_dist)? seg : min_seg,
|
||||
mdist = (min_dist==undef || dist<min_dist)? dist : min_dist,
|
||||
mu = (min_dist==undef || dist<min_dist)? u : min_u
|
||||
|
@ -874,9 +870,9 @@ module trace_bezier(bez, N=3, size=1) {
|
|||
// Section: Patch Functions
|
||||
|
||||
|
||||
// Function: bezier_patch_point()
|
||||
// Function: bezier_patch_points()
|
||||
// Usage:
|
||||
// bezier_patch_point(patch, u, v)
|
||||
// bezier_patch_points(patch, u, v)
|
||||
// Description:
|
||||
// Given a square 2-dimensional array of (N+1) by (N+1) points size,
|
||||
// that represents a Bezier Patch of degree N, returns a point on that
|
||||
|
@ -885,8 +881,8 @@ module trace_bezier(bez, N=3, size=1) {
|
|||
// its own degree.
|
||||
// Arguments:
|
||||
// patch = The 2D array of endpoints and control points for this bezier patch.
|
||||
// u = The proportion of the way along the first dimension of the patch to find the point of. 0<=`u`<=1
|
||||
// v = The proportion of the way along the second dimension of the patch to find the point of. 0<=`v`<=1
|
||||
// u = The proportion of the way along the horizontal inner list of the patch to find the point of. 0<=`u`<=1. If given as a list or range of values, returns a list of point lists.
|
||||
// v = The proportion of the way along the vertical outer list of the patch to find the point of. 0<=`v`<=1. If given as a list or range of values, returns a list of point lists.
|
||||
// Example(3D):
|
||||
// patch = [
|
||||
// [[-50, 50, 0], [-16, 50, 20], [ 16, 50, 20], [50, 50, 0]],
|
||||
|
@ -895,9 +891,26 @@ module trace_bezier(bez, N=3, size=1) {
|
|||
// [[-50,-50, 0], [-16,-50, 20], [ 16,-50, 20], [50,-50, 0]]
|
||||
// ];
|
||||
// trace_bezier_patches(patches=[patch], size=1, showcps=true);
|
||||
// pt = bezier_patch_point(patch, 0.6, 0.75);
|
||||
// pt = bezier_patch_points(patch, 0.6, 0.75);
|
||||
// translate(pt) color("magenta") sphere(d=3, $fn=12);
|
||||
function bezier_patch_point(patch, u, v) = bez_point([for (bez = patch) bez_point(bez, u)], v);
|
||||
// Example(3D): Getting Multiple Points at Once
|
||||
// patch = [
|
||||
// [[-50, 50, 0], [-16, 50, 20], [ 16, 50, 20], [50, 50, 0]],
|
||||
// [[-50, 16, 20], [-16, 16, 40], [ 16, 16, 40], [50, 16, 20]],
|
||||
// [[-50,-16, 20], [-16,-16, 40], [ 16,-16, 40], [50,-16, 20]],
|
||||
// [[-50,-50, 0], [-16,-50, 20], [ 16,-50, 20], [50,-50, 0]]
|
||||
// ];
|
||||
// trace_bezier_patches(patches=[patch], size=1, showcps=true);
|
||||
// pts = bezier_patch_points(patch, [0:0.2:1], [0:0.2:1]);
|
||||
// for (row=pts) move_copies(row) color("magenta") sphere(d=3, $fn=12);
|
||||
function bezier_patch_points(patch, u, v) =
|
||||
is_num(u) && is_num(v)? bezier_points([for (bez = patch) bezier_points(bez, u)], v) :
|
||||
assert(is_num(u) || !is_undef(u[0]))
|
||||
assert(is_num(v) || !is_undef(v[0]))
|
||||
let(
|
||||
vbezes = [for (i = idx(patch[0])) bezier_points(subindex(patch,i), is_num(u)? [u] : u)]
|
||||
)
|
||||
[for (i = idx(vbezes[0])) bezier_points(subindex(vbezes,i), is_num(v)? [v] : v)];
|
||||
|
||||
|
||||
// Function: bezier_triangle_point()
|
||||
|
@ -967,10 +980,12 @@ function is_patch(x) = is_tripatch(x) || is_rectpatch(x);
|
|||
// style = The style of subdividing the quads into faces. Valid options are "default", "alt", and "quincunx".
|
||||
// Example(3D):
|
||||
// patch = [
|
||||
// [[-50, 50, 0], [-16, 50, -20], [ 16, 50, 20], [50, 50, 0]],
|
||||
// [[-50, 16, 20], [-16, 16, -20], [ 16, 16, 20], [50, 16, 20]],
|
||||
// // u=0,v=0 u=1,v=0
|
||||
// [[-50,-50, 0], [-16,-50, 20], [ 16,-50, -20], [50,-50, 0]],
|
||||
// [[-50,-16, 20], [-16,-16, 20], [ 16,-16, -20], [50,-16, 20]],
|
||||
// [[-50,-50, 0], [-16,-50, 20], [ 16,-50, -20], [50,-50, 0]]
|
||||
// [[-50, 16, 20], [-16, 16, -20], [ 16, 16, 20], [50, 16, 20]],
|
||||
// [[-50, 50, 0], [-16, 50, -20], [ 16, 50, 20], [50, 50, 0]],
|
||||
// // u=0,v=1 u=1,v=1
|
||||
// ];
|
||||
// vnf = bezier_patch(patch, splinesteps=16);
|
||||
// vnf_polyhedron(vnf);
|
||||
|
@ -984,10 +999,12 @@ function is_patch(x) = is_tripatch(x) || is_rectpatch(x);
|
|||
// vnf_polyhedron(vnf);
|
||||
// Example(3DFlatSpin): Chaining Patches
|
||||
// patch = [
|
||||
// [[0, 0,0], [33, 0, 0], [67, 0, 0], [100, 0,0]],
|
||||
// [[0, 33,0], [33, 33, 33], [67, 33, 33], [100, 33,0]],
|
||||
// [[0, 67,0], [33, 67, 33], [67, 67, 33], [100, 67,0]],
|
||||
// [[0,100,0], [33,100, 0], [67,100, 0], [100,100,0]],
|
||||
// // u=0,v=0 u=1,v=0
|
||||
// [[0, 0,0], [33, 0, 0], [67, 0, 0], [100, 0,0]],
|
||||
// [[0, 33,0], [33, 33, 33], [67, 33, 33], [100, 33,0]],
|
||||
// [[0, 67,0], [33, 67, 33], [67, 67, 33], [100, 67,0]],
|
||||
// [[0,100,0], [33,100, 0], [67,100, 0], [100,100,0]],
|
||||
// // u=0,v=1 u=1,v=1
|
||||
// ];
|
||||
// vnf1 = bezier_patch(translate(p=patch,[-50,-50,50]));
|
||||
// vnf2 = bezier_patch(vnf=vnf1, rot(a=[90,0,0],p=translate(p=patch,[-50,-50,50])));
|
||||
|
@ -996,23 +1013,27 @@ function is_patch(x) = is_tripatch(x) || is_rectpatch(x);
|
|||
// vnf5 = bezier_patch(vnf=vnf4, rot(a=[0,90,0],p=translate(p=patch,[-50,-50,50])));
|
||||
// vnf6 = bezier_patch(vnf=vnf5, rot(a=[0,-90,0],p=translate(p=patch,[-50,-50,50])));
|
||||
// vnf_polyhedron(vnf6);
|
||||
// Example(3D): Chaining Patches with Asymmetric Splinesteps
|
||||
// Example(3D): Connecting Patches with Asymmetric Splinesteps
|
||||
// steps = 8;
|
||||
// edge_patch = [
|
||||
// // u=0, v=0 u=1,v=0
|
||||
// [[-60, 0,-40], [0, 0,-40], [60, 0,-40]],
|
||||
// [[-60, 0, 0], [0, 0, 0], [60, 0, 0]],
|
||||
// [[-60,40, 0], [0,40, 0], [60,40, 0]],
|
||||
// // u=0, v=1 u=1,v=1
|
||||
// ];
|
||||
// corner_patch = [
|
||||
// [[40, 0,-40], [ 0, 0,-40], [ 0, 40,-40]],
|
||||
// [[40, 0, 0], [ 0, 0, 0], [ 0, 40, 0]],
|
||||
// [[40, 40, 0], [40, 40, 0], [40, 40, 0]]
|
||||
// // u=0, v=0 u=1,v=0
|
||||
// [[ 0, 40,-40], [ 0, 0,-40], [40, 0,-40]],
|
||||
// [[ 0, 40, 0], [ 0, 0, 0], [40, 0, 0]],
|
||||
// [[40, 40, 0], [40, 40, 0], [40, 40, 0]],
|
||||
// // u=0, v=1 u=1,v=1
|
||||
// ];
|
||||
// face_patch = bezier_patch_flat([120,120],orient=LEFT);
|
||||
// edges = [
|
||||
// for (axrot=[[0,0,0],[0,90,0],[0,0,90]], xang=[-90:90:180])
|
||||
// bezier_patch(
|
||||
// splinesteps=[1,steps],
|
||||
// splinesteps=[steps,1],
|
||||
// rot(a=axrot,
|
||||
// p=rot(a=[xang,0,0],
|
||||
// p=translate(v=[0,-100,100],p=edge_patch)
|
||||
|
@ -1021,7 +1042,7 @@ function is_patch(x) = is_tripatch(x) || is_rectpatch(x);
|
|||
// )
|
||||
// ];
|
||||
// corners = [
|
||||
// for (zang=[0,180], xang=[-90:90:180])
|
||||
// for (xang=[0,180], zang=[-90:90:180])
|
||||
// bezier_patch(
|
||||
// splinesteps=steps,
|
||||
// rot(a=[xang,0,zang],
|
||||
|
@ -1035,7 +1056,7 @@ function is_patch(x) = is_tripatch(x) || is_rectpatch(x);
|
|||
// splinesteps=1,
|
||||
// rot(a=axrot,
|
||||
// p=rot(a=[0,0,zang],
|
||||
// p=translate(v=[-100,0,0], p=face_patch)
|
||||
// p=move([-100,0,0], p=face_patch)
|
||||
// )
|
||||
// )
|
||||
// )
|
||||
|
@ -1043,22 +1064,19 @@ function is_patch(x) = is_tripatch(x) || is_rectpatch(x);
|
|||
// vnf_polyhedron(concat(edges,corners,faces));
|
||||
function bezier_patch(patch, splinesteps=16, vnf=EMPTY_VNF, style="default") =
|
||||
assert(is_num(splinesteps) || is_vector(splinesteps,2))
|
||||
is_tripatch(patch) ? _bezier_triangle(patch, splinesteps=splinesteps, vnf=vnf) :
|
||||
is_tripatch(patch)? _bezier_triangle(patch, splinesteps=splinesteps, vnf=vnf) :
|
||||
let(
|
||||
splinesteps = is_list(splinesteps) ? splinesteps : [splinesteps,splinesteps],
|
||||
bpatch = [
|
||||
for(step=[0:1:splinesteps.x]) [
|
||||
for(patchline=patch)
|
||||
bez_point(patchline, step/splinesteps.x)
|
||||
]
|
||||
uvals = [
|
||||
for(step=[0:1:splinesteps.x])
|
||||
step/splinesteps.x
|
||||
],
|
||||
pts = [
|
||||
for(step=[0:1:splinesteps.y]) [
|
||||
for(bezparm=bpatch)
|
||||
bez_point(bezparm, step/splinesteps.y)
|
||||
]
|
||||
vvals = [
|
||||
for(step=[0:1:splinesteps.y])
|
||||
1-step/splinesteps.y
|
||||
],
|
||||
vnf = vnf_vertex_array(pts, style=style, vnf=vnf, reverse=true)
|
||||
pts = bezier_patch_points(patch, uvals, vvals),
|
||||
vnf = vnf_vertex_array(pts, style=style, vnf=vnf, reverse=false)
|
||||
) vnf;
|
||||
|
||||
|
||||
|
@ -1147,16 +1165,16 @@ function patch_reverse(patch) = [for (row=patch) reverse(row)];
|
|||
// style = The style of subdividing the quads into faces. Valid options are "default", "alt", and "quincunx".
|
||||
// Example(3D):
|
||||
// patch1 = [
|
||||
// [[18,18,0], [33, 0, 0], [ 67, 0, 0], [ 82, 18,0]],
|
||||
// [[ 0,40,0], [ 0, 0,100], [100, 0, 20], [100, 40,0]],
|
||||
// [[ 0,60,0], [ 0,100,100], [100,100, 20], [100, 60,0]],
|
||||
// [[18,82,0], [33,100, 0], [ 67,100, 0], [ 82, 82,0]],
|
||||
// [[18,18,0], [33, 0, 0], [ 67, 0, 0], [ 82, 18,0]],
|
||||
// [[ 0,40,0], [ 0, 0,100], [100, 0, 20], [100, 40,0]],
|
||||
// [[ 0,60,0], [ 0,100,100], [100,100, 20], [100, 60,0]],
|
||||
// [[18,82,0], [33,100, 0], [ 67,100, 0], [ 82, 82,0]],
|
||||
// ];
|
||||
// patch2 = [
|
||||
// [[18,18,0], [33, 0, 0], [ 67, 0, 0], [ 82, 18,0]],
|
||||
// [[ 0,40,0], [ 0, 0,-50], [100, 0,-50], [100, 40,0]],
|
||||
// [[ 0,60,0], [ 0,100,-50], [100,100,-50], [100, 60,0]],
|
||||
// [[18,82,0], [33,100, 0], [ 67,100, 0], [ 82, 82,0]],
|
||||
// [[18,82,0], [33,100, 0], [ 67,100, 0], [ 82, 82,0]],
|
||||
// [[ 0,60,0], [ 0,100,-50], [100,100,-50], [100, 60,0]],
|
||||
// [[ 0,40,0], [ 0, 0,-50], [100, 0,-50], [100, 40,0]],
|
||||
// [[18,18,0], [33, 0, 0], [ 67, 0, 0], [ 82, 18,0]],
|
||||
// ];
|
||||
// vnf = bezier_surface(patches=[patch1, patch2], splinesteps=16);
|
||||
// polyhedron(points=vnf[0], faces=vnf[1]);
|
||||
|
@ -1185,16 +1203,16 @@ function bezier_surface(patches=[], splinesteps=16, vnf=EMPTY_VNF, style="defaul
|
|||
// convexity = Max number of times a line could intersect a wall of the shape.
|
||||
// Example:
|
||||
// patch1 = [
|
||||
// [[18,18,0], [33, 0, 0], [ 67, 0, 0], [ 82, 18,0]],
|
||||
// [[ 0,40,0], [ 0, 0, 20], [100, 0, 20], [100, 40,0]],
|
||||
// [[ 0,60,0], [ 0,100, 20], [100,100,100], [100, 60,0]],
|
||||
// [[18,82,0], [33,100, 0], [ 67,100, 0], [ 82, 82,0]],
|
||||
// [[18,18,0], [33, 0, 0], [ 67, 0, 0], [ 82, 18,0]],
|
||||
// [[ 0,40,0], [ 0, 0, 20], [100, 0, 20], [100, 40,0]],
|
||||
// [[ 0,60,0], [ 0,100, 20], [100,100,100], [100, 60,0]],
|
||||
// [[18,82,0], [33,100, 0], [ 67,100, 0], [ 82, 82,0]],
|
||||
// ];
|
||||
// patch2 = [
|
||||
// [[18,18,0], [33, 0, 0], [ 67, 0, 0], [ 82, 18,0]],
|
||||
// [[ 0,40,0], [ 0, 0,-50], [100, 0,-50], [100, 40,0]],
|
||||
// [[ 0,60,0], [ 0,100,-50], [100,100,-50], [100, 60,0]],
|
||||
// [[18,82,0], [33,100, 0], [ 67,100, 0], [ 82, 82,0]],
|
||||
// [[18,82,0], [33,100, 0], [ 67,100, 0], [ 82, 82,0]],
|
||||
// [[ 0,60,0], [ 0,100,-50], [100,100,-50], [100, 60,0]],
|
||||
// [[ 0,40,0], [ 0, 0,-50], [100, 0,-50], [100, 40,0]],
|
||||
// [[18,18,0], [33, 0, 0], [ 67, 0, 0], [ 82, 18,0]],
|
||||
// ];
|
||||
// bezier_polyhedron([patch1, patch2], splinesteps=8);
|
||||
module bezier_polyhedron(patches=[], splinesteps=16, vnf=EMPTY_VNF, style="default", convexity=10)
|
||||
|
@ -1223,16 +1241,16 @@ module bezier_polyhedron(patches=[], splinesteps=16, vnf=EMPTY_VNF, style="defau
|
|||
// convexity = Max number of times a line could intersect a wall of the shape.
|
||||
// Example:
|
||||
// patch1 = [
|
||||
// [[15,15,0], [33, 0, 0], [ 67, 0, 0], [ 85, 15,0]],
|
||||
// [[ 0,33,0], [33, 33, 50], [ 67, 33, 50], [100, 33,0]],
|
||||
// [[ 0,67,0], [33, 67, 50], [ 67, 67, 50], [100, 67,0]],
|
||||
// [[15,85,0], [33,100, 0], [ 67,100, 0], [ 85, 85,0]],
|
||||
// [[15,15,0], [33, 0, 0], [ 67, 0, 0], [ 85, 15,0]],
|
||||
// [[ 0,33,0], [33, 33, 50], [ 67, 33, 50], [100, 33,0]],
|
||||
// [[ 0,67,0], [33, 67, 50], [ 67, 67, 50], [100, 67,0]],
|
||||
// [[15,85,0], [33,100, 0], [ 67,100, 0], [ 85, 85,0]],
|
||||
// ];
|
||||
// patch2 = [
|
||||
// [[15,15,0], [33, 0, 0], [ 67, 0, 0], [ 85, 15,0]],
|
||||
// [[ 0,33,0], [33, 33,-50], [ 67, 33,-50], [100, 33,0]],
|
||||
// [[ 0,67,0], [33, 67,-50], [ 67, 67,-50], [100, 67,0]],
|
||||
// [[15,85,0], [33,100, 0], [ 67,100, 0], [ 85, 85,0]],
|
||||
// [[15,85,0], [33,100, 0], [ 67,100, 0], [ 85, 85,0]],
|
||||
// [[ 0,67,0], [33, 67,-50], [ 67, 67,-50], [100, 67,0]],
|
||||
// [[ 0,33,0], [33, 33,-50], [ 67, 33,-50], [100, 33,0]],
|
||||
// [[15,15,0], [33, 0, 0], [ 67, 0, 0], [ 85, 15,0]],
|
||||
// ];
|
||||
// trace_bezier_patches(patches=[patch1, patch2], splinesteps=8, showcps=true);
|
||||
module trace_bezier_patches(patches=[], size, splinesteps=16, showcps=true, showdots=false, showpatch=true, convexity=10, style="default")
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
include <BOSL2/std.scad>
|
||||
include <BOSL2/paths.scad>
|
||||
include <BOSL2/beziers.scad>
|
||||
|
||||
|
||||
|
@ -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) {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
include <BOSL2/std.scad>
|
||||
include <BOSL2/beziers.scad>
|
||||
|
||||
//$fa=2;
|
||||
//$fs=2;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
include <BOSL2/std.scad>
|
||||
include <BOSL2/beziers.scad>
|
||||
|
||||
//$fa=2;
|
||||
//$fs=2;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BOSL_VERSION = [2,0,299];
|
||||
BOSL_VERSION = [2,0,300];
|
||||
|
||||
|
||||
// Section: BOSL Library Version Functions
|
||||
|
|
Loading…
Reference in a new issue