mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
386 lines
16 KiB
Python
386 lines
16 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate 3D CAD models (.scad files) for visualizing PIST manifolds.
|
|
|
|
These are NOT the full manifolds — they are 3D slices through the
|
|
pathological structures, making them human-visualizable. The full
|
|
9+ dimensional address cannot be embedded in 3D, but these slices
|
|
provide geometric intuition.
|
|
"""
|
|
|
|
import math
|
|
import os
|
|
|
|
OUTDIR = "/home/allaun/Desktop/cad_models"
|
|
os.makedirs(OUTDIR, exist_ok=True)
|
|
|
|
PHI = (1 + math.sqrt(5)) / 2
|
|
|
|
|
|
def write_scad(filename, content):
|
|
path = os.path.join(OUTDIR, filename)
|
|
with open(path, 'w') as f:
|
|
f.write(content)
|
|
print(f"Wrote {path}")
|
|
|
|
|
|
def scad_header(title):
|
|
return f"// {title}\n// 3D slice of PIST pathological manifold\n\n"
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# MODEL 1: PIST SHELLS AS CONCENTRIC POLYGONS
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def generate_pist_shells():
|
|
"""PIST shells visualized as concentric polygonal rings.
|
|
Shell k has 2k+1 positions arranged as vertices of a regular polygon."""
|
|
|
|
scad = scad_header("PIST Shells: Concentric Polygonal Rings")
|
|
scad += "// Each shell k is a regular (2k+1)-gon with radius k\n"
|
|
scad += "// Inner shells: dense, low curvature\n"
|
|
scad += "// Outer shells: sparse, high curvature\n\n"
|
|
|
|
max_shell = 12 # shells 0 through 12
|
|
|
|
for k in range(max_shell + 1):
|
|
n_points = 2 * k + 1 if k > 0 else 1
|
|
radius = k * 5 # mm
|
|
|
|
scad += f"// Shell k={k}, capacity={n_points}, radius={radius}mm\n"
|
|
|
|
if k == 0:
|
|
scad += f"translate([0, 0, {k * 2}]) sphere(r=2, $fn=32);\n"
|
|
else:
|
|
points = []
|
|
for t in range(n_points):
|
|
angle = 2 * math.pi * t / n_points
|
|
x = radius * math.cos(angle)
|
|
y = radius * math.sin(angle)
|
|
points.append(f"[{x:.2f}, {y:.2f}, {k * 2}]")
|
|
|
|
scad += f"// Vertices at positions t=0..{n_points-1}\n"
|
|
for t in range(n_points):
|
|
angle = 2 * math.pi * t / n_points
|
|
x = radius * math.cos(angle)
|
|
y = radius * math.sin(angle)
|
|
scad += f"translate([{x:.2f}, {y:.2f}, {k * 2}]) "
|
|
scad += f"sphere(r=1.5, $fn=16);\n"
|
|
|
|
# Draw polygon ring
|
|
scad += f"\n// Polygon ring for shell k={k}\n"
|
|
scad += f"linear_extrude(height=0.5, center=true) "
|
|
scad += f"polygon(points=[{', '.join(points)}]);\n"
|
|
|
|
scad += "\n"
|
|
|
|
# Mirror connections (involution axis)
|
|
scad += "// Mirror involution connections between shells\n"
|
|
for k in range(1, max_shell + 1):
|
|
n_points = 2 * k + 1
|
|
radius = k * 5
|
|
t_mirror = k # center position
|
|
angle = 2 * math.pi * t_mirror / n_points
|
|
x = radius * math.cos(angle)
|
|
y = radius * math.sin(angle)
|
|
scad += f"// Shell {k}: mirror axis at t={t_mirror}\n"
|
|
scad += f"color([1, 0, 0, 0.3]) translate([{x:.2f}, {y:.2f}, {k * 2}]) "
|
|
scad += f"cylinder(h=5, r=0.5, center=true, $fn=8);\n"
|
|
|
|
return scad
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# MODEL 2: MENGER SPONGE (3D FRACTAL)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def generate_menger_sponge():
|
|
"""Menger sponge: recursive cube removal.
|
|
We model depth=3 with explicit cube placement."""
|
|
|
|
scad = scad_header("Menger Sponge: Recursive Cube Removal")
|
|
scad += "// Iteration 0: solid cube\n"
|
|
scad += "// Iteration 1: 20 subcubes (remove center + 6 face centers)\n"
|
|
scad += "// Iteration 2: 400 subcubes\n"
|
|
scad += "// Iteration 3: 8000 subcubes (visualized as simplified)\n\n"
|
|
|
|
def menger_cubes(level, x, y, z, size):
|
|
"""Generate cube placements for Menger sponge at given level."""
|
|
if level == 0:
|
|
return [(x, y, z, size)]
|
|
|
|
cubes = []
|
|
new_size = size / 3.0
|
|
for i in range(3):
|
|
for j in range(3):
|
|
for k in range(3):
|
|
# Skip center cube and face centers
|
|
removed = (
|
|
(i == 1 and j == 1) or # center of xy face
|
|
(i == 1 and k == 1) or # center of xz face
|
|
(j == 1 and k == 1) or # center of yz face
|
|
(i == 1 and j == 1 and k == 1) # very center
|
|
)
|
|
if not removed:
|
|
cubes.extend(menger_cubes(
|
|
level - 1,
|
|
x + i * new_size,
|
|
y + j * new_size,
|
|
z + k * new_size,
|
|
new_size
|
|
))
|
|
return cubes
|
|
|
|
# Generate level 3 (simplified: only render centers of level-2 cubes)
|
|
scad += "// Level 3 Menger sponge (simplified for visibility)\n"
|
|
scad += "module sponge_cube(x, y, z, s) {\n"
|
|
scad += " translate([x, y, z]) cube([s, s, s], center=true);\n"
|
|
scad += "}\n\n"
|
|
|
|
# For practical rendering, show a single level-2 sponge
|
|
# and label subcubes with their indices
|
|
scad += "// Level 2 sponge with subcube indices labeled\n"
|
|
scad += "difference() {\n"
|
|
scad += " cube([90, 90, 90], center=true);\n"
|
|
# Remove 7 holes (center + 6 faces)
|
|
centers = [
|
|
(30, 0, 0), (-30, 0, 0), (0, 30, 0), (0, -30, 0), (0, 0, 30), (0, 0, -30),
|
|
(0, 0, 0)
|
|
]
|
|
for cx, cy, cz in centers:
|
|
scad += f" translate([{cx}, {cy}, {cz}]) cube([30.1, 30.1, 30.1], center=true);\n"
|
|
scad += "}\n\n"
|
|
|
|
# Add annotations for valid subcubes
|
|
scad += "// Valid subcubes (remaining after removal)\n"
|
|
scad += "color([0.2, 0.6, 0.9]) {\n"
|
|
idx = 0
|
|
for i in range(3):
|
|
for j in range(3):
|
|
for k in range(3):
|
|
removed = (
|
|
(i == 1 and j == 1) or
|
|
(i == 1 and k == 1) or
|
|
(j == 1 and k == 1) or
|
|
(i == 1 and j == 1 and k == 1)
|
|
)
|
|
if not removed:
|
|
x = (i - 1) * 30
|
|
y = (j - 1) * 30
|
|
z = (k - 1) * 30
|
|
scad += f" translate([{x}, {y}, {z}]) "
|
|
scad += f"cube([28, 28, 28], center=true); // subcube {idx}\n"
|
|
idx += 1
|
|
scad += "}\n"
|
|
|
|
return scad
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# MODEL 3: GABRIEL'S HORN (SURFACE OF REVOLUTION)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def generate_gabriels_horn():
|
|
"""Gabriel's horn: y = 1/x rotated around x-axis.
|
|
Truncated at x=256, visualized as a hollow horn."""
|
|
|
|
scad = scad_header("Gabriel's Horn: y = 1/x Surface of Revolution")
|
|
scad += "// Finite volume, infinite surface area\n"
|
|
scad += "// Visualized as hollow shell (the byte container surface)\n\n"
|
|
|
|
scad += "// Generate horn profile as a polygon\n"
|
|
scad += "// Then rotate_extrude to create the surface\n\n"
|
|
|
|
scad += "module horn_profile() {\n"
|
|
scad += " points = [\n"
|
|
|
|
# Generate profile points: (x, y) where y = 1/x
|
|
points = []
|
|
for n in range(1, 257, 4): # sample every 4 units
|
|
x = n / 20.0 # scale to ~13mm wide
|
|
y = 40.0 / n # scale to start at 40mm, thin to 0.15mm
|
|
points.append((x, y))
|
|
scad += f" [{x:.3f}, {y:.3f}],\n"
|
|
|
|
# Close the polygon
|
|
scad += f" [{points[-1][0]:.3f}, 0],\n"
|
|
scad += " [0.05, 0]\n"
|
|
scad += " ];\n"
|
|
scad += " polygon(points);\n"
|
|
scad += "}\n\n"
|
|
|
|
scad += "// Thick-walled horn (byte positions on surface)\n"
|
|
scad += "difference() {\n"
|
|
scad += " rotate_extrude($fn=64) horn_profile();\n"
|
|
scad += " scale([0.95, 0.95, 0.95]) rotate_extrude($fn=64) horn_profile();\n"
|
|
scad += "}\n\n"
|
|
|
|
# Add position markers along the horn
|
|
scad += "// Byte position markers (sample every 32 positions)\n"
|
|
scad += "color([1, 0.5, 0]) {\n"
|
|
for n in range(1, 257, 32):
|
|
x = n / 20.0
|
|
y = 40.0 / n
|
|
angle = (n * PHI) % (2 * math.pi)
|
|
mx = x
|
|
my = y * math.cos(angle)
|
|
mz = y * math.sin(angle)
|
|
scad += f" translate([{mx:.2f}, {my:.2f}, {mz:.2f}]) "
|
|
scad += f"sphere(r=0.8, $fn=8); // n={n}\n"
|
|
scad += "}\n"
|
|
|
|
return scad
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# MODEL 4: HYPERTORUS SLICE (3D PROJECTION)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def generate_hypertorus_slice():
|
|
"""Hypertorus: take a 3D slice (two angles + one radius).
|
|
This is not the full 4D torus but a visualizable projection."""
|
|
|
|
scad = scad_header("Hypertorus: 3D Slice of 4D Structure")
|
|
scad += "// Full hypertorus has 3 angular coordinates\n"
|
|
scad += "// This model shows a 3D slice: theta, phi, with R=3, r=1\n"
|
|
scad += "// The psi angle is represented by color/phase\n\n"
|
|
|
|
R = 30.0 # major radius
|
|
r = 10.0 # minor radius
|
|
|
|
scad += f"// Major radius R = {R}mm, minor radius r = {r}mm\n"
|
|
scad += "// Points colored by psi angle (the third, unseen dimension)\n\n"
|
|
|
|
scad += "module torus_point(theta, phi, psi, R, r) {\n"
|
|
scad += " x = (R + r * cos(phi)) * cos(theta);\n"
|
|
scad += " y = (R + r * cos(phi)) * sin(theta);\n"
|
|
scad += " z = r * sin(phi);\n"
|
|
scad += " // Color by psi: [red=sin(psi), green=cos(psi), blue=0.5]\n"
|
|
scad += " color([0.5 + 0.5*sin(psi), 0.5 + 0.5*cos(psi), 0.5])\n"
|
|
scad += " translate([x, y, z]) sphere(r=1.5, $fn=8);\n"
|
|
scad += "}\n\n"
|
|
|
|
scad += "// Sample points using irrational rotations by PHI\n"
|
|
for n in range(64):
|
|
theta = (n * PHI) % (2 * math.pi)
|
|
phi = (n * PHI * PHI) % (2 * math.pi)
|
|
psi = (n * PHI * PHI * PHI) % (2 * math.pi)
|
|
scad += f"torus_point({theta:.4f}, {phi:.4f}, {psi:.4f}, {R}, {r});\n"
|
|
|
|
# Add the torus skeleton
|
|
scad += "\n// Torus skeleton (major circle)\n"
|
|
scad += f"color([0.3, 0.3, 0.3]) rotate_extrude($fn=64) "
|
|
scad += f"translate([{R}, 0, 0]) circle(r={r}, $fn=32);\n"
|
|
|
|
return scad
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# MODEL 5: COMPOSITE PATHOLOGICAL MANIFOLD (3D SLICE)
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def generate_composite_slice():
|
|
"""A 3D slice showing the COMPOSITION of structures:
|
|
PIST shells nested inside Menger sponge nodes on a Gabriel's horn.
|
|
This is the most complex model — a true pathological visualization."""
|
|
|
|
scad = scad_header("Composite Pathological Manifold: 3D Slice")
|
|
scad += "// NOT the full 9D structure — a visualizable 3D projection\n"
|
|
scad += "// Shows how PIST shells nest within sponge nodes on the horn\n\n"
|
|
|
|
scad += "// The composite structure:\n"
|
|
scad += "// - Gabriel's horn: backbone (x-axis curve)\n"
|
|
scad += "// - Menger sponge nodes: attached at 8 sample points\n"
|
|
scad += "// - PIST shells: inside each sponge node\n\n"
|
|
|
|
# Sample points along the horn
|
|
sample_positions = [16, 32, 64, 128, 192, 240]
|
|
|
|
scad += "module sponge_node(x_pos, horn_radius) {\n"
|
|
scad += " // Small Menger-like cube cluster at this horn position\n"
|
|
scad += " translate([x_pos, 0, 0]) {\n"
|
|
|
|
# 8 valid subcubes (from 2x2x2, no center removal)
|
|
offsets = [
|
|
(-1, -1, -1), (-1, -1, 1), (-1, 1, -1), (-1, 1, 1),
|
|
(1, -1, -1), (1, -1, 1), (1, 1, -1), (1, 1, 1)
|
|
]
|
|
for idx, (dx, dy, dz) in enumerate(offsets):
|
|
scad += f" translate([{dx*3}, {dy*3}, {dz*3}]) "
|
|
scad += f"cube([4, 4, 4], center=true); // subcube {idx}\n"
|
|
|
|
scad += " }\n"
|
|
scad += "}\n\n"
|
|
|
|
scad += "// Horn backbone\n"
|
|
scad += "color([0.2, 0.2, 0.4]) {\n"
|
|
for i in range(len(sample_positions) - 1):
|
|
x1 = sample_positions[i] / 5.0
|
|
x2 = sample_positions[i+1] / 5.0
|
|
y1 = 20.0 / sample_positions[i] if sample_positions[i] > 0 else 2
|
|
y2 = 20.0 / sample_positions[i+1] if sample_positions[i+1] > 0 else 1
|
|
scad += f" hull() {{\n"
|
|
scad += f" translate([{x1:.1f}, 0, 0]) sphere(r={y1:.1f}, $fn=16);\n"
|
|
scad += f" translate([{x2:.1f}, 0, 0]) sphere(r={y2:.1f}, $fn=16);\n"
|
|
scad += " }\n"
|
|
scad += "}\n\n"
|
|
|
|
scad += "// Sponge nodes at sample positions\n"
|
|
scad += "color([0.8, 0.4, 0.1]) {\n"
|
|
for n in sample_positions:
|
|
x = n / 5.0
|
|
r = 20.0 / n if n > 0 else 2
|
|
scad += f" sponge_node({x:.1f}, {r:.1f}); // n={n}\n"
|
|
scad += "}\n\n"
|
|
|
|
# PIST shell inside each sponge node
|
|
scad += "// PIST shell inside sponge node 0 (n=16, k=4)\n"
|
|
scad += "color([0.1, 0.7, 0.3]) translate([3.2, 0, 0]) {\n"
|
|
k = 4
|
|
radius = k * 1.5
|
|
for t in range(2*k + 1):
|
|
angle = 2 * math.pi * t / (2*k + 1)
|
|
x = radius * math.cos(angle)
|
|
y = radius * math.sin(angle)
|
|
scad += f" translate([{x:.1f}, {y:.1f}, 0]) "
|
|
scad += f"sphere(r=0.5, $fn=8); // t={t}\n"
|
|
scad += "}\n"
|
|
|
|
return scad
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# MAIN
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
def main():
|
|
print("=" * 70)
|
|
print("Generating 3D CAD Models for PIST Manifolds")
|
|
print(f"Output directory: {OUTDIR}")
|
|
print("=" * 70)
|
|
print()
|
|
|
|
write_scad("pist_shells.scad", generate_pist_shells())
|
|
write_scad("menger_sponge.scad", generate_menger_sponge())
|
|
write_scad("gabriels_horn.scad", generate_gabriels_horn())
|
|
write_scad("hypertorus_slice.scad", generate_hypertorus_slice())
|
|
write_scad("composite_manifold.scad", generate_composite_slice())
|
|
|
|
print()
|
|
print("=" * 70)
|
|
print("All models generated.")
|
|
print()
|
|
print("To view: install OpenSCAD (openscad.org)")
|
|
print(" openscad pist_shells.scad &")
|
|
print(" openscad menger_sponge.scad &")
|
|
print(" openscad gabriels_horn.scad &")
|
|
print(" openscad hypertorus_slice.scad &")
|
|
print(" openscad composite_manifold.scad &")
|
|
print()
|
|
print("To export STL for 3D printing:")
|
|
print(" openscad -o pist_shells.stl pist_shells.scad")
|
|
print("=" * 70)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|