Merge branch 'revarbat_dev' of github.com:BelfrySCAD/BOSL2 into revarbat_dev

This commit is contained in:
Revar Desmera
2025-08-25 17:28:54 -07:00

View File

@@ -3359,12 +3359,136 @@ function _dual_vertices(vnf) =
]; ];
function _make_octa_sphere(r) =
let(
// Get number of triangles on each side of each octant.
subdivs = quantup(segs(r),4)/4,
// Get known exact octant edge vertices, on sphere surface, for all three octant edges.
edge1 = [for (p = [0:1:subdivs]) spherical_to_xyz(r,0,p/subdivs*90)],
edge2 = zrot(90, p=edge1),
edge3 = xrot(-90, p=edge1),
// Precache edge points
edge_pts1 = [for (i = [0:1:subdivs]) [edge1[i], edge2[i]]],
edge_pts2 = [for (i = [0:1:subdivs]) [edge1[subdivs-i], edge3[subdivs-i]]],
edge_pts3 = [for (i = [0:1:subdivs]) [edge2[subdivs-i], edge3[i]]],
// Precache greater circle axes
edge_axis1 = [UP, for (i = [1:1:subdivs]) vector_axis(edge_pts1[i][0], edge_pts1[i][1])],
edge_axis2 = [UP, for (i = [1:1:subdivs]) vector_axis(edge_pts2[i][0], edge_pts2[i][1])],
edge_axis3 = [UP, for (i = [1:1:subdivs]) vector_axis(edge_pts3[i][0], edge_pts3[i][1])],
// Precache greater circle angles
edge_ang1 = [0, for (i = [1:1:subdivs]) vector_angle(edge_pts1[i][0], edge_pts1[i][1])],
edge_ang2 = [0, for (i = [1:1:subdivs]) vector_angle(edge_pts2[i][0], edge_pts2[i][1])],
edge_ang3 = [0, for (i = [1:1:subdivs]) vector_angle(edge_pts3[i][0], edge_pts3[i][1])],
// Calculate greater circle subdivisions between octant edges to get vertices.
pts = [
for (row = [0:1:subdivs]) [
for (col = [0:1:row])
if (col == 0)
edge1[row] // Point on first edge is exact. (± FP rounding)
else if (col == row)
edge2[row] // Point on second edge is exact. (± FP rounding)
else if (row == subdivs)
edge3[subdivs-col] // Point on third edge is exact. (± FP rounding)
else // Calculating interior point.
let(
i1 = row,
i2 = subdivs - row + col,
i3 = subdivs - col,
u1 = col / i1,
u2 = col / i2,
u3 = (row - col) / i3,
p1 = rot(a=lerp(0, edge_ang1[i1], u1), v=edge_axis1[i1], p=edge_pts1[i1][0]),
p2 = rot(a=lerp(0, edge_ang2[i2], u2), v=edge_axis2[i2], p=edge_pts2[i2][0]),
p3 = rot(a=lerp(0, edge_ang3[i3], u3), v=edge_axis3[i3], p=edge_pts3[i3][0])
) unit(p1 + p2 + p3) * r // Average greater circle points beween the three edge pairs.
]
],
rows = [
[ pts[0][0] ],
for (row = [1:1:subdivs]) [
for (a = [0:90:359])
each zrot(a, p=select(pts[row], [0:1:row-1]))
],
for (row = [subdivs-1:-1:1]) [
for (a = [0:90:359])
each zrot(a, p=zflip(p=select(pts[row], [0:1:row-1])))
],
[ zflip(p=pts[0][0]) ]
],
verts = flatten(rows),
offsets = cumsum([0, for (row = rows) len(row)]),
faces = [
for (i = [0:1:3])
let(
l1 = len(rows[1]),
o1 = offsets[1]
)
[i+o1, 0, (i+1)%l1+o1],
for (j = [1:1:subdivs-1])
let(
l1 = len(rows[j]),
l2 = len(rows[j+1]),
o1 = offsets[j],
o2 = offsets[j+1],
q1 = len(rows[j])/4,
q2 = len(rows[j+1])/4
)
for (n = [0:1:3])
each [
[o2+q2*n, o1+q1*n, o2+(q2*n+1)%l2],
for (i = [0:1:q1-1]) each [
[o2+(i+q2*n+1)%l2, o1+(i+q1*n+1)%l1, o2+(i+q2*n+2)%l2],
[o2+(i+q2*n+1)%l2, o1+(i+q1*n)%l1, o1+(i+q1*n+1)%l1]
]
],
for (j = [subdivs:1:2*subdivs-2])
let(
l1 = len(rows[j]),
l2 = len(rows[j+1]),
o1 = offsets[j],
o2 = offsets[j+1],
q1 = len(rows[j])/4,
q2 = len(rows[j+1])/4
)
for (n = [0:1:3])
each [
[o2+q2*n, o1+q1*n, o1+(q1*n+1)%l1],
for (i = [0:1:q1-2]) each [
[o2+(i+q2*n)%l2, o1+(i+q1*n+1)%l1, o2+(i+q2*n+1)%l2],
[o1+(i+q1*n+2)%l1, o2+(i+q2*n+1)%l2, o1+(i+q1*n+1)%l1]
]
],
for (i = [0:1:3])
let(
row = len(rows) -2,
l1 = len(rows[row]),
o1 = offsets[row],
o2 = offsets[row+1]
)
[o2, o1+i, o1+(i+1)%l1]
]
) [verts, faces];
function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, orient=UP) = function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, orient=UP) =
assert(in_list(style, ["orig", "aligned", "stagger", "octa", "icosa"]))
let( let(
r = get_radius(r=r, d=d, dflt=1), r = get_radius(r=r, d=d, dflt=1),
hsides = segs(r), hsides = segs(r),
vsides = max(2,ceil(hsides/2)), vsides = max(2,ceil(hsides/2)),
octa_steps = round(max(4,hsides)/4),
icosa_steps = round(max(5,hsides)/5), icosa_steps = round(max(5,hsides)/5),
stagger = style=="stagger" stagger = style=="stagger"
) )
@@ -3390,6 +3514,10 @@ function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, or
) )
[reorient(anchor,spin,orient, r=r, p=dualvert), faces] [reorient(anchor,spin,orient, r=r, p=dualvert), faces]
: :
style=="octa" ?
let( vnf = _make_octa_sphere(r) )
reorient(anchor,spin,orient, r=r, p=vnf)
:
style=="icosa" ? // subdivide faces of an icosahedron and project them onto a sphere style=="icosa" ? // subdivide faces of an icosahedron and project them onto a sphere
let( let(
N = icosa_steps-1, N = icosa_steps-1,
@@ -3445,19 +3573,6 @@ function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, or
spherical_to_xyz(r, theta, phi), spherical_to_xyz(r, theta, phi),
spherical_to_xyz(r, 0, 180) spherical_to_xyz(r, 0, 180)
] ]
: style=="octa"?
let(
meridians = [
1,
for (i = [1:1:octa_steps]) i*4,
for (i = [octa_steps-1:-1:1]) i*4,
1,
]
)
[
for (i=idx(meridians), j=[0:1:meridians[i]-1])
spherical_to_xyz(r, j*360/meridians[i], i*180/(len(meridians)-1))
]
: assert(in_list(style,["orig","aligned","stagger","octa","icosa"])), : assert(in_list(style,["orig","aligned","stagger","octa","icosa"])),
lv = len(verts), lv = len(verts),
faces = circum && style=="stagger" ? faces = circum && style=="stagger" ?
@@ -3499,52 +3614,16 @@ function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, or
] ]
) )
] ]
: style=="orig"? [ : style=="orig"?
[
[for (i=[0:1:hsides-1]) hsides-i-1], [for (i=[0:1:hsides-1]) hsides-i-1],
[for (i=[0:1:hsides-1]) lv-hsides+i], [for (i=[0:1:hsides-1]) lv-hsides+i],
for (i=[0:1:vsides-2], j=[0:1:hsides-1]) for (i=[0:1:vsides-2], j=[0:1:hsides-1]) each [
each [
[(i+1)*hsides+j, i*hsides+j, i*hsides+(j+1)%hsides], [(i+1)*hsides+j, i*hsides+j, i*hsides+(j+1)%hsides],
[(i+1)*hsides+j, i*hsides+(j+1)%hsides, (i+1)*hsides+(j+1)%hsides], [(i+1)*hsides+j, i*hsides+(j+1)%hsides, (i+1)*hsides+(j+1)%hsides],
] ]
] ]
: /*style=="octa"?*/ : assert(in_list(style,["orig","aligned","stagger","octa","icosa"]))
let(
meridians = [
0, 1,
for (i = [1:1:octa_steps]) i*4,
for (i = [octa_steps-1:-1:1]) i*4,
1,
],
offs = cumsum(meridians),
pc = last(offs)-1,
os = octa_steps * 2
)
[
for (i=[0:1:3]) [0, 1+(i+1)%4, 1+i],
for (i=[0:1:3]) [pc-0, pc-(1+(i+1)%4), pc-(1+i)],
for (i=[1:1:octa_steps-1])
let(m = meridians[i+2]/4)
for (j=[0:1:3], k=[0:1:m-1])
let(
m1 = meridians[i+1],
m2 = meridians[i+2],
p1 = offs[i+0] + (j*m1/4 + k+0) % m1,
p2 = offs[i+0] + (j*m1/4 + k+1) % m1,
p3 = offs[i+1] + (j*m2/4 + k+0) % m2,
p4 = offs[i+1] + (j*m2/4 + k+1) % m2,
p5 = offs[os-i+0] + (j*m1/4 + k+0) % m1,
p6 = offs[os-i+0] + (j*m1/4 + k+1) % m1,
p7 = offs[os-i-1] + (j*m2/4 + k+0) % m2,
p8 = offs[os-i-1] + (j*m2/4 + k+1) % m2
)
each [
[p1, p4, p3],
if (k<m-1) [p1, p2, p4],
[p5, p7, p8],
if (k<m-1) [p5, p8, p6],
],
]
) [reorient(anchor,spin,orient, r=r, p=verts), faces]; ) [reorient(anchor,spin,orient, r=r, p=verts), faces];