Project 1

Mapping 4D Geometry

Visualizing 4D objects in a 3D space using vpython modeling and vector geometry.

This project was focused on trying to essentially visualize a 4D object (tesseract) within a 3D layout, which I achieved using vpython and numpy.

To visualize the tesseract in 3 dimensions, I essentially used a projection using this formula:

\[ \begin{bmatrix} x' \\ y' \\ z' \end{bmatrix} = \begin{bmatrix} \frac{x}{w+d} \\ \frac{y}{w+d} \\ \frac{z}{w+d} \end{bmatrix} \]

Rotation in 4D space requires a 4x4 rotation matrix. For simplicity, rotations can be performed in pairs of dimensions (ie, \(xy\)-plane, \(xz\)-plane). A rotation matrix for a plane (say \(xy\)-plane) in 4D is:

\[ R_{xy}(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta & 0 & 0 \\ \sin\theta & \cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} \]

The final rotation is a combination of rotations in different planes:

\[ R(\theta_x, \theta_y, \theta_z) = R_{xy}(\theta_x) R_{xz}(\theta_y) R_{yz}(\theta_z) \]

To make it more interesting, I implemented a slider to adjust rotation speed. Unfortunately, Python does not have a lot of good libraries (afaik) for rendering 3D visuals in a fully separated application, so it currently opens within a web browser.

Technologies Used

Raw Code

import numpy as np
from vpython import canvas, sphere, vector, curve, color, rate, slider, wtext

tesseract_size = 2
projection_distance = 5
rotation_speed = 0.05

scene = canvas(title='4D Visualization', width=800, height=800, center=vector(0,0,0), background=color.black)

# vertices of the tesseract in all possible combinations
vertices_4d = 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], [-1,  1,  1, -1], [-1,  1,  1,  1],
     [1, -1, -1, -1],  [1, -1, -1,  1],  [1, -1,  1, -1],  [1, -1,  1,  1],
     [1,  1, -1, -1],  [1,  1, -1,  1],  [1,  1,  1, -1],  [1,  1,  1,  1]
]) * tesseract_size

# function to project 4d vertices to 3d space
def project_to_3d(vertices_4d):
    vertices_3d = []
    for vertex in vertices_4d:
        w = 1 / (projection_distance - vertex[3])
        x = vertex[0] * w
        y = vertex[1] * w
        z = vertex[2] * w
        vertices_3d.append([x, y, z])
    return np.array(vertices_3d)

# function to rotate vertices in 4d space
def rotate_4d(vertices, angle, axis1, axis2):
    rotation_matrix = np.eye(4)
    cos_angle = np.cos(angle)
    sin_angle = np.sin(angle)
    rotation_matrix[axis1, axis1] = cos_angle
    rotation_matrix[axis1, axis2] = -sin_angle
    rotation_matrix[axis2, axis1] = sin_angle
    rotation_matrix[axis2, axis2] = cos_angle
    return np.dot(vertices, rotation_matrix)

# function to create edges between vertices
def create_edges(vertices_3d):
    edges = [
        (0, 1), (0, 2), (0, 4), (0, 8),
        (1, 3), (1, 5), (1, 9), (2, 3),
        (2, 6), (2, 10), (3, 7), (3, 11),
        (4, 5), (4, 6), (4, 12), (5, 7),
        (5, 13), (6, 7), (6, 14), (7, 15),
        (8, 9), (8, 10), (8, 12), (9, 11),
        (9, 13), (10, 11), (10, 14), (11, 15),
        (12, 13), (12, 14), (13, 15), (14, 15)
    ]
    edge_objects = []
    for edge in edges:
        edge_obj = curve(color=color.white, radius=0.02)
        edge_obj.append(vector(*vertices_3d[edge[0]]), vector(*vertices_3d[edge[1]]))
        edge_objects.append(edge_obj)
    return edge_objects, edges

# initial projection and visualization
vertices_3d = project_to_3d(vertices_4d)
spheres = [sphere(pos=vector(*vertex), radius=0.1, color=color.cyan) for vertex in vertices_3d]
edges, edge_indices = create_edges(vertices_3d)

# control for animation speed
wtext(text="Rotation Speed: ")
speed_slider = slider(min=0.01, max=0.2, value=rotation_speed, length=220, bind=lambda s: set_rotation_speed(s.value))

def set_rotation_speed(value):
    global rotation_speed
    rotation_speed = value

# simulation loop with continuous rotation
while True:
    rate(30)
    # rotate the tesseract in 4D space around different axes
    vertices_4d = rotate_4d(vertices_4d, rotation_speed, 0, 3)
    vertices_4d = rotate_4d(vertices_4d, rotation_speed, 1, 2)
    
    # update the projection and visualization
    vertices_3d = project_to_3d(vertices_4d)
    for i, vertex in enumerate(vertices_3d):
        spheres[i].pos = vector(*vertex)
    for edge, (start, end) in zip(edges, edge_indices):
        edge.modify(0, vector(*vertices_3d[start]))
        edge.modify(1, vector(*vertices_3d[end]))
        

Sources

https://brilliant.org/wiki/tesseract/

https://en.wikipedia.org/wiki/Tesseract