mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Merge pull request #496 from adrianVmariano/master
vnf_tri_array & bezier_patch_degenerate
This commit is contained in:
commit
0718bcb9be
5 changed files with 295 additions and 125 deletions
187
beziers.scad
187
beziers.scad
|
@ -1222,6 +1222,193 @@ function bezier_patch(patch, splinesteps=16, vnf=EMPTY_VNF, style="default") =
|
|||
) vnf;
|
||||
|
||||
|
||||
|
||||
// Function: bezier_patch_degenerate()
|
||||
// Usage:
|
||||
// vnf = bezier_patch_degenerate(patch, <splinesteps>, <reverse>);
|
||||
// vnf_edges = bezier_patch_degenerate(patch, <splinesteps>, <reverse>, return_edges=true);
|
||||
// Description:
|
||||
// Returns a VNF for a degenerate rectangular bezier patch where some of the corners of the patch are
|
||||
// equal. If the resulting patch has no faces then returns an empty VNF. Note that due to the degeneracy,
|
||||
// the shape of the patch can be triangular even though the actual underlying patch is a rectangle. This is
|
||||
// a different method for creating triangular bezier patches than the triangular patch.
|
||||
// If you specify return_edges then the return is a list whose first element is the vnf and whose second
|
||||
// element lists the edges in the order [left, right, top, bottom], where each list is a list of the actual
|
||||
// point values, but possibly only a single point if that edge is degenerate.
|
||||
// The method checks for various types of degeneracy and uses a triangular or partly triangular array of sample points.
|
||||
// See examples below for the types of degeneracy detected and how the patch is sampled for those cases.
|
||||
// Note that splinesteps is the same for both directions of the patch, so it cannot be an array.
|
||||
// Arguments:
|
||||
// patch = Patch to process
|
||||
// splinesteps = Number of segments to produce on each side. Default: 16
|
||||
// reverse = reverse direction of faces. Default: false
|
||||
// return_edges = if true return the points on the four edges: [left, right, top, bottom]. Default: false
|
||||
// Example: This quartic patch is degenerate at one corner, where a row of control points are equal. Processing this degenerate patch normally produces excess triangles near the degenerate point.
|
||||
// splinesteps=8;
|
||||
// patch=[
|
||||
// repeat([-12.5, 12.5, 15],5),
|
||||
// [[-6.25, 11.25, 15], [-6.25, 8.75, 15], [-6.25, 6.25, 15], [-8.75, 6.25, 15], [-11.25, 6.25, 15]],
|
||||
// [[0, 10, 15], [0, 5, 15], [0, 0, 15], [-5, 0, 15], [-10, 0, 15]],
|
||||
// [[0, 10, 8.75], [0, 5, 8.75], [0, 0, 8.75], [-5, 0, 8.75], [-10, 0, 8.75]],
|
||||
// [[0, 10, 2.5], [0, 5, 2.5], [0, 0, 2.5], [-5, 0, 2.5], [-10, 0, 2.5]]
|
||||
// ];
|
||||
// vnf_wireframe((bezier_patch(patch, splinesteps)),d=0.1);
|
||||
// color("red")move_copies(flatten(patch)) sphere(r=0.3,$fn=9);
|
||||
// Example: With bezier_patch_degenerate the degenerate point does not have excess triangles. The top half of the patch decreases the number of sampled points by 2 for each row.
|
||||
// splinesteps=8;
|
||||
// patch=[
|
||||
// repeat([-12.5, 12.5, 15],5),
|
||||
// [[-6.25, 11.25, 15], [-6.25, 8.75, 15], [-6.25, 6.25, 15], [-8.75, 6.25, 15], [-11.25, 6.25, 15]],
|
||||
// [[0, 10, 15], [0, 5, 15], [0, 0, 15], [-5, 0, 15], [-10, 0, 15]],
|
||||
// [[0, 10, 8.75], [0, 5, 8.75], [0, 0, 8.75], [-5, 0, 8.75], [-10, 0, 8.75]],
|
||||
// [[0, 10, 2.5], [0, 5, 2.5], [0, 0, 2.5], [-5, 0, 2.5], [-10, 0, 2.5]]
|
||||
// ];
|
||||
// vnf_wireframe(bezier_patch_degenerate(patch, splinesteps),d=0.1);
|
||||
// color("red")move_copies(flatten(patch)) sphere(r=0.3,$fn=9);
|
||||
// Example: With splinesteps odd you get one "odd" row where the point count decreases by 1 instead of 2. You may prefer even values for splinesteps to avoid this.
|
||||
// splinesteps=7;
|
||||
// patch=[
|
||||
// repeat([-12.5, 12.5, 15],5),
|
||||
// [[-6.25, 11.25, 15], [-6.25, 8.75, 15], [-6.25, 6.25, 15], [-8.75, 6.25, 15], [-11.25, 6.25, 15]],
|
||||
// [[0, 10, 15], [0, 5, 15], [0, 0, 15], [-5, 0, 15], [-10, 0, 15]],
|
||||
// [[0, 10, 8.75], [0, 5, 8.75], [0, 0, 8.75], [-5, 0, 8.75], [-10, 0, 8.75]],
|
||||
// [[0, 10, 2.5], [0, 5, 2.5], [0, 0, 2.5], [-5, 0, 2.5], [-10, 0, 2.5]]
|
||||
// ];
|
||||
// vnf_wireframe(bezier_patch_degenerate(patch, splinesteps),d=0.1);
|
||||
// color("red")move_copies(flatten(patch)) sphere(r=0.3,$fn=9);
|
||||
// Example: A more extreme degeneracy occurs when the top half of a patch is degenerate to a line. (For odd length patches the middle row must be degenerate to trigger this style.) In this case the number of points in each row decreases by 1 for every row. It doesn't matter of splinesteps is odd or even.
|
||||
// splinesteps=8;
|
||||
// patch = [[[10, 0, 0], [10, -10.4, 0], [10, -20.8, 0], [1.876, -14.30, 0], [-6.24, -7.8, 0]],
|
||||
// [[5, 0, 0], [5, -5.2, 0], [5, -10.4, 0], [0.938, -7.15, 0], [-3.12, -3.9, 0]],
|
||||
// repeat([0,0,0],5),
|
||||
// repeat([0,0,5],5),
|
||||
// repeat([0,0,10],5)
|
||||
// ];
|
||||
// vnf_wireframe(bezier_patch_degenerate(patch, splinesteps),d=0.1);
|
||||
// color("red")move_copies(flatten(patch)) sphere(r=0.3,$fn=9);
|
||||
// Example: Here is a degenerate cubic patch.
|
||||
// splinesteps=8;
|
||||
// patch = [ [ [-20,0,0], [-10,0,0],[0,10,0],[0,20,0] ],
|
||||
// [ [-20,0,10], [-10,0,10],[0,10,10],[0,20,10]],
|
||||
// [ [-10,0,20], [-5,0,20], [0,5,20], [0,10,20]],
|
||||
// repeat([0,0,30],4)
|
||||
// ];
|
||||
// color("red")move_copies(flatten(patch)) sphere(r=0.3,$fn=9);
|
||||
// vnf_wireframe(bezier_patch_degenerate(patch, splinesteps),d=0.1);
|
||||
// Example: A more extreme degenerate cubic patch, where two rows are equal.
|
||||
// splinesteps=8;
|
||||
// patch = [ [ [-20,0,0], [-10,0,0],[0,10,0],[0,20,0] ],
|
||||
// [ [-20,0,10], [-10,0,10],[0,10,10],[0,20,10] ],
|
||||
// repeat([-10,10,20],4),
|
||||
// repeat([-10,10,30],4)
|
||||
// ];
|
||||
// color("red")move_copies(flatten(patch)) sphere(r=0.3,$fn=9);
|
||||
// vnf_wireframe(bezier_patch_degenerate(patch, splinesteps),d=0.1);
|
||||
// Example: Quadratic patch degenerate at the right side:
|
||||
// splinesteps=8;
|
||||
// patch = [[[0, -10, 0],[10, -5, 0],[20, 0, 0]],
|
||||
// [[0, 0, 0], [10, 0, 0], [20, 0, 0]],
|
||||
// [[0, 0, 10], [10, 0, 5], [20, 0, 0]]];
|
||||
// vnf_wireframe(bezier_patch_degenerate(patch, splinesteps),d=0.1);
|
||||
// color("red")move_copies(flatten(patch)) sphere(r=0.3,$fn=9);
|
||||
// Example: Cubic patch degenerate at both ends. In this case the point count changes by 2 at every row.
|
||||
// splinesteps=8;
|
||||
// patch = [
|
||||
// repeat([10,-10,0],4),
|
||||
// [ [-20,0,0], [-1,0,0],[0,10,0],[0,20,0] ],
|
||||
// [ [-20,0,10], [-10,0,10],[0,10,10],[0,20,10] ],
|
||||
// repeat([-10,10,20],4),
|
||||
// ];
|
||||
// vnf_wireframe(bezier_patch_degenerate(patch, splinesteps),d=0.1);
|
||||
// color("red")move_copies(flatten(patch)) sphere(r=0.3,$fn=9);
|
||||
function bezier_patch_degenerate(patch, splinesteps=16, reverse=false, return_edges=false) =
|
||||
!return_edges ? bezier_patch_degenerate(patch, splinesteps, reverse, true)[0] :
|
||||
assert(is_rectpatch(patch), "Must supply rectangular bezier patch")
|
||||
assert(is_int(splinesteps) && splinesteps>=3, "splinesteps must be an integer 3 or larger")
|
||||
let(
|
||||
row_degen = [for(row=patch) all_equal(row)],
|
||||
col_degen = [for(col=transpose(patch)) all_equal(col)],
|
||||
|
||||
top_degen = row_degen[0],
|
||||
bot_degen = last(row_degen),
|
||||
left_degen = col_degen[0],
|
||||
right_degen = last(col_degen),
|
||||
samplepts = lerpn(0,1,splinesteps+1)
|
||||
)
|
||||
all(row_degen) && all(col_degen) ? // fully degenerate case
|
||||
[EMPTY_VNF, repeat([patch[0][0]],4)] :
|
||||
all(row_degen) ? // degenerate to a line (top to bottom)
|
||||
let(pts = bezier_points(subindex(patch,0), samplepts))
|
||||
[EMPTY_VNF, [pts,pts,[pts[0]],[last(pts)]]] :
|
||||
all(col_degen) ? // degenerate to a line (left to right)
|
||||
let(pts = bezier_points(patch[0], samplepts))
|
||||
[EMPTY_VNF, [[pts[0]], [last(pts)], pts, pts]] :
|
||||
!top_degen && !bot_degen && !left_degen && !right_degen ? // non-degenerate case
|
||||
let(pts = bezier_patch_points(patch, samplepts, samplepts))
|
||||
[
|
||||
vnf_vertex_array(pts, reverse=!reverse),
|
||||
[subindex(pts,0), subindex(pts,len(pts)-1), pts[0], last(pts)]
|
||||
] :
|
||||
top_degen && bot_degen ?
|
||||
let(
|
||||
rowcount = [
|
||||
each list([3:2:splinesteps]),
|
||||
if (splinesteps%2==0) splinesteps+1,
|
||||
each reverse(list([3:2:splinesteps]))
|
||||
],
|
||||
bpatch = [for(i=[0:1:len(patch[0])-1]) bezier_points(subindex(patch,i), samplepts)],
|
||||
pts = [
|
||||
[bpatch[0][0]],
|
||||
for(j=[0:splinesteps-2]) bezier_points(subindex(bpatch,j+1), lerpn(0,1,rowcount[j])),
|
||||
[last(bpatch[0])]
|
||||
],
|
||||
vnf = vnf_tri_array(pts, reverse=reverse)
|
||||
) [
|
||||
vnf,
|
||||
[
|
||||
subindex(pts,0),
|
||||
[for(row=pts) last(row)],
|
||||
pts[0],
|
||||
last(pts),
|
||||
]
|
||||
] :
|
||||
bot_degen ? // only bottom is degenerate
|
||||
let(
|
||||
result = bezier_patch_degenerate(reverse(patch), splinesteps=splinesteps, reverse=!reverse, return_edges=true)
|
||||
)
|
||||
[
|
||||
result[0],
|
||||
[reverse(result[1][0]), reverse(result[1][1]), (result[1][3]), (result[1][2])]
|
||||
] :
|
||||
top_degen ? // only top is degenerate
|
||||
let(
|
||||
full_degen = len(patch)>=4 && all(select(row_degen,1,ceil(len(patch)/2-1))),
|
||||
rowmax = full_degen ? count(splinesteps+1) :
|
||||
[for(j=[0:splinesteps]) j<=splinesteps/2 ? 2*j : splinesteps],
|
||||
bpatch = [for(i=[0:1:len(patch[0])-1]) bezier_points(subindex(patch,i), samplepts)],
|
||||
pts = [
|
||||
[bpatch[0][0]],
|
||||
for(j=[1:splinesteps]) bezier_points(subindex(bpatch,j), lerpn(0,1,rowmax[j]+1))
|
||||
],
|
||||
vnf = vnf_tri_array(pts, reverse=reverse)
|
||||
) [
|
||||
vnf,
|
||||
[
|
||||
subindex(pts,0),
|
||||
[for(row=pts) last(row)],
|
||||
pts[0],
|
||||
last(pts),
|
||||
]
|
||||
] :
|
||||
// must have left or right degeneracy, so transpose and recurse
|
||||
let(
|
||||
result = bezier_patch_degenerate(transpose(patch), splinesteps=splinesteps, reverse=!reverse, return_edges=true)
|
||||
)
|
||||
[result[0],
|
||||
select(result[1],[2,3,0,1])
|
||||
];
|
||||
|
||||
|
||||
function _tri_count(n) = (n*(1+n))/2;
|
||||
|
||||
|
||||
|
|
|
@ -1904,10 +1904,10 @@ function reindex_polygon(reference, poly, return_error=false) =
|
|||
polygon_is_clockwise(reference)
|
||||
? clockwise_polygon(poly)
|
||||
: ccw_polygon(poly),
|
||||
I = [for(i=[0:N-1]) 1],
|
||||
I = [for(i=reference) 1],
|
||||
val = [ for(k=[0:N-1])
|
||||
[for(i=[0:N-1])
|
||||
(reference[i]*poly[(i+k)%N]) ] ]*I,
|
||||
[for(i=[0:N-1])
|
||||
(reference[i]*poly[(i+k)%N]) ] ]*I,
|
||||
optimal_poly = polygon_shift(fixpoly, max_index(val))
|
||||
)
|
||||
return_error? [optimal_poly, min(poly*(I*poly)-2*val)] :
|
||||
|
|
14
math.scad
14
math.scad
|
@ -583,7 +583,7 @@ function lcm(a,b=[]) =
|
|||
function sum(v, dflt=0) =
|
||||
v==[]? dflt :
|
||||
assert(is_consistent(v), "Input to sum is non-numeric or inconsistent")
|
||||
is_vector(v) || is_matrix(v) ? [for(i=[1:len(v)]) 1]*v :
|
||||
is_vector(v) || is_matrix(v) ? [for(i=v) 1]*v :
|
||||
_sum(v,v[0]*0);
|
||||
|
||||
function _sum(v,_total,_i=0) = _i>=len(v) ? _total : _sum(v,_total+v[_i], _i+1);
|
||||
|
@ -1153,6 +1153,18 @@ function all_nonnegative(x) =
|
|||
false;
|
||||
|
||||
|
||||
// Function all_equal()
|
||||
// Usage:
|
||||
// b = all_equal(vec,<eps>);
|
||||
// Description:
|
||||
// Returns true if all of the entries in vec are equal to each other, or approximately equal to each other if eps is set.
|
||||
// Arguments:
|
||||
// vec = vector to check
|
||||
// eps = Set to tolerance for approximate equality. Default: 0
|
||||
function all_equal(vec,eps=0) =
|
||||
eps==0 ? [for(v=vec) if (v!=vec[0]) v] == []
|
||||
: [for(v=vec) if (!approx(v,vec[0])) v] == [];
|
||||
|
||||
// Function: approx()
|
||||
// Usage:
|
||||
// b = approx(a,b,<eps>)
|
||||
|
|
132
rounding.scad
132
rounding.scad
|
@ -1779,8 +1779,8 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) =
|
|||
// M = path3d(turtle(["left", 180, "length",3,"move", "left", "move", 3, "right", "move", "right", "move", 4, "right", "move", 3, "right", "move", 2]));
|
||||
// rounded_prism(M, apply(right(1)*scale(.75)*up(3),M), joint_top=0.5, joint_bot=0.2, joint_sides=[.2,1,1,0.5,1.5,.5,2], splinesteps=32);
|
||||
// Example: this example shows most of the different types of patches that rounded_prism creates. Note that some of the patches are close to interfering with each other across the top of the polyhedron, which would create an invalid result.
|
||||
// N = apply(rot(180)*yscale(.8),turtle(["length",3,"left", "move", 2, "right", 135, "move", sqrt(2), "left", "move", sqrt(2), "right", 135, "move", 2]));
|
||||
// rounded_prism(N, height=3, joint_bot=0.5, joint_top=1.25, joint_sides=[[1,1.75],0,.5,.5,2], debug=true);
|
||||
N = apply(rot(180)*yscale(.8),turtle(["length",3,"left", "move", 2, "right", 135, "move", sqrt(2), "left", "move", sqrt(2), "right", 135, "move", 2]));
|
||||
rounded_prism(N, height=3, joint_bot=0.5, joint_top=1.25, joint_sides=[[1,1.75],0,.5,.5,2], debug=true);
|
||||
// Example: This object has different scales on its different axies. Here is the largest symmetric rounding that fits. Note that the rounding is slightly smaller than the object dimensions because of roundoff error.
|
||||
// rounded_prism(square([100.1,30.1]), height=8.1, joint_top=4, joint_bot=4, joint_sides=15, k_sides=0.3, splinesteps=32);
|
||||
// Example: Using asymetric rounding enables a much more rounded form:
|
||||
|
@ -1886,8 +1886,8 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b
|
|||
let(
|
||||
// Entries in the next two lists have the form [edges, vnf] where
|
||||
// edges is a list [leftedge, rightedge, topedge, botedge]
|
||||
top_samples = [for(patch=top_patch) bezier_patch_degenerate(patch,splinesteps,reverse=true) ],
|
||||
bot_samples = [for(patch=bot_patch) bezier_patch_degenerate(patch,splinesteps,reverse=false) ],
|
||||
top_samples = [for(patch=top_patch) bezier_patch_degenerate(patch,splinesteps,reverse=true,return_edges=true) ],
|
||||
bot_samples = [for(patch=bot_patch) bezier_patch_degenerate(patch,splinesteps,reverse=false,return_edges=true) ],
|
||||
leftidx=0,
|
||||
rightidx=1,
|
||||
topidx=2,
|
||||
|
@ -1895,14 +1895,14 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b
|
|||
edge_points =
|
||||
[for(i=[0:N-1])
|
||||
let(
|
||||
top_edge = [ top_samples[i][0][rightidx], select(top_samples, i+1)[0][leftidx]],
|
||||
bot_edge = [ select(bot_samples, i+1)[0][leftidx], bot_samples[i][0][rightidx]],
|
||||
vert_edge = [ bot_samples[i][0][botidx], top_samples[i][0][botidx]]
|
||||
top_edge = [ top_samples[i][1][rightidx], select(top_samples, i+1)[1][leftidx]],
|
||||
bot_edge = [ select(bot_samples, i+1)[1][leftidx], bot_samples[i][1][rightidx]],
|
||||
vert_edge = [ bot_samples[i][1][botidx], top_samples[i][1][botidx]]
|
||||
)
|
||||
each [top_edge, bot_edge, vert_edge] ],
|
||||
faces = [
|
||||
[for(i=[0:N-1]) each top_samples[i][0][topidx]],
|
||||
[for(i=[N-1:-1:0]) each reverse(bot_samples[i][0][topidx])],
|
||||
[for(i=[0:N-1]) each top_samples[i][1][topidx]],
|
||||
[for(i=[N-1:-1:0]) each reverse(bot_samples[i][1][topidx])],
|
||||
for(i=[0:N-1]) [
|
||||
bot_patch[i][4][4],
|
||||
select(bot_patch,i+1)[4][0],
|
||||
|
@ -1934,8 +1934,8 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b
|
|||
"Roundovers interfere with each other on bottom face: either input is self intersecting or top joint length is too large")
|
||||
assert(debug || (verify_vert==[] && verify_horiz==[]), "Curvature continuity failed")
|
||||
let(
|
||||
vnf = vnf_merge([ each subindex(top_samples,1),
|
||||
each subindex(bot_samples,1),
|
||||
vnf = vnf_merge([ each subindex(top_samples,0),
|
||||
each subindex(bot_samples,0),
|
||||
for(pts=edge_points) vnf_vertex_array(pts),
|
||||
vnf_triangulate(vnf_add_faces(EMPTY_VNF,faces))
|
||||
])
|
||||
|
@ -1943,116 +1943,6 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b
|
|||
debug ? [concat(top_patch, bot_patch), vnf] : vnf;
|
||||
|
||||
|
||||
// This function takes a bezier patch as input and returns [edges, vnf], where
|
||||
// edges = [leftedge, rightedge, topedge, bottomedge]
|
||||
// gives the points along the edges of the patch, and the vnf is the patch vnf.
|
||||
// It checks for various types of degeneracy and uses half or full triangular
|
||||
// sampling on degenerate patches.
|
||||
|
||||
function bezier_patch_degenerate(patch, splinesteps=16, reverse=false) =
|
||||
assert(is_num(splinesteps), "splinesteps must be a number")
|
||||
let(
|
||||
top_degen = patch[0][0] == last(patch[0]),
|
||||
bot_degen = last(patch)[0] == last(last(patch)),
|
||||
left_degen = patch[0][0] == last(patch)[0],
|
||||
right_degen = last(patch[0]) == last(last(patch)),
|
||||
samplepts = count(splinesteps+1)/splinesteps
|
||||
)
|
||||
top_degen && bot_degen && left_degen && right_degen ? // fully degenerate case
|
||||
[repeat([patch[0][0]],4), EMPTY_VNF] :
|
||||
top_degen && bot_degen ? // double degenerate case (top/bot)
|
||||
let(
|
||||
pts = bezier_points(subindex(patch,0), samplepts)
|
||||
)
|
||||
[[pts,pts,[pts[0]],[last(pts)]], EMPTY_VNF] :
|
||||
left_degen && right_degen ? // double degenerate case (sides)
|
||||
let(
|
||||
pts = bezier_points(patch[0], samplepts)
|
||||
)
|
||||
[[[pts[0]], [last(pts)], pts, pts], EMPTY_VNF] :
|
||||
!top_degen && !bot_degen ? // non-degenerate case
|
||||
let(
|
||||
k=echo("non-degenerate case"),
|
||||
pts = bezier_patch_points(patch, samplepts, samplepts)
|
||||
)
|
||||
[
|
||||
[subindex(pts,0), subindex(pts,len(pts)-1), pts[0], last(pts)],
|
||||
vnf_vertex_array(pts, reverse=reverse)
|
||||
] :
|
||||
bot_degen ? // only bottom is degenerate
|
||||
let(
|
||||
result = bezier_patch_degenerate(reverse(patch), splinesteps=splinesteps, reverse=!reverse)
|
||||
)
|
||||
[
|
||||
[reverse(result[0][0]), reverse(result[0][1]), (result[0][3]), (result[0][2])],
|
||||
result[1]
|
||||
] :
|
||||
// at this point top_degen is true // only top is degenerate
|
||||
let(
|
||||
full_degen = patch[1][0] == last(patch[1]),
|
||||
rowmax = full_degen ? count(splinesteps+1) :
|
||||
[for(j=[0:splinesteps]) j<=splinesteps/2 ? 2*j : splinesteps],
|
||||
vbb=echo("single degenerate case"),
|
||||
bpatch = [for(i=[0:1:len(patch[0])-1]) bezier_points(subindex(patch,i), samplepts)],
|
||||
pts = [
|
||||
[bpatch[0][0]],
|
||||
for(j=[1:splinesteps]) bezier_points(subindex(bpatch,j), lerpn(0,1,rowmax[j]+1))
|
||||
],
|
||||
vnf = vnf_tri_array(pts, reverse=reverse)
|
||||
) [
|
||||
[
|
||||
subindex(pts,0),
|
||||
[for(row=pts) last(row)],
|
||||
pts[0],
|
||||
last(pts),
|
||||
],
|
||||
vnf
|
||||
];
|
||||
|
||||
|
||||
// This function produces a vnf with a triangulation for a list of rows
|
||||
// where the number of points between rows differs by at most 2.
|
||||
// It's a generalization of vnf_vertex_array.
|
||||
function vnf_tri_array(points, row_wrap=false, reverse=false) =
|
||||
let(
|
||||
lens = [for(row=points) len(row)],
|
||||
rowstarts = [0,each cumsum(lens)],
|
||||
faces =
|
||||
[for(i=[0:1:len(points) - 1 - (row_wrap ? 0 : 1)]) each
|
||||
let(
|
||||
rowstart = rowstarts[i],
|
||||
nextrow = select(rowstarts,i+1),
|
||||
delta = select(lens,i+1)-lens[i]
|
||||
)
|
||||
delta == 0 ?
|
||||
[for(j=[0:1:lens[i]-2]) reverse ? [j+rowstart+1, j+rowstart, j+nextrow] : [j+rowstart, j+rowstart+1, j+nextrow],
|
||||
for(j=[0:1:lens[i]-2]) reverse ? [j+rowstart+1, j+nextrow, j+nextrow+1] : [j+rowstart+1, j+nextrow+1, j+nextrow]] :
|
||||
delta == 1 ?
|
||||
[for(j=[0:1:lens[i]-2]) reverse ? [j+rowstart+1, j+rowstart, j+nextrow+1] : [j+rowstart, j+rowstart+1, j+nextrow+1],
|
||||
for(j=[0:1:lens[i]-1]) reverse ? [j+rowstart, j+nextrow, j+nextrow+1] : [j+rowstart, j+nextrow+1, j+nextrow]] :
|
||||
delta == -1 ?
|
||||
[for(j=[0:1:lens[i]-3]) reverse ? [j+rowstart+1, j+nextrow, j+nextrow+1]: [j+rowstart+1, j+nextrow+1, j+nextrow],
|
||||
for(j=[0:1:lens[i]-2]) reverse ? [j+rowstart+1, j+rowstart, j+nextrow] : [j+rowstart, j+rowstart+1, j+nextrow]] :
|
||||
let(count = floor((lens[i]-1)/2))
|
||||
delta == 2 ?
|
||||
[
|
||||
for(j=[0:1:count-1]) reverse ? [j+rowstart+1, j+rowstart, j+nextrow+1] : [j+rowstart, j+rowstart+1, j+nextrow+1], // top triangles left
|
||||
for(j=[count:1:lens[i]-2]) reverse ? [j+rowstart+1, j+rowstart, j+nextrow+2] : [j+rowstart, j+rowstart+1, j+nextrow+2], // top triangles right
|
||||
for(j=[0:1:count]) reverse ? [j+rowstart, j+nextrow, j+nextrow+1] : [j+rowstart, j+nextrow+1, j+nextrow], // bot triangles left
|
||||
for(j=[count+1:1:select(lens,i+1)-2]) reverse ? [j+rowstart-1, j+nextrow, j+nextrow+1] : [j+rowstart-1, j+nextrow+1, j+nextrow], // bot triangles right
|
||||
] :
|
||||
delta == -2 ?
|
||||
[
|
||||
for(j=[0:1:count-2]) reverse ? [j+nextrow, j+nextrow+1, j+rowstart+1] : [j+nextrow, j+rowstart+1, j+nextrow+1],
|
||||
for(j=[count-1:1:lens[i]-4]) reverse ? [j+nextrow,j+nextrow+1,j+rowstart+2] : [j+nextrow,j+rowstart+2, j+nextrow+1],
|
||||
for(j=[0:1:count-1]) reverse ? [j+nextrow, j+rowstart+1, j+rowstart] : [j+nextrow, j+rowstart, j+rowstart+1],
|
||||
for(j=[count:1:select(lens,i+1)]) reverse ? [ j+nextrow-1, j+rowstart+1, j+rowstart]: [ j+nextrow-1, j+rowstart, j+rowstart+1],
|
||||
] :
|
||||
assert(false,str("Unsupported row length difference of ",delta, " between row ",i," and ",i+1))
|
||||
])
|
||||
[flatten(points), faces];
|
||||
|
||||
|
||||
|
||||
// Converts a 2d path to a path on a cylinder at radius r
|
||||
function _cyl_hole(r, path) =
|
||||
|
|
81
vnf.scad
81
vnf.scad
|
@ -367,6 +367,87 @@ function vnf_vertex_array(
|
|||
]);
|
||||
|
||||
|
||||
// Function: vnf_tri_array()
|
||||
// Usage:
|
||||
// vnf = vnf_tri_array(points, <row_wrap>, <reverse>)
|
||||
// Description:
|
||||
// Produces a vnf from an array of points where each row length can differ from the adjacent rows by up to 2 in length. This enables
|
||||
// the construction of triangular VNF patches. The resulting VNF can be wrapped along the rows by setting `row_wrap` to true.
|
||||
// Arguments:
|
||||
// points = List of point lists for each row
|
||||
// row_wrap = If true then add faces connecting the first row and last row. These rows must differ by at most 2 in length.
|
||||
// reverse = Set this to reverse the direction of the faces
|
||||
// Examples: Each row has one more point than the preceeding one.
|
||||
// pts = [for(y=[1:1:10]) [for(x=[0:y-1]) [x,y,y]]];
|
||||
// vnf = vnf_tri_array(pts);
|
||||
// vnf_wireframe(vnf,d=.1);
|
||||
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
|
||||
// Examples: Each row has one more point than the preceeding one.
|
||||
// pts = [for(y=[0:2:10]) [for(x=[-y/2:y/2]) [x,y,y]]];
|
||||
// vnf = vnf_tri_array(pts);
|
||||
// vnf_wireframe(vnf,d=.1);
|
||||
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
|
||||
// Example: Chaining two VNFs to construct a cone with one point length change between rows.
|
||||
// pts1 = [for(z=[0:10]) path3d(arc(3+z,r=z/2+1, angle=[0,180]),10-z)];
|
||||
// pts2 = [for(z=[0:10]) path3d(arc(3+z,r=z/2+1, angle=[180,360]),10-z)];
|
||||
// vnf = vnf_tri_array(pts1,
|
||||
// vnf=vnf_tri_array(pts2));
|
||||
// color("green")vnf_wireframe(vnf,d=.1);
|
||||
// vnf_polyhedron(vnf);
|
||||
// Example: Cone with length change two between rows
|
||||
// pts1 = [for(z=[0:1:10]) path3d(arc(3+2*z,r=z/2+1, angle=[0,180]),10-z)];
|
||||
// pts2 = [for(z=[0:1:10]) path3d(arc(3+2*z,r=z/2+1, angle=[180,360]),10-z)];
|
||||
// vnf = vnf_tri_array(pts1,
|
||||
// vnf=vnf_tri_array(pts2));
|
||||
// color("green")vnf_wireframe(vnf,d=.1);
|
||||
// vnf_polyhedron(vnf);
|
||||
// Example: Point count can change irregularly
|
||||
// lens = [10,9,7,5,6,8,8,10];
|
||||
// pts = [for(y=idx(lens)) lerpn([-lens[y],y,y],[lens[y],y,y],lens[y])];
|
||||
// vnf = vnf_tri_array(pts);
|
||||
// vnf_wireframe(vnf,d=.1);
|
||||
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
|
||||
function vnf_tri_array(points, row_wrap=false, reverse=false, vnf=EMPTY_VNF) =
|
||||
let(
|
||||
lens = [for(row=points) len(row)],
|
||||
rowstarts = [0,each cumsum(lens)],
|
||||
faces =
|
||||
[for(i=[0:1:len(points) - 1 - (row_wrap ? 0 : 1)]) each
|
||||
let(
|
||||
rowstart = rowstarts[i],
|
||||
nextrow = select(rowstarts,i+1),
|
||||
delta = select(lens,i+1)-lens[i]
|
||||
)
|
||||
delta == 0 ?
|
||||
[for(j=[0:1:lens[i]-2]) reverse ? [j+rowstart+1, j+rowstart, j+nextrow] : [j+rowstart, j+rowstart+1, j+nextrow],
|
||||
for(j=[0:1:lens[i]-2]) reverse ? [j+rowstart+1, j+nextrow, j+nextrow+1] : [j+rowstart+1, j+nextrow+1, j+nextrow]] :
|
||||
delta == 1 ?
|
||||
[for(j=[0:1:lens[i]-2]) reverse ? [j+rowstart+1, j+rowstart, j+nextrow+1] : [j+rowstart, j+rowstart+1, j+nextrow+1],
|
||||
for(j=[0:1:lens[i]-1]) reverse ? [j+rowstart, j+nextrow, j+nextrow+1] : [j+rowstart, j+nextrow+1, j+nextrow]] :
|
||||
delta == -1 ?
|
||||
[for(j=[0:1:lens[i]-3]) reverse ? [j+rowstart+1, j+nextrow, j+nextrow+1]: [j+rowstart+1, j+nextrow+1, j+nextrow],
|
||||
for(j=[0:1:lens[i]-2]) reverse ? [j+rowstart+1, j+rowstart, j+nextrow] : [j+rowstart, j+rowstart+1, j+nextrow]] :
|
||||
let(count = floor((lens[i]-1)/2))
|
||||
delta == 2 ?
|
||||
[
|
||||
for(j=[0:1:count-1]) reverse ? [j+rowstart+1, j+rowstart, j+nextrow+1] : [j+rowstart, j+rowstart+1, j+nextrow+1], // top triangles left
|
||||
for(j=[count:1:lens[i]-2]) reverse ? [j+rowstart+1, j+rowstart, j+nextrow+2] : [j+rowstart, j+rowstart+1, j+nextrow+2], // top triangles right
|
||||
for(j=[0:1:count]) reverse ? [j+rowstart, j+nextrow, j+nextrow+1] : [j+rowstart, j+nextrow+1, j+nextrow], // bot triangles left
|
||||
for(j=[count+1:1:select(lens,i+1)-2]) reverse ? [j+rowstart-1, j+nextrow, j+nextrow+1] : [j+rowstart-1, j+nextrow+1, j+nextrow], // bot triangles right
|
||||
] :
|
||||
delta == -2 ?
|
||||
[
|
||||
for(j=[0:1:count-2]) reverse ? [j+nextrow, j+nextrow+1, j+rowstart+1] : [j+nextrow, j+rowstart+1, j+nextrow+1],
|
||||
for(j=[count-1:1:lens[i]-4]) reverse ? [j+nextrow,j+nextrow+1,j+rowstart+2] : [j+nextrow,j+rowstart+2, j+nextrow+1],
|
||||
for(j=[0:1:count-1]) reverse ? [j+nextrow, j+rowstart+1, j+rowstart] : [j+nextrow, j+rowstart, j+rowstart+1],
|
||||
for(j=[count:1:select(lens,i+1)]) reverse ? [ j+nextrow-1, j+rowstart+1, j+rowstart]: [ j+nextrow-1, j+rowstart, j+rowstart+1],
|
||||
] :
|
||||
assert(false,str("Unsupported row length difference of ",delta, " between row ",i," and ",(i+1)%len(points)))
|
||||
])
|
||||
vnf_merge(cleanup=true, [vnf, [flatten(points), faces]]);
|
||||
|
||||
|
||||
|
||||
// Module: vnf_polyhedron()
|
||||
// Usage:
|
||||
// vnf_polyhedron(vnf);
|
||||
|
|
Loading…
Reference in a new issue