You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

155 lines
5.2 KiB

import sys
import pathlib
import numpy
import scipy.integrate
import scipy.optimize
import scipy.interpolate
def large_arch(fc=60, Qb=100, Qt=49, L=30, nsegs=25):
"""
http://en.wikipedia.org/wiki/Jefferson_National_Expansion_Memorial
fc = maximum height of centroid (in feet) = 625.0925
Qb = maximum cross sectional area of arch at base (in sq. feet) = 1262.6651
Qt = minimum cross sectional area of arch at top (in sq. feet) = 125.1406
L = half width of centroid at the base (in feet) = 299.2239
for our scene, in cm
"""
A = fc / (Qb / Qt - 1)
C = numpy.arccosh(Qb / Qt)
x0 = -L
#################################################################
# Arch function
#################################################################
def arch(x: numpy.ndarray):
return -A * (numpy.cosh(C * x / L) - 1) + fc
#################################################################
# Arch derivative function
#################################################################
def darch(x: numpy.ndarray):
return -A * numpy.sinh(C * x / L) * C / L
#################################################################
# Arc-length function
#################################################################
def arc_len_func(x: numpy.ndarray):
return numpy.sqrt(
1 + A**2 * (numpy.cosh(C * x / L)**2 - 1) * C**2 / L**2)
arc_len = scipy.integrate.quad(arc_len_func, -L, L)[0]
#################################################################
# Function used to indicate when equal arc-length
# has been reached
#################################################################
def eq_arc_len_func(x: numpy.ndarray):
return scipy.integrate.quad(arc_len_func, x0, x)[0] - arc_len / nsegs
p = numpy.zeros((nsegs * 8, 3))
k = 0
while x0 < L * 0.999:
# next segment
x1 = scipy.optimize.fsolve(eq_arc_len_func, x0)[0]
y0 = arch(x0)
y1 = arch(x1)
# normals
dydx0 = darch(x0)
dydx1 = darch(x1)
v0 = numpy.array([-dydx0, 1])
v1 = numpy.array([-dydx1, 1])
v0 /= numpy.linalg.norm(v0)
v1 /= numpy.linalg.norm(v1)
# block width depends on the height
a0 = y0 / fc
a1 = y1 / fc
a0 = max(min(a0, 1), 0)
a1 = max(min(a1, 1), 0)
w = scipy.interpolate.interp1d(
[0, 1], [numpy.sqrt(Qb), numpy.sqrt(Qt)])
w0 = w(a0)
w1 = w(a1)
# hack to make faces not exactly the same size
if x0 < 0:
w1 = w0
else:
w0 = w1
# block corners
v0 = 0.5 * w0 * v0
v1 = 0.5 * w1 * v1
p000 = numpy.array([x0 - v0[0], y0 - v0[1], -0.5 * w0])
p100 = numpy.array([x0 + v0[0], y0 + v0[1], -0.5 * w0])
p010 = numpy.array([x1 - v1[0], y1 - v1[1], -0.5 * w1])
p110 = numpy.array([x1 + v1[0], y1 + v1[1], -0.5 * w1])
p001 = numpy.array([x0 - v0[0], y0 - v0[1], 0.5 * w0])
p101 = numpy.array([x0 + v0[0], y0 + v0[1], 0.5 * w0])
p011 = numpy.array([x1 - v1[0], y1 - v1[1], 0.5 * w1])
p111 = numpy.array([x1 + v1[0], y1 + v1[1], 0.5 * w1])
p[k * 8 + 0] = p000.ravel()
p[k * 8 + 1] = p001.ravel()
p[k * 8 + 2] = p010.ravel()
p[k * 8 + 3] = p011.ravel()
p[k * 8 + 4] = p100.ravel()
p[k * 8 + 5] = p101.ravel()
p[k * 8 + 6] = p110.ravel()
p[k * 8 + 7] = p111.ravel()
# Add a small gap between pieces
p[k * 8:(k + 1) * 8, 0] += (k - nsegs // 2) * 0.1
p[k * 8:(k + 1) * 8, 1] += (nsegs // 2 * 0.1 -
abs((k - nsegs // 2) * 0.1))
x0 = x1
k += 1
# Flatten the bottom stones
m = (p[4, 1] - p[6, 1]) / (p[4, 0] - p[6, 0])
p[[4, 5], 1] = p[0, 1]
p[[4, 5], 0] = (p[4, 1] - p[6, 1]) / m + p[6, 0]
m = (p[-2, 1] - p[-4, 1]) / (p[-2, 0] - p[-4, 0])
p[[-2, -1], 1] = p[-5, 1]
p[[-2, -1], 0] = (p[-2, 1] - p[-4, 1]) / m + p[-4, 0]
# Shift entire arch so the base is at y=0.1
min_y = min(p[:, 1])
p[:, 1] += 0.1 - min_y
dir = (pathlib.Path(__file__).resolve().parents[2] / "meshes" / "arch" /
f"num_stones={nsegs:d}")
dir.mkdir(parents=True, exist_ok=True)
# print(f"Saving meshes to {dir}")
# Write to obj files, one for each segment...
for i in range(nsegs):
filename = dir / f"stone-{i+1:02d}.obj"
with open(filename, 'w') as f:
# vertices
for k in range(8):
r = i * 8 + k
f.write("v {:f} {:f} {:f}\n".format(*p[r]))
# faces
f.write("f 1 3 4\n") # -x
f.write("f 4 2 1\n") # -x
f.write("f 5 6 8\n") # +x
f.write("f 8 7 5\n") # +x
f.write("f 1 2 6\n") # -y
f.write("f 6 5 1\n") # -y
f.write("f 3 7 8\n") # +y
f.write("f 8 4 3\n") # +y
f.write("f 1 5 7\n") # -z
f.write("f 7 3 1\n") # -z
f.write("f 2 4 8\n") # +z
f.write("f 8 6 2\n") # +z
return p
if __name__ == "__main__":
if len(sys.argv) > 1:
assert(int(sys.argv[1]) % 2)
large_arch(nsegs=int(sys.argv[1]))
else:
large_arch()