-
-
Save DanielHabib/0713ba93cc5505061a736cef00ddb42a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| clifford_torus.py | |
| 18-second 3D voxel animation of a Clifford torus - a flat torus embedded in 4D space, | |
| projected into 3D. The torus morphs through different 4D rotations creating mesmerizing | |
| flowing patterns that reveal the hidden geometry of higher dimensions. | |
| The Clifford torus is defined by: | |
| (x,y,z,w) = (cos(u), sin(u), cos(v), sin(v)) where u,v ∈ [0,2π] | |
| We then apply 4D rotations and project to 3D for visualization. | |
| Run: | |
| pip install spatialstudio numpy | |
| python clifford_torus.py | |
| Outputs: | |
| clifford_torus.splv | |
| """ | |
| import math | |
| import numpy as np | |
| from colorsys import hsv_to_rgb | |
| from spatialstudio import splv | |
| # ------------------------------------------------- | |
| GRID = 128 # cubic voxel grid size | |
| FPS = 30 # frames per second | |
| DURATION = 18 # seconds | |
| U_SAMPLES = 80 # samples along first torus parameter | |
| V_SAMPLES = 80 # samples along second torus parameter | |
| OUTPUT = "../outputs/clifford_torus.splv" | |
| # ------------------------------------------------- | |
| TOTAL_FRAMES = FPS * DURATION | |
| CENTER = np.array([GRID // 2] * 3, dtype=float) | |
| SCALE = GRID * 0.28 # scale factor for the torus | |
| def smoothstep(edge0: float, edge1: float, x: float) -> float: | |
| """Smooth interpolation function""" | |
| t = max(0.0, min(1.0, (x - edge0) / (edge1 - edge0))) | |
| return t * t * (3 - 2 * t) | |
| def hsv_bytes(h: float, s: float = 1.0, v: float = 1.0) -> tuple: | |
| """Convert HSV to RGB bytes""" | |
| h = h % 1.0 | |
| r, g, b = hsv_to_rgb(h, s, v) | |
| return int(r * 255), int(g * 255), int(b * 255) | |
| def clifford_torus_4d(u: float, v: float) -> np.ndarray: | |
| """Generate a point on the Clifford torus in 4D space""" | |
| return np.array([ | |
| math.cos(u), | |
| math.sin(u), | |
| math.cos(v), | |
| math.sin(v) | |
| ]) | |
| def rotate_4d_xy(point_4d: np.ndarray, angle: float) -> np.ndarray: | |
| """Rotate in the XY plane of 4D space""" | |
| cos_a, sin_a = math.cos(angle), math.sin(angle) | |
| result = point_4d.copy() | |
| result[0] = point_4d[0] * cos_a - point_4d[1] * sin_a | |
| result[1] = point_4d[0] * sin_a + point_4d[1] * cos_a | |
| return result | |
| def rotate_4d_zw(point_4d: np.ndarray, angle: float) -> np.ndarray: | |
| """Rotate in the ZW plane of 4D space""" | |
| cos_a, sin_a = math.cos(angle), math.sin(angle) | |
| result = point_4d.copy() | |
| result[2] = point_4d[2] * cos_a - point_4d[3] * sin_a | |
| result[3] = point_4d[2] * sin_a + point_4d[3] * cos_a | |
| return result | |
| def rotate_4d_xz(point_4d: np.ndarray, angle: float) -> np.ndarray: | |
| """Rotate in the XZ plane of 4D space""" | |
| cos_a, sin_a = math.cos(angle), math.sin(angle) | |
| result = point_4d.copy() | |
| result[0] = point_4d[0] * cos_a - point_4d[2] * sin_a | |
| result[2] = point_4d[0] * sin_a + point_4d[2] * cos_a | |
| return result | |
| def rotate_4d_yw(point_4d: np.ndarray, angle: float) -> np.ndarray: | |
| """Rotate in the YW plane of 4D space""" | |
| cos_a, sin_a = math.cos(angle), math.sin(angle) | |
| result = point_4d.copy() | |
| result[1] = point_4d[1] * cos_a - point_4d[3] * sin_a | |
| result[3] = point_4d[1] * sin_a + point_4d[3] * cos_a | |
| return result | |
| def project_4d_to_3d(point_4d: np.ndarray, distance: float = 3.0) -> np.ndarray: | |
| """Project 4D point to 3D using perspective projection""" | |
| # Perspective projection from 4D to 3D | |
| w_offset = distance # distance from 4D hyperplane | |
| scale_factor = w_offset / (w_offset - point_4d[3]) | |
| return np.array([ | |
| point_4d[0] * scale_factor, | |
| point_4d[1] * scale_factor, | |
| point_4d[2] * scale_factor | |
| ]) | |
| def rotate_3d_y(vec: np.ndarray, angle: float) -> np.ndarray: | |
| """Rotate vector around Y axis in 3D""" | |
| cos_a, sin_a = math.cos(angle), math.sin(angle) | |
| return np.array([ | |
| vec[0] * cos_a + vec[2] * sin_a, | |
| vec[1], | |
| -vec[0] * sin_a + vec[2] * cos_a | |
| ]) | |
| def rotate_3d_x(vec: np.ndarray, angle: float) -> np.ndarray: | |
| """Rotate vector around X axis in 3D""" | |
| cos_a, sin_a = math.cos(angle), math.sin(angle) | |
| return np.array([ | |
| vec[0], | |
| vec[1] * cos_a - vec[2] * sin_a, | |
| vec[1] * sin_a + vec[2] * cos_a | |
| ]) | |
| enc = splv.Encoder(GRID, GRID, GRID, framerate=FPS, outputPath=OUTPUT) | |
| print(f"Encoding {TOTAL_FRAMES} frames for Clifford torus animation...") | |
| for frame_idx in range(TOTAL_FRAMES): | |
| global_time = frame_idx / TOTAL_FRAMES # 0 to 1 | |
| # Complex 4D rotation sequence that reveals different aspects of the torus | |
| # Primary rotation through different 4D planes | |
| xy_angle = global_time * 2 * math.pi * 0.7 # slow rotation in XY plane | |
| zw_angle = global_time * 2 * math.pi * 1.3 # faster rotation in ZW plane | |
| # Secondary rotations that create morphing effects | |
| xz_angle = global_time * 2 * math.pi * 0.5 + math.sin(global_time * 4 * math.pi) * 0.3 | |
| yw_angle = global_time * 2 * math.pi * 0.9 + math.cos(global_time * 3 * math.pi) * 0.4 | |
| # 3D viewing rotation for better perspective | |
| view_rot_y = global_time * 2 * math.pi * 0.2 # slow 3D rotation | |
| view_rot_x = 0.3 * math.sin(global_time * 2 * math.pi) # gentle tilt | |
| # Dynamic projection distance for zoom effect | |
| proj_distance = 3.5 + 1.0 * math.sin(global_time * 2 * math.pi) | |
| frame = splv.Frame(GRID, GRID, GRID) | |
| # Generate torus surface | |
| for i in range(U_SAMPLES): | |
| for j in range(V_SAMPLES): | |
| u = (i / U_SAMPLES) * 2 * math.pi | |
| v = (j / V_SAMPLES) * 2 * math.pi | |
| # Generate 4D point on Clifford torus | |
| point_4d = clifford_torus_4d(u, v) | |
| # Apply 4D rotations in sequence | |
| point_4d = rotate_4d_xy(point_4d, xy_angle) | |
| point_4d = rotate_4d_zw(point_4d, zw_angle) | |
| point_4d = rotate_4d_xz(point_4d, xz_angle) | |
| point_4d = rotate_4d_yw(point_4d, yw_angle) | |
| # Project to 3D | |
| point_3d = project_4d_to_3d(point_4d, proj_distance) | |
| # Scale and apply final 3D rotations for viewing | |
| point_3d *= SCALE | |
| point_3d = rotate_3d_y(point_3d, view_rot_y) | |
| point_3d = rotate_3d_x(point_3d, view_rot_x) | |
| # Translate to grid center | |
| world_pos = CENTER + point_3d | |
| x, y, z = world_pos.astype(int) | |
| if 0 <= x < GRID and 0 <= y < GRID and 0 <= z < GRID: | |
| # Create flowing rainbow colors based on torus parameters and time | |
| # Use both u and v to create interesting color patterns | |
| base_hue = (u / (2 * math.pi) * 0.6 + v / (2 * math.pi) * 0.4) % 1.0 | |
| # Add time-based color shifts | |
| time_hue_shift = global_time * 0.3 | |
| # Add 4D rotation effects to color | |
| rotation_hue_shift = (xy_angle + zw_angle) / (4 * math.pi) * 0.2 | |
| # Combine all hue components | |
| hue = (base_hue + time_hue_shift + rotation_hue_shift) % 1.0 | |
| # High saturation for vibrant colors | |
| saturation = 0.9 + 0.1 * math.sin(u * 3 + v * 2 + global_time * 4 * math.pi) | |
| # Brightness varies with 4D depth and creates flowing patterns | |
| w_depth = point_4d[3] # use 4th dimension for depth | |
| brightness = 0.6 + 0.4 * (1 + w_depth) / 2 # normalize w_depth from [-1,1] to [0,1] | |
| # Add pulsing brightness | |
| brightness *= 0.8 + 0.2 * math.sin(global_time * 6 * math.pi + u + v) | |
| brightness = max(0.3, min(1.0, brightness)) | |
| color = hsv_bytes(hue, saturation, brightness) | |
| frame[x, y, z] = color | |
| enc.encode(frame) | |
| # Progress indicator | |
| if frame_idx % FPS == 0: | |
| seconds_done = frame_idx // FPS + 1 | |
| print(f" second {seconds_done} / {DURATION} - Clifford Torus 4D→3D") | |
| enc.finish() | |
| print("Done. Saved", OUTPUT) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment