torus visualizations
  • Home

Isotopy: continuous deformation of homeomorphisms

UV Checkerboard Texture

Swirled

The pseudocode for the isotopy is below. The swirl is more intense near the center, and decays exponentially as you approach the border.

Code
img = load_image
x,y = pixel_coordinates(img)

r,t = convert_to_polar(x,y)
radius = r/rmax

swirl = 2pi / exp(radius)  # more intense near center
near_border = where(r > 0.6*rmax)  # threshold for "near the border"

# swirl less near the border
swirl[near_border] *= exp(-40*(radius - 0.6))  # Decay factor for near the border

# linear homotopy b/w swirl and minor swirl
minor_swirl = 0.0000001
swirl_strength = (1-radius)*swirl  + radius*minor_swirl

theta += swirl_strength # add swirl to theta

# convert back to Cartesian coordinates
map_x = r * np.cos(theta) + center_x
map_y = r * np.sin(theta) + center_y

show(img, map_x, map_y)
Code
import cv2
import numpy as np
from PIL import Image

# load image and convert to float32 numpy array
img = np.array(Image.open('uv_checkerboard.png').convert('RGB'))
rows, cols = img.shape[:2]

# create the coordinate grids
# These MUST be float32 for cv2.remap
x_grid, y_grid = np.meshgrid(np.arange(cols), np.arange(rows))
map_x = x_grid.astype(np.float32)
map_y = y_grid.astype(np.float32)


# convert map_x, map_y to polar coordinates
# center the coordinates around the middle of the image
center_x = cols / 2
center_y = rows / 2
# shift the grid to be centered around (0, 0)
shifted_x = map_x - center_x
shifted_y = map_y - center_y
# convert to polar coordinates
r = np.sqrt(shifted_x**2 + shifted_y**2)
theta = np.arctan2(shifted_y, shifted_x)

# swirl the image by adding a function of r to theta
# create a swirl effect that increases with distance from the center
rmax = r.max()
img_id = '1'

main_swirl = 2*np.pi / np.exp(r/rmax)  # more intense near center
near_border = np.where(r > 0.6*rmax)  # Define a threshold for "near the border"

# gradually reduce the swirl strength as you approach the border
main_swirl[near_border] *= np.exp(-40*(r[near_border]/rmax - 0.6))  # Decay factor for near the border
# swirl that decays exponentially as you approach the border, creating a more natural swirl effect
minor_swirl = 0.0000001
swirl_strength = (1-r/rmax)*main_swirl  + r/rmax*minor_swirl

theta += swirl_strength

# convert back to Cartesian coordinates
map_x = r * np.cos(theta) + center_x
map_y = r * np.sin(theta) + center_y

# remap call
# using BORDER_WRAP makes the pixels that fall off the left side
# appear on the right side 
result = cv2.remap(
    img,
    map_x,
    map_y,
    interpolation=cv2.INTER_LINEAR,
    borderMode=cv2.BORDER_WRAP
)

# show it
Image.fromarray(result).show()

# save the image
Image.fromarray(result).save(f'swirled_checkerboard_{img_id}.png')

The swirled image is mapped onto the torus below.

Use your mouse to rotate - Scroll to zoom - Or click and use arrow keys