mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-09-09 04:00:48 +02:00
Standardize indention on spaces, not tabs.
This commit is contained in:
788
vnf.scad
788
vnf.scad
@@ -29,12 +29,12 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
|
||||
// Description:
|
||||
// Returns true if the given value looks like a VNF structure.
|
||||
function is_vnf(x) =
|
||||
is_list(x) &&
|
||||
len(x)==2 &&
|
||||
is_list(x[0]) &&
|
||||
is_list(x[1]) &&
|
||||
(x[0]==[] || (len(x[0])>=3 && is_vector(x[0][0]))) &&
|
||||
(x[1]==[] || is_vector(x[1][0]));
|
||||
is_list(x) &&
|
||||
len(x)==2 &&
|
||||
is_list(x[0]) &&
|
||||
is_list(x[1]) &&
|
||||
(x[0]==[] || (len(x[0])>=3 && is_vector(x[0][0]))) &&
|
||||
(x[1]==[] || is_vector(x[1][0]));
|
||||
|
||||
|
||||
// Function: is_vnf_list()
|
||||
@@ -61,7 +61,7 @@ function vnf_faces(vnf) = vnf[1];
|
||||
// vnf = The VNF to quantize.
|
||||
// q = The quanta to quantize the VNF coordinates to.
|
||||
function vnf_quantize(vnf,q=pow(2,-12)) =
|
||||
[[for (pt = vnf[0]) quant(pt,q)], vnf[1]];
|
||||
[[for (pt = vnf[0]) quant(pt,q)], vnf[1]];
|
||||
|
||||
|
||||
// Function: vnf_get_vertex()
|
||||
@@ -81,11 +81,11 @@ function vnf_quantize(vnf,q=pow(2,-12)) =
|
||||
// vnf3 = vnf_get_vertex(vnf2, p=[3,5,8]); // Returns: [0, [[[3,5,8],[3,2,1]],[]]]
|
||||
// vnf4 = vnf_get_vertex(vnf3, p=[[1,3,2],[3,2,1]]); // Returns: [[1,2], [[[3,5,8],[3,2,1],[1,3,2]],[]]]
|
||||
function vnf_get_vertex(vnf=EMPTY_VNF, p) =
|
||||
let(
|
||||
p = is_vector(p)? [p] : p,
|
||||
res = set_union(vnf[0], p, get_indices=true)
|
||||
)
|
||||
[res[0], [res[1],vnf[1]]];
|
||||
let(
|
||||
p = is_vector(p)? [p] : p,
|
||||
res = set_union(vnf[0], p, get_indices=true)
|
||||
)
|
||||
[res[0], [res[1],vnf[1]]];
|
||||
|
||||
|
||||
// Function: vnf_add_face()
|
||||
@@ -99,15 +99,15 @@ function vnf_get_vertex(vnf=EMPTY_VNF, p) =
|
||||
// vnf = The VNF structure to add a face to.
|
||||
// pts = The vertex points for the face.
|
||||
function vnf_add_face(vnf=EMPTY_VNF, pts) =
|
||||
assert(is_vnf(vnf))
|
||||
assert(is_path(pts))
|
||||
let(
|
||||
res = set_union(vnf[0], pts, get_indices=true),
|
||||
face = deduplicate(res[0], closed=true)
|
||||
) [
|
||||
res[1],
|
||||
concat(vnf[1], len(face)>2? [face] : [])
|
||||
];
|
||||
assert(is_vnf(vnf))
|
||||
assert(is_path(pts))
|
||||
let(
|
||||
res = set_union(vnf[0], pts, get_indices=true),
|
||||
face = deduplicate(res[0], closed=true)
|
||||
) [
|
||||
res[1],
|
||||
concat(vnf[1], len(face)>2? [face] : [])
|
||||
];
|
||||
|
||||
|
||||
// Function: vnf_add_faces()
|
||||
@@ -122,23 +122,23 @@ function vnf_add_face(vnf=EMPTY_VNF, pts) =
|
||||
// vnf = The VNF structure to add a face to.
|
||||
// faces = The list of faces, where each face is given as a list of vertex points.
|
||||
function vnf_add_faces(vnf=EMPTY_VNF, faces) =
|
||||
assert(is_vnf(vnf))
|
||||
assert(is_list(faces))
|
||||
let(
|
||||
res = set_union(vnf[0], flatten(faces), get_indices=true),
|
||||
idxs = res[0],
|
||||
nverts = res[1],
|
||||
offs = cumsum([0, for (face=faces) len(face)]),
|
||||
ifaces = [
|
||||
for (i=idx(faces)) [
|
||||
for (j=idx(faces[i]))
|
||||
idxs[offs[i]+j]
|
||||
]
|
||||
]
|
||||
) [
|
||||
nverts,
|
||||
concat(vnf[1],ifaces)
|
||||
];
|
||||
assert(is_vnf(vnf))
|
||||
assert(is_list(faces))
|
||||
let(
|
||||
res = set_union(vnf[0], flatten(faces), get_indices=true),
|
||||
idxs = res[0],
|
||||
nverts = res[1],
|
||||
offs = cumsum([0, for (face=faces) len(face)]),
|
||||
ifaces = [
|
||||
for (i=idx(faces)) [
|
||||
for (j=idx(faces[i]))
|
||||
idxs[offs[i]+j]
|
||||
]
|
||||
]
|
||||
) [
|
||||
nverts,
|
||||
concat(vnf[1],ifaces)
|
||||
];
|
||||
|
||||
|
||||
// Function: vnf_merge()
|
||||
@@ -147,14 +147,14 @@ function vnf_add_faces(vnf=EMPTY_VNF, faces) =
|
||||
// Description:
|
||||
// Given a list of VNF structures, merges them all into a single VNF structure.
|
||||
function vnf_merge(vnfs=[],_i=0,_acc=EMPTY_VNF) =
|
||||
(assert(is_vnf_list(vnfs)) _i>=len(vnfs))? _acc :
|
||||
vnf_merge(
|
||||
vnfs, _i=_i+1,
|
||||
_acc = let(base=len(_acc[0])) [
|
||||
concat(_acc[0], vnfs[_i][0]),
|
||||
concat(_acc[1], [for (f=vnfs[_i][1]) [for (i=f) i+base]]),
|
||||
]
|
||||
);
|
||||
(assert(is_vnf_list(vnfs)) _i>=len(vnfs))? _acc :
|
||||
vnf_merge(
|
||||
vnfs, _i=_i+1,
|
||||
_acc = let(base=len(_acc[0])) [
|
||||
concat(_acc[0], vnfs[_i][0]),
|
||||
concat(_acc[1], [for (f=vnfs[_i][1]) [for (i=f) i+base]]),
|
||||
]
|
||||
);
|
||||
|
||||
// Function: vnf_compact()
|
||||
// Usage:
|
||||
@@ -162,15 +162,15 @@ function vnf_merge(vnfs=[],_i=0,_acc=EMPTY_VNF) =
|
||||
// Description:
|
||||
// Takes a VNF and consolidates all duplicate vertices, and drops unreferenced vertices.
|
||||
function vnf_compact(vnf) =
|
||||
let(
|
||||
vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf,
|
||||
verts = vnf[0],
|
||||
faces = [
|
||||
for (face=vnf[1]) [
|
||||
for (i=face) verts[i]
|
||||
]
|
||||
]
|
||||
) vnf_add_faces(faces=faces);
|
||||
let(
|
||||
vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf,
|
||||
verts = vnf[0],
|
||||
faces = [
|
||||
for (face=vnf[1]) [
|
||||
for (i=face) verts[i]
|
||||
]
|
||||
]
|
||||
) vnf_add_faces(faces=faces);
|
||||
|
||||
|
||||
// Function: vnf_triangulate()
|
||||
@@ -179,10 +179,10 @@ function vnf_compact(vnf) =
|
||||
// Description:
|
||||
// Forces triangulation of faces in the VNF that have more than 3 vertices.
|
||||
function vnf_triangulate(vnf) =
|
||||
let(
|
||||
vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf,
|
||||
verts = vnf[0]
|
||||
) [verts, triangulate_faces(verts, vnf[1])];
|
||||
let(
|
||||
vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf,
|
||||
verts = vnf[0]
|
||||
) [verts, triangulate_faces(verts, vnf[1])];
|
||||
|
||||
|
||||
// Function: vnf_vertex_array()
|
||||
@@ -257,79 +257,79 @@ function vnf_triangulate(vnf) =
|
||||
// vnf3 = vnf_vertex_array(points=cap2, col_wrap=true, reverse=true);
|
||||
// vnf_polyhedron([vnf1, vnf2, vnf3]);
|
||||
function vnf_vertex_array(
|
||||
points,
|
||||
caps, cap1, cap2,
|
||||
col_wrap=false,
|
||||
row_wrap=false,
|
||||
reverse=false,
|
||||
style="default",
|
||||
vnf=EMPTY_VNF
|
||||
points,
|
||||
caps, cap1, cap2,
|
||||
col_wrap=false,
|
||||
row_wrap=false,
|
||||
reverse=false,
|
||||
style="default",
|
||||
vnf=EMPTY_VNF
|
||||
) =
|
||||
assert((!caps)||(caps&&col_wrap))
|
||||
assert(in_list(style,["default","alt","quincunx"]))
|
||||
assert((!caps)||(caps&&col_wrap))
|
||||
assert(in_list(style,["default","alt","quincunx"]))
|
||||
assert(is_consistent(points), "Non-rectangular or invalid point array")
|
||||
let(
|
||||
pts = flatten(points),
|
||||
pcnt = len(pts),
|
||||
rows = len(points),
|
||||
cols = len(points[0]),
|
||||
cap1 = first_defined([cap1,caps,false]),
|
||||
cap2 = first_defined([cap2,caps,false]),
|
||||
colcnt = cols - (col_wrap?0:1),
|
||||
rowcnt = rows - (row_wrap?0:1)
|
||||
)
|
||||
rows<=1 || cols<=1 ? vnf :
|
||||
vnf_merge([
|
||||
vnf, [
|
||||
concat(
|
||||
pts,
|
||||
style!="quincunx"? [] : [
|
||||
for (r = [0:1:rowcnt-1]) (
|
||||
for (c = [0:1:colcnt-1]) (
|
||||
let(
|
||||
i1 = ((r+0)%rows)*cols + ((c+0)%cols),
|
||||
i2 = ((r+1)%rows)*cols + ((c+0)%cols),
|
||||
i3 = ((r+1)%rows)*cols + ((c+1)%cols),
|
||||
i4 = ((r+0)%rows)*cols + ((c+1)%cols)
|
||||
) mean([pts[i1], pts[i2], pts[i3], pts[i4]])
|
||||
)
|
||||
)
|
||||
]
|
||||
),
|
||||
concat(
|
||||
[
|
||||
for (r = [0:1:rowcnt-1]) (
|
||||
for (c = [0:1:colcnt-1]) each (
|
||||
let(
|
||||
i1 = ((r+0)%rows)*cols + ((c+0)%cols),
|
||||
i2 = ((r+1)%rows)*cols + ((c+0)%cols),
|
||||
i3 = ((r+1)%rows)*cols + ((c+1)%cols),
|
||||
i4 = ((r+0)%rows)*cols + ((c+1)%cols)
|
||||
)
|
||||
style=="quincunx"? (
|
||||
let(i5 = pcnt + r*colcnt + c)
|
||||
reverse? [[i1,i2,i5],[i2,i3,i5],[i3,i4,i5],[i4,i1,i5]] : [[i1,i5,i2],[i2,i5,i3],[i3,i5,i4],[i4,i5,i1]]
|
||||
) : style=="alt"? (
|
||||
reverse? [[i1,i2,i4],[i2,i3,i4]] : [[i1,i4,i2],[i2,i4,i3]]
|
||||
) : (
|
||||
reverse? [[i1,i2,i3],[i1,i3,i4]] : [[i1,i3,i2],[i1,i4,i3]]
|
||||
)
|
||||
)
|
||||
)
|
||||
],
|
||||
!cap1? [] : [
|
||||
reverse?
|
||||
[for (c = [0:1:cols-1]) c] :
|
||||
[for (c = [cols-1:-1:0]) c]
|
||||
],
|
||||
!cap2? [] : [
|
||||
reverse?
|
||||
[for (c = [cols-1:-1:0]) (rows-1)*cols + c] :
|
||||
[for (c = [0:1:cols-1]) (rows-1)*cols + c]
|
||||
]
|
||||
)
|
||||
]
|
||||
]);
|
||||
let(
|
||||
pts = flatten(points),
|
||||
pcnt = len(pts),
|
||||
rows = len(points),
|
||||
cols = len(points[0]),
|
||||
cap1 = first_defined([cap1,caps,false]),
|
||||
cap2 = first_defined([cap2,caps,false]),
|
||||
colcnt = cols - (col_wrap?0:1),
|
||||
rowcnt = rows - (row_wrap?0:1)
|
||||
)
|
||||
rows<=1 || cols<=1 ? vnf :
|
||||
vnf_merge([
|
||||
vnf, [
|
||||
concat(
|
||||
pts,
|
||||
style!="quincunx"? [] : [
|
||||
for (r = [0:1:rowcnt-1]) (
|
||||
for (c = [0:1:colcnt-1]) (
|
||||
let(
|
||||
i1 = ((r+0)%rows)*cols + ((c+0)%cols),
|
||||
i2 = ((r+1)%rows)*cols + ((c+0)%cols),
|
||||
i3 = ((r+1)%rows)*cols + ((c+1)%cols),
|
||||
i4 = ((r+0)%rows)*cols + ((c+1)%cols)
|
||||
) mean([pts[i1], pts[i2], pts[i3], pts[i4]])
|
||||
)
|
||||
)
|
||||
]
|
||||
),
|
||||
concat(
|
||||
[
|
||||
for (r = [0:1:rowcnt-1]) (
|
||||
for (c = [0:1:colcnt-1]) each (
|
||||
let(
|
||||
i1 = ((r+0)%rows)*cols + ((c+0)%cols),
|
||||
i2 = ((r+1)%rows)*cols + ((c+0)%cols),
|
||||
i3 = ((r+1)%rows)*cols + ((c+1)%cols),
|
||||
i4 = ((r+0)%rows)*cols + ((c+1)%cols)
|
||||
)
|
||||
style=="quincunx"? (
|
||||
let(i5 = pcnt + r*colcnt + c)
|
||||
reverse? [[i1,i2,i5],[i2,i3,i5],[i3,i4,i5],[i4,i1,i5]] : [[i1,i5,i2],[i2,i5,i3],[i3,i5,i4],[i4,i5,i1]]
|
||||
) : style=="alt"? (
|
||||
reverse? [[i1,i2,i4],[i2,i3,i4]] : [[i1,i4,i2],[i2,i4,i3]]
|
||||
) : (
|
||||
reverse? [[i1,i2,i3],[i1,i3,i4]] : [[i1,i3,i2],[i1,i4,i3]]
|
||||
)
|
||||
)
|
||||
)
|
||||
],
|
||||
!cap1? [] : [
|
||||
reverse?
|
||||
[for (c = [0:1:cols-1]) c] :
|
||||
[for (c = [cols-1:-1:0]) c]
|
||||
],
|
||||
!cap2? [] : [
|
||||
reverse?
|
||||
[for (c = [cols-1:-1:0]) (rows-1)*cols + c] :
|
||||
[for (c = [0:1:cols-1]) (rows-1)*cols + c]
|
||||
]
|
||||
)
|
||||
]
|
||||
]);
|
||||
|
||||
|
||||
// Module: vnf_polyhedron()
|
||||
@@ -342,8 +342,8 @@ function vnf_vertex_array(
|
||||
// vnf = A VNF structure, or list of VNF structures.
|
||||
// convexity = Max number of times a line could intersect a wall of the shape.
|
||||
module vnf_polyhedron(vnf, convexity=2) {
|
||||
vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf;
|
||||
polyhedron(vnf[0], vnf[1], convexity=convexity);
|
||||
vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf;
|
||||
polyhedron(vnf[0], vnf[1], convexity=convexity);
|
||||
}
|
||||
|
||||
|
||||
@@ -358,15 +358,15 @@ module vnf_polyhedron(vnf, convexity=2) {
|
||||
// no holes; otherwise the results are undefined. Returns a positive volume if face direction is clockwise and a negative volume
|
||||
// if face direction is counter-clockwise.
|
||||
function vnf_volume(vnf) =
|
||||
let(
|
||||
vnf = vnf_triangulate(vnf),
|
||||
verts = vnf[0]
|
||||
) sum([
|
||||
for(face_index=vnf[1]) let(
|
||||
face = select(verts, face_index),
|
||||
n = cross(face[2]-face[0],face[1]-face[0])
|
||||
) face[0] * n
|
||||
])/6;
|
||||
let(
|
||||
vnf = vnf_triangulate(vnf),
|
||||
verts = vnf[0]
|
||||
) sum([
|
||||
for(face_index=vnf[1]) let(
|
||||
face = select(verts, face_index),
|
||||
n = cross(face[2]-face[0],face[1]-face[0])
|
||||
) face[0] * n
|
||||
])/6;
|
||||
|
||||
|
||||
// Function: vnf_centroid()
|
||||
@@ -378,36 +378,36 @@ function vnf_volume(vnf) =
|
||||
|
||||
// Algorithm from: https://wwwf.imperial.ac.uk/~rn/centroid.pdf
|
||||
function vnf_centroid(vnf) =
|
||||
let(
|
||||
vnf = vnf_triangulate(vnf),
|
||||
verts = vnf[0],
|
||||
val=sum([
|
||||
for(face_index=vnf[1])
|
||||
let(
|
||||
face = select(verts, face_index),
|
||||
n = cross(face[2]-face[0],face[1]-face[0])
|
||||
) [
|
||||
face[0] * n,
|
||||
vmul(n,
|
||||
sqr(face[0] + face[1]) +
|
||||
sqr(face[0] + face[2]) +
|
||||
sqr(face[1] + face[2])
|
||||
)
|
||||
]
|
||||
])
|
||||
) val[1]/val[0]/8;
|
||||
let(
|
||||
vnf = vnf_triangulate(vnf),
|
||||
verts = vnf[0],
|
||||
val=sum([
|
||||
for(face_index=vnf[1])
|
||||
let(
|
||||
face = select(verts, face_index),
|
||||
n = cross(face[2]-face[0],face[1]-face[0])
|
||||
) [
|
||||
face[0] * n,
|
||||
vmul(n,
|
||||
sqr(face[0] + face[1]) +
|
||||
sqr(face[0] + face[2]) +
|
||||
sqr(face[1] + face[2])
|
||||
)
|
||||
]
|
||||
])
|
||||
) val[1]/val[0]/8;
|
||||
|
||||
|
||||
function _triangulate_planar_convex_polygons(polys) =
|
||||
polys==[]? [] :
|
||||
let(
|
||||
tris = [for (poly=polys) if (len(poly)==3) poly],
|
||||
bigs = [for (poly=polys) if (len(poly)>3) poly],
|
||||
newtris = [for (poly=bigs) select(poly,-2,0)],
|
||||
newbigs = [for (poly=bigs) select(poly,0,-2)],
|
||||
newtris2 = _triangulate_planar_convex_polygons(newbigs),
|
||||
outtris = concat(tris, newtris, newtris2)
|
||||
) outtris;
|
||||
polys==[]? [] :
|
||||
let(
|
||||
tris = [for (poly=polys) if (len(poly)==3) poly],
|
||||
bigs = [for (poly=polys) if (len(poly)>3) poly],
|
||||
newtris = [for (poly=bigs) select(poly,-2,0)],
|
||||
newbigs = [for (poly=bigs) select(poly,0,-2)],
|
||||
newtris2 = _triangulate_planar_convex_polygons(newbigs),
|
||||
outtris = concat(tris, newtris, newtris2)
|
||||
) outtris;
|
||||
|
||||
|
||||
// Function: vnf_bend()
|
||||
@@ -482,49 +482,49 @@ function _triangulate_planar_convex_polygons(polys) =
|
||||
// bent1 = vnf_bend(vnf1, axis="Z");
|
||||
// vnf_polyhedron([bent1]);
|
||||
function vnf_bend(vnf,r,d,axis="Z") =
|
||||
let(
|
||||
chk_axis = assert(in_list(axis,["X","Y","Z"])),
|
||||
vnf = vnf_triangulate(vnf),
|
||||
verts = vnf[0],
|
||||
bounds = pointlist_bounds(verts),
|
||||
bmin = bounds[0],
|
||||
bmax = bounds[1],
|
||||
dflt = axis=="Z"?
|
||||
max(abs(bmax.y), abs(bmin.y)) :
|
||||
max(abs(bmax.z), abs(bmin.z)),
|
||||
r = get_radius(r=r,d=d,dflt=dflt),
|
||||
width = axis=="X"? (bmax.y-bmin.y) : (bmax.x - bmin.x)
|
||||
)
|
||||
assert(width <= 2*PI*r, "Shape would wrap more than completely around the cylinder.")
|
||||
let(
|
||||
span_chk = axis=="Z"?
|
||||
assert(bmin.y > 0 || bmax.y < 0, "Entire shape MUST be completely in front of or behind y=0.") :
|
||||
assert(bmin.z > 0 || bmax.z < 0, "Entire shape MUST be completely above or below z=0."),
|
||||
min_ang = 180 * bmin.x / (PI * r),
|
||||
max_ang = 180 * bmax.x / (PI * r),
|
||||
ang_span = max_ang-min_ang,
|
||||
steps = ceil(segs(r) * ang_span/360),
|
||||
step = width / steps,
|
||||
bend_at = axis=="X"? [for(i = [1:1:steps-1]) i*step+bmin.y] :
|
||||
[for(i = [1:1:steps-1]) i*step+bmin.x],
|
||||
facepolys = [for (face=vnf[1]) select(verts,face)],
|
||||
splits = axis=="X"?
|
||||
split_polygons_at_each_y(facepolys, bend_at) :
|
||||
split_polygons_at_each_x(facepolys, bend_at),
|
||||
newtris = _triangulate_planar_convex_polygons(splits),
|
||||
bent_faces = [
|
||||
for (tri = newtris) [
|
||||
for (p = tri) let(
|
||||
a = axis=="X"? 180*p.y/(r*PI) * sign(bmax.z) :
|
||||
axis=="Y"? 180*p.x/(r*PI) * sign(bmax.z) :
|
||||
180*p.x/(r*PI) * sign(bmax.y)
|
||||
)
|
||||
axis=="X"? [p.x, p.z*sin(a), p.z*cos(a)] :
|
||||
axis=="Y"? [p.z*sin(a), p.y, p.z*cos(a)] :
|
||||
[p.y*sin(a), p.y*cos(a), p.z]
|
||||
]
|
||||
]
|
||||
) vnf_add_faces(faces=bent_faces);
|
||||
let(
|
||||
chk_axis = assert(in_list(axis,["X","Y","Z"])),
|
||||
vnf = vnf_triangulate(vnf),
|
||||
verts = vnf[0],
|
||||
bounds = pointlist_bounds(verts),
|
||||
bmin = bounds[0],
|
||||
bmax = bounds[1],
|
||||
dflt = axis=="Z"?
|
||||
max(abs(bmax.y), abs(bmin.y)) :
|
||||
max(abs(bmax.z), abs(bmin.z)),
|
||||
r = get_radius(r=r,d=d,dflt=dflt),
|
||||
width = axis=="X"? (bmax.y-bmin.y) : (bmax.x - bmin.x)
|
||||
)
|
||||
assert(width <= 2*PI*r, "Shape would wrap more than completely around the cylinder.")
|
||||
let(
|
||||
span_chk = axis=="Z"?
|
||||
assert(bmin.y > 0 || bmax.y < 0, "Entire shape MUST be completely in front of or behind y=0.") :
|
||||
assert(bmin.z > 0 || bmax.z < 0, "Entire shape MUST be completely above or below z=0."),
|
||||
min_ang = 180 * bmin.x / (PI * r),
|
||||
max_ang = 180 * bmax.x / (PI * r),
|
||||
ang_span = max_ang-min_ang,
|
||||
steps = ceil(segs(r) * ang_span/360),
|
||||
step = width / steps,
|
||||
bend_at = axis=="X"? [for(i = [1:1:steps-1]) i*step+bmin.y] :
|
||||
[for(i = [1:1:steps-1]) i*step+bmin.x],
|
||||
facepolys = [for (face=vnf[1]) select(verts,face)],
|
||||
splits = axis=="X"?
|
||||
split_polygons_at_each_y(facepolys, bend_at) :
|
||||
split_polygons_at_each_x(facepolys, bend_at),
|
||||
newtris = _triangulate_planar_convex_polygons(splits),
|
||||
bent_faces = [
|
||||
for (tri = newtris) [
|
||||
for (p = tri) let(
|
||||
a = axis=="X"? 180*p.y/(r*PI) * sign(bmax.z) :
|
||||
axis=="Y"? 180*p.x/(r*PI) * sign(bmax.z) :
|
||||
180*p.x/(r*PI) * sign(bmax.y)
|
||||
)
|
||||
axis=="X"? [p.x, p.z*sin(a), p.z*cos(a)] :
|
||||
axis=="Y"? [p.z*sin(a), p.y, p.z*cos(a)] :
|
||||
[p.y*sin(a), p.y*cos(a), p.z]
|
||||
]
|
||||
]
|
||||
) vnf_add_faces(faces=bent_faces);
|
||||
|
||||
|
||||
// Function&Module: vnf_validate()
|
||||
@@ -614,198 +614,198 @@ function vnf_bend(vnf,r,d,axis="Z") =
|
||||
// ], slices=0, caps=false);
|
||||
// vnf_validate(vnf,size=2);
|
||||
function vnf_validate(vnf, show_warns=true, check_isects=false) =
|
||||
assert(is_path(vnf[0]))
|
||||
let(
|
||||
vnf = vnf_compact(vnf),
|
||||
varr = vnf[0],
|
||||
faces = vnf[1],
|
||||
edges = sort([
|
||||
for (face=faces, edge=pair_wrap(face))
|
||||
edge[0]<edge[1]? edge : [edge[1],edge[0]]
|
||||
]),
|
||||
edgecnts = unique_count(edges),
|
||||
uniq_edges = edgecnts[0],
|
||||
big_faces = !show_warns? [] : [
|
||||
for (face = faces)
|
||||
if (len(face) > 3) [
|
||||
"WARNING",
|
||||
"BIG_FACE",
|
||||
"Face has more than 3 vertices, and may confuse CGAL",
|
||||
[for (i=face) varr[i]],
|
||||
"yellow"
|
||||
]
|
||||
],
|
||||
null_faces = !show_warns? [] : [
|
||||
for (face = faces) let(
|
||||
face = deduplicate(face,closed=true)
|
||||
)
|
||||
if (len(face)>=3) let(
|
||||
faceverts = [for (k=face) varr[k]],
|
||||
area = polygon_area(faceverts)
|
||||
) if (is_num(area) && abs(area) < EPSILON) [
|
||||
"WARNING",
|
||||
"NULL_FACE",
|
||||
str("Face has zero area: ",fmt_float(abs(area),15)),
|
||||
faceverts,
|
||||
"brown"
|
||||
]
|
||||
],
|
||||
nonplanars = unique([
|
||||
for (face = faces) let(
|
||||
faceverts = [for (k=face) varr[k]]
|
||||
) if (!points_are_coplanar(faceverts)) [
|
||||
"ERROR",
|
||||
"NONPLANAR",
|
||||
"Face vertices are not coplanar",
|
||||
faceverts,
|
||||
"cyan"
|
||||
]
|
||||
]),
|
||||
overpop_edges = unique([
|
||||
for (i=idx(uniq_edges))
|
||||
if (edgecnts[1][i]>2) [
|
||||
"ERROR",
|
||||
"OVRPOP_EDGE",
|
||||
"Too many faces attached at Edge",
|
||||
[for (i=uniq_edges[i]) varr[i]],
|
||||
"#f70"
|
||||
]
|
||||
]),
|
||||
reversals = unique([
|
||||
for(i = idx(faces), j = idx(faces)) if(i != j)
|
||||
if(len(deduplicate(faces[i],closed=true))>=3)
|
||||
if(len(deduplicate(faces[j],closed=true))>=3)
|
||||
for(edge1 = pair_wrap(faces[i]))
|
||||
for(edge2 = pair_wrap(faces[j]))
|
||||
if(edge1 == edge2) // Valid adjacent faces will never have the same vertex ordering.
|
||||
if(_edge_not_reported(edge1, varr, overpop_edges))
|
||||
[
|
||||
"ERROR",
|
||||
"REVERSAL",
|
||||
"Faces Reverse Across Edge",
|
||||
[for (i=edge1) varr[i]],
|
||||
"violet"
|
||||
]
|
||||
]),
|
||||
t_juncts = unique([
|
||||
for (v=idx(varr), edge=uniq_edges)
|
||||
if (v!=edge[0] && v!=edge[1]) let(
|
||||
a = varr[edge[0]],
|
||||
b = varr[v],
|
||||
c = varr[edge[1]],
|
||||
pt = segment_closest_point([a,c],b)
|
||||
) if (pt == b) [
|
||||
"ERROR",
|
||||
"T_JUNCTION",
|
||||
"Vertex is mid-edge on another Face",
|
||||
[b],
|
||||
"red"
|
||||
]
|
||||
]),
|
||||
isect_faces = !check_isects? [] : unique([
|
||||
for (i = [0:1:len(faces)-2])
|
||||
for (j = [i+1:1:len(faces)-1]) let(
|
||||
f1 = faces[i],
|
||||
f2 = faces[j],
|
||||
shared_edges = [
|
||||
for (edge1 = pair_wrap(f1), edge2 = pair_wrap(f2)) let(
|
||||
e1 = edge1[0]<edge1[1]? edge1 : [edge1[1],edge1[0]],
|
||||
e2 = edge2[0]<edge2[1]? edge2 : [edge2[1],edge2[0]]
|
||||
) if (e1==e2) 1
|
||||
]
|
||||
)
|
||||
if (!shared_edges) let(
|
||||
plane1 = plane3pt_indexed(varr, f1[0], f1[1], f1[2]),
|
||||
plane2 = plane3pt_indexed(varr, f2[0], f2[1], f2[2]),
|
||||
line = plane_intersection(plane1, plane2)
|
||||
)
|
||||
if (!is_undef(line)) let(
|
||||
poly1 = select(varr,f1),
|
||||
isects = polygon_line_intersection(poly1,line)
|
||||
)
|
||||
if (!is_undef(isects))
|
||||
for (isect=isects)
|
||||
if (len(isect)>1) let(
|
||||
poly2 = select(varr,f2),
|
||||
isects2 = polygon_line_intersection(poly2,isect,bounded=true)
|
||||
)
|
||||
if (!is_undef(isects2))
|
||||
for (seg=isects2)
|
||||
if (seg[0] != seg[1]) [
|
||||
"ERROR",
|
||||
"FACE_ISECT",
|
||||
"Faces intersect",
|
||||
seg,
|
||||
"blue"
|
||||
]
|
||||
]),
|
||||
hole_edges = unique([
|
||||
for (i=idx(uniq_edges))
|
||||
if (edgecnts[1][i]<2)
|
||||
if (_pts_not_reported(uniq_edges[i], varr, t_juncts))
|
||||
if (_pts_not_reported(uniq_edges[i], varr, isect_faces))
|
||||
[
|
||||
"ERROR",
|
||||
"HOLE_EDGE",
|
||||
"Edge bounds Hole",
|
||||
[for (i=uniq_edges[i]) varr[i]],
|
||||
"magenta"
|
||||
]
|
||||
])
|
||||
) concat(
|
||||
big_faces,
|
||||
null_faces,
|
||||
nonplanars,
|
||||
overpop_edges,
|
||||
reversals,
|
||||
t_juncts,
|
||||
isect_faces,
|
||||
hole_edges
|
||||
);
|
||||
assert(is_path(vnf[0]))
|
||||
let(
|
||||
vnf = vnf_compact(vnf),
|
||||
varr = vnf[0],
|
||||
faces = vnf[1],
|
||||
edges = sort([
|
||||
for (face=faces, edge=pair_wrap(face))
|
||||
edge[0]<edge[1]? edge : [edge[1],edge[0]]
|
||||
]),
|
||||
edgecnts = unique_count(edges),
|
||||
uniq_edges = edgecnts[0],
|
||||
big_faces = !show_warns? [] : [
|
||||
for (face = faces)
|
||||
if (len(face) > 3) [
|
||||
"WARNING",
|
||||
"BIG_FACE",
|
||||
"Face has more than 3 vertices, and may confuse CGAL",
|
||||
[for (i=face) varr[i]],
|
||||
"yellow"
|
||||
]
|
||||
],
|
||||
null_faces = !show_warns? [] : [
|
||||
for (face = faces) let(
|
||||
face = deduplicate(face,closed=true)
|
||||
)
|
||||
if (len(face)>=3) let(
|
||||
faceverts = [for (k=face) varr[k]],
|
||||
area = polygon_area(faceverts)
|
||||
) if (is_num(area) && abs(area) < EPSILON) [
|
||||
"WARNING",
|
||||
"NULL_FACE",
|
||||
str("Face has zero area: ",fmt_float(abs(area),15)),
|
||||
faceverts,
|
||||
"brown"
|
||||
]
|
||||
],
|
||||
nonplanars = unique([
|
||||
for (face = faces) let(
|
||||
faceverts = [for (k=face) varr[k]]
|
||||
) if (!points_are_coplanar(faceverts)) [
|
||||
"ERROR",
|
||||
"NONPLANAR",
|
||||
"Face vertices are not coplanar",
|
||||
faceverts,
|
||||
"cyan"
|
||||
]
|
||||
]),
|
||||
overpop_edges = unique([
|
||||
for (i=idx(uniq_edges))
|
||||
if (edgecnts[1][i]>2) [
|
||||
"ERROR",
|
||||
"OVRPOP_EDGE",
|
||||
"Too many faces attached at Edge",
|
||||
[for (i=uniq_edges[i]) varr[i]],
|
||||
"#f70"
|
||||
]
|
||||
]),
|
||||
reversals = unique([
|
||||
for(i = idx(faces), j = idx(faces)) if(i != j)
|
||||
if(len(deduplicate(faces[i],closed=true))>=3)
|
||||
if(len(deduplicate(faces[j],closed=true))>=3)
|
||||
for(edge1 = pair_wrap(faces[i]))
|
||||
for(edge2 = pair_wrap(faces[j]))
|
||||
if(edge1 == edge2) // Valid adjacent faces will never have the same vertex ordering.
|
||||
if(_edge_not_reported(edge1, varr, overpop_edges))
|
||||
[
|
||||
"ERROR",
|
||||
"REVERSAL",
|
||||
"Faces Reverse Across Edge",
|
||||
[for (i=edge1) varr[i]],
|
||||
"violet"
|
||||
]
|
||||
]),
|
||||
t_juncts = unique([
|
||||
for (v=idx(varr), edge=uniq_edges)
|
||||
if (v!=edge[0] && v!=edge[1]) let(
|
||||
a = varr[edge[0]],
|
||||
b = varr[v],
|
||||
c = varr[edge[1]],
|
||||
pt = segment_closest_point([a,c],b)
|
||||
) if (pt == b) [
|
||||
"ERROR",
|
||||
"T_JUNCTION",
|
||||
"Vertex is mid-edge on another Face",
|
||||
[b],
|
||||
"red"
|
||||
]
|
||||
]),
|
||||
isect_faces = !check_isects? [] : unique([
|
||||
for (i = [0:1:len(faces)-2])
|
||||
for (j = [i+1:1:len(faces)-1]) let(
|
||||
f1 = faces[i],
|
||||
f2 = faces[j],
|
||||
shared_edges = [
|
||||
for (edge1 = pair_wrap(f1), edge2 = pair_wrap(f2)) let(
|
||||
e1 = edge1[0]<edge1[1]? edge1 : [edge1[1],edge1[0]],
|
||||
e2 = edge2[0]<edge2[1]? edge2 : [edge2[1],edge2[0]]
|
||||
) if (e1==e2) 1
|
||||
]
|
||||
)
|
||||
if (!shared_edges) let(
|
||||
plane1 = plane3pt_indexed(varr, f1[0], f1[1], f1[2]),
|
||||
plane2 = plane3pt_indexed(varr, f2[0], f2[1], f2[2]),
|
||||
line = plane_intersection(plane1, plane2)
|
||||
)
|
||||
if (!is_undef(line)) let(
|
||||
poly1 = select(varr,f1),
|
||||
isects = polygon_line_intersection(poly1,line)
|
||||
)
|
||||
if (!is_undef(isects))
|
||||
for (isect=isects)
|
||||
if (len(isect)>1) let(
|
||||
poly2 = select(varr,f2),
|
||||
isects2 = polygon_line_intersection(poly2,isect,bounded=true)
|
||||
)
|
||||
if (!is_undef(isects2))
|
||||
for (seg=isects2)
|
||||
if (seg[0] != seg[1]) [
|
||||
"ERROR",
|
||||
"FACE_ISECT",
|
||||
"Faces intersect",
|
||||
seg,
|
||||
"blue"
|
||||
]
|
||||
]),
|
||||
hole_edges = unique([
|
||||
for (i=idx(uniq_edges))
|
||||
if (edgecnts[1][i]<2)
|
||||
if (_pts_not_reported(uniq_edges[i], varr, t_juncts))
|
||||
if (_pts_not_reported(uniq_edges[i], varr, isect_faces))
|
||||
[
|
||||
"ERROR",
|
||||
"HOLE_EDGE",
|
||||
"Edge bounds Hole",
|
||||
[for (i=uniq_edges[i]) varr[i]],
|
||||
"magenta"
|
||||
]
|
||||
])
|
||||
) concat(
|
||||
big_faces,
|
||||
null_faces,
|
||||
nonplanars,
|
||||
overpop_edges,
|
||||
reversals,
|
||||
t_juncts,
|
||||
isect_faces,
|
||||
hole_edges
|
||||
);
|
||||
|
||||
|
||||
function _pts_not_reported(pts, varr, reports) =
|
||||
[
|
||||
for (i = pts, report = reports, pt = report[3])
|
||||
if (varr[i] == pt) 1
|
||||
] == [];
|
||||
[
|
||||
for (i = pts, report = reports, pt = report[3])
|
||||
if (varr[i] == pt) 1
|
||||
] == [];
|
||||
|
||||
|
||||
function _edge_not_reported(edge, varr, reports) =
|
||||
let(
|
||||
edge = sort([for (i=edge) varr[i]])
|
||||
) [
|
||||
for (report = reports) let(
|
||||
pts = sort(report[3])
|
||||
) if (len(pts)==2 && edge == pts) 1
|
||||
] == [];
|
||||
let(
|
||||
edge = sort([for (i=edge) varr[i]])
|
||||
) [
|
||||
for (report = reports) let(
|
||||
pts = sort(report[3])
|
||||
) if (len(pts)==2 && edge == pts) 1
|
||||
] == [];
|
||||
|
||||
|
||||
module vnf_validate(vnf, size=1, show_warns=true, check_isects=false) {
|
||||
faults = vnf_validate(
|
||||
vnf, show_warns=show_warns,
|
||||
check_isects=check_isects
|
||||
);
|
||||
for (fault = faults) {
|
||||
typ = fault[0];
|
||||
err = fault[1];
|
||||
msg = fault[2];
|
||||
pts = fault[3];
|
||||
clr = fault[4];
|
||||
echo(str(typ, " ", err, ": ", msg, " at ", pts));
|
||||
color(clr) {
|
||||
if (len(pts)==2) {
|
||||
stroke(pts, width=size);
|
||||
} else if (len(pts)>2) {
|
||||
stroke(pts, width=size, closed=true);
|
||||
polyhedron(pts,[[for (i=idx(pts)) i]]);
|
||||
} else {
|
||||
move_copies(pts) sphere(d=size*3, $fn=18);
|
||||
}
|
||||
}
|
||||
}
|
||||
color([0.5,0.5,0.5,0.5]) vnf_polyhedron(vnf);
|
||||
faults = vnf_validate(
|
||||
vnf, show_warns=show_warns,
|
||||
check_isects=check_isects
|
||||
);
|
||||
for (fault = faults) {
|
||||
typ = fault[0];
|
||||
err = fault[1];
|
||||
msg = fault[2];
|
||||
pts = fault[3];
|
||||
clr = fault[4];
|
||||
echo(str(typ, " ", err, ": ", msg, " at ", pts));
|
||||
color(clr) {
|
||||
if (len(pts)==2) {
|
||||
stroke(pts, width=size);
|
||||
} else if (len(pts)>2) {
|
||||
stroke(pts, width=size, closed=true);
|
||||
polyhedron(pts,[[for (i=idx(pts)) i]]);
|
||||
} else {
|
||||
move_copies(pts) sphere(d=size*3, $fn=18);
|
||||
}
|
||||
}
|
||||
}
|
||||
color([0.5,0.5,0.5,0.5]) vnf_polyhedron(vnf);
|
||||
}
|
||||
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
Reference in New Issue
Block a user