Spaces:
Sleeping
Sleeping
| import argparse, numpy as np | |
| from math import sqrt | |
| from ideal_poly_volume_toolkit.geometry import lift_to_sphere_with_inf, ideal_poly_volume_via_hull_project_back, ideal_poly_volume_via_delaunay | |
| def unit(V): V = np.asarray(V, float); return V/np.linalg.norm(V, axis=1, keepdims=True) | |
| def rotate_vec_a_to_b(a, b): | |
| a = a/np.linalg.norm(a); b = b/np.linalg.norm(b) | |
| v = np.cross(a, b); c = np.dot(a, b) | |
| if np.allclose(v, 0) and c > 0: return np.eye(3) | |
| if np.allclose(v, 0) and c < 0: | |
| axis = np.array([1,0,0]) if abs(a[0]) < 0.9 else np.array([0,1,0]) | |
| v = np.cross(a, axis); v = v/np.linalg.norm(v) | |
| K = np.array([[0,-v[2],v[1]],[v[2],0,-v[0]],[-v[1],v[0],0]]) | |
| return np.eye(3)+2*K@K | |
| s = np.linalg.norm(v); K = np.array([[0,-v[2],v[1]],[v[2],0,-v[0]],[-v[1],v[0],0]]) | |
| return np.eye(3)+K+K@K*((1-c)/(s*s)) | |
| def inverse_stereographic(X): | |
| x,y,z = X[:,0], X[:,1], X[:,2]; denom = (1-z); return (x/denom) + 1j*(y/denom) | |
| def vertices_platonic(name): | |
| phi = (1+sqrt(5))/2 | |
| if name == 'tetrahedron': | |
| V = np.array([[ 1, 1, 1],[-1,-1, 1],[-1, 1,-1],[ 1,-1,-1]], float) | |
| elif name == 'octahedron': | |
| V = np.array([[ 1,0,0],[-1,0,0],[0,1,0],[0,-1,0],[0,0,1],[0,0,-1]], float) | |
| elif name == 'cube': | |
| V = np.array([[ 1, 1, 1],[ 1, 1,-1],[ 1,-1, 1],[ 1,-1,-1], | |
| [-1, 1, 1],[-1, 1,-1],[-1,-1, 1],[-1,-1,-1]], float) | |
| elif name == 'icosahedron': | |
| V = np.array([[0, 1, phi],[0,-1, phi],[0, 1,-phi],[0,-1,-phi], | |
| [ 1, phi,0],[-1, phi,0],[ 1,-phi,0],[-1,-phi,0], | |
| [ phi,0, 1],[ phi,0,-1],[-phi,0, 1],[-phi,0,-1]], float) | |
| elif name == 'dodecahedron': | |
| a = 1/phi; b = phi; V = [] | |
| for s1 in (-1,1): | |
| for s2 in (-1,1): | |
| for s3 in (-1,1): | |
| V.append([s1, s2, s3]) | |
| for s1 in (-1,1): | |
| for s2 in (-1,1): | |
| V.append([0, s1*a, s2*b]); V.append([s1*a, s2*b, 0]); V.append([s1*b, 0, s2*a]) | |
| V = np.array(V, float) | |
| else: | |
| raise ValueError('unknown solid') | |
| return unit(V) | |
| def build_finite_complex(name): | |
| V = vertices_platonic(name) | |
| R = rotate_vec_a_to_b(V[0], np.array([0,0,1.0])) | |
| Vr = (R @ V.T).T | |
| Z = inverse_stereographic(Vr[1:]) # finite ones; ∞ is the dropped vertex | |
| return Z | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument('--solid', type=str, required=True, | |
| choices=['tetrahedron','octahedron','cube','dodecahedron','icosahedron']) | |
| ap.add_argument('--mode', type=str, default='exact', choices=['fast','exact']) | |
| ap.add_argument('--dps', type=int, default=200) | |
| args = ap.parse_args() | |
| Z = build_finite_complex(args.solid) | |
| mode = 'eval_only' if args.mode == 'exact' else 'fast' | |
| vol_del = ideal_poly_volume_via_delaunay(Z, mode=mode, dps=args.dps, series_terms=96) | |
| W = np.empty(len(Z)+1, dtype=np.complex128); W[0] = np.inf+0j; W[1:] = Z | |
| vol_hull = ideal_poly_volume_via_hull_project_back(W, index_inf=0, mode=mode, dps=args.dps, series_terms=96) | |
| print(f'[Regular ideal {args.solid}]') | |
| print(f' volume via Delaunay: {vol_del:.12f}') | |
| print(f' volume via hull→project: {vol_hull:.12f}') | |
| print(f' |difference|: {abs(vol_del - vol_hull):.3e}') | |
| if __name__ == '__main__': | |
| main() | |