Skip to content

Instantly share code, notes, and snippets.

@DanielHabib
Created January 7, 2026 13:08
Show Gist options
  • Select an option

  • Save DanielHabib/2b56dd6d351ef781b7d8b71af026d347 to your computer and use it in GitHub Desktop.

Select an option

Save DanielHabib/2b56dd6d351ef781b7d8b71af026d347 to your computer and use it in GitHub Desktop.
import dgs
import numpy as np
def create_circular_orb(
output_path="circular_orb.dgs",
duration=5.0,
radius=1.0, # Radius of circular path
orb_radius=0.12, # Radius of the orb itself (smaller for tighter look)
num_segments=400, # Even more segments for ultra-smooth motion
gaussians_per_segment=800 # Way more gaussians for ultra-solid appearance
):
"""
Create an orb moving in a circular path using linear motion gaussians.
Circular motion is simulated by creating/destroying gaussians along the path.
"""
rng = np.random.default_rng(42)
print(f"Creating circular orb motion...")
print(f"Circle radius: {radius}")
print(f"Orb radius: {orb_radius}")
print(f"Segments: {num_segments}")
print(f"Duration: {duration}s")
all_positions = []
all_colors = []
all_velocities = []
all_tMeans = []
all_tStdevs = []
# Time per segment
time_per_segment = duration / num_segments
for seg_idx in range(num_segments):
# Current angle on the circle
angle = (seg_idx / num_segments) * 2 * np.pi
# Center position of orb at this segment
center_x = np.cos(angle) * radius
center_y = np.sin(angle) * radius
center_z = 0.0
# Velocity tangent to the circle (perpendicular to radius)
# Tangent vector at this point
tangent_x = -np.sin(angle)
tangent_y = np.cos(angle)
# Speed needed to travel one segment in the time available
# Distance between adjacent points on circle
next_angle = ((seg_idx + 1) / num_segments) * 2 * np.pi
next_x = np.cos(next_angle) * radius
next_y = np.sin(next_angle) * radius
# Distance to travel
segment_distance = np.sqrt((next_x - center_x)**2 + (next_y - center_y)**2)
speed = segment_distance / time_per_segment
# Velocity vector
vel_x = tangent_x * speed
vel_y = tangent_y * speed
vel_z = 0.0
# Time when this segment appears
segment_time = seg_idx * time_per_segment
# Create gaussians forming a solid sphere (the orb)
# Use surface-concentrated distribution for sharper edges
for _ in range(gaussians_per_segment):
# Generate random direction
theta = rng.random() * 2 * np.pi
phi = np.arccos(2 * rng.random() - 1)
# Surface-concentrated radius distribution
# Use power of 5 instead of 1/3 to concentrate near surface
r = rng.random() ** 0.2 # High power = more concentrated at surface
# Convert to Cartesian coordinates
x = r * np.sin(phi) * np.cos(theta)
y = r * np.sin(phi) * np.sin(theta)
z = r * np.cos(phi)
# Scale to orb radius
offset_x = x * orb_radius
offset_y = y * orb_radius
offset_z = z * orb_radius
# Position relative to orb center
pos_x = center_x + offset_x
pos_y = center_y + offset_y
pos_z = center_z + offset_z
all_positions.append([pos_x, pos_y, pos_z])
all_velocities.append([vel_x, vel_y, vel_z])
# Solid colored orb with subtle depth-based shading
hue = (seg_idx / num_segments) * 0.2 + 0.45 # Smooth color cycle
base_color = np.array([
np.sin(2 * np.pi * hue) * 0.5 + 0.5,
np.sin(2 * np.pi * (hue + 0.33)) * 0.5 + 0.5,
np.sin(2 * np.pi * (hue + 0.66)) * 0.5 + 0.5,
])
# Subtle brightness variation - brighter at center, slightly dimmer at edges
distance_from_center = np.sqrt(x*x + y*y + z*z)
brightness = 0.9 + 0.2 * (1.0 - distance_from_center)
color = base_color * brightness
all_colors.append(color)
# Temporal properties - appear during this segment
# Normalized time (0 to 1)
t_mean = segment_time / duration
all_tMeans.append(t_mean)
# Visible for exactly 1 segment for ultra-sharp transitions
t_std = (time_per_segment * 1.0) / duration
all_tStdevs.append(t_std)
total = len(all_positions)
print(f"Total gaussians: {total}")
# Convert to numpy arrays
means = np.array(all_positions, dtype=np.float32)
colors = np.array(all_colors, dtype=np.float32)
velocities = np.array(all_velocities, dtype=np.float32)
# Initialize gaussian arrays
scales = np.zeros((total, 3), np.float32)
rotations = np.zeros((total, 4), np.float32)
opacities = np.ones((total, 1), np.float32)
harmonics = np.zeros((total, 1, 3), np.float32)
tMeans = np.zeros((total, 1), np.float32)
tStdevs = np.zeros((total, 1), np.float32)
print("Setting gaussian properties...")
for i in range(total):
# Get position to calculate orientation
pos = means[i]
seg_idx = i // gaussians_per_segment
angle = (seg_idx / num_segments) * 2 * np.pi
center_x = np.cos(angle) * radius
center_y = np.sin(angle) * radius
# Vector from orb center to this gaussian
dx = pos[0] - center_x
dy = pos[1] - center_y
dz = pos[2] - 0.0
dist = np.sqrt(dx*dx + dy*dy + dz*dz)
# Ultra-thin ellipsoidal gaussians - paper-thin discs oriented radially
# Make them extremely flat in the radial direction for razor-sharp edges
base_scale = 0.012
scale_tangent = base_scale # Larger in tangential directions
scale_radial = base_scale * 0.08 # ULTRA thin in radial direction (paper-thin disc)
scales[i] = np.log([scale_tangent, scale_tangent, scale_radial])
# Calculate rotation to orient gaussian radially
# Point the thin axis (z) toward the center
if dist > 0.001:
# Normalize direction vector
dx /= dist
dy /= dist
dz /= dist
# Create rotation quaternion to align z-axis with radial direction
# Using vector-to-vector rotation
# Default is (0,0,1), want to rotate to (dx,dy,dz)
# Cross product for rotation axis
cross_x = -dy
cross_y = dx
cross_z = 0.0
cross_len = np.sqrt(cross_x*cross_x + cross_y*cross_y + cross_z*cross_z)
# Dot product for rotation angle
dot = dz # dot product with (0,0,1)
if cross_len > 0.001:
# Normalize rotation axis
cross_x /= cross_len
cross_y /= cross_len
cross_z /= cross_len
# Quaternion from axis-angle
angle_rot = np.arccos(np.clip(dot, -1.0, 1.0))
half_angle = angle_rot / 2.0
sin_half = np.sin(half_angle)
qx = cross_x * sin_half
qy = cross_y * sin_half
qz = cross_z * sin_half
qw = np.cos(half_angle)
rotations[i] = [qx, qy, qz, qw]
elif dot < 0:
# 180 degree rotation
rotations[i] = [1.0, 0.0, 0.0, 0.0]
else:
# No rotation needed
rotations[i] = [0.0, 0.0, 0.0, 1.0]
else:
# At center, no specific orientation
rotations[i] = [0.0, 0.0, 0.0, 1.0]
# Set color
color = colors[i]
harmonics[i, 0] = (color - 0.5) / 0.28209479177387814
# Temporal properties
tMeans[i] = all_tMeans[i]
tStdevs[i] = all_tStdevs[i]
# High opacity for solid appearance
opacities[i] = 1.0
print("Creating gaussians...")
# Create gaussians
gaussians = dgs.Gaussians(
means,
scales,
rotations,
opacities,
harmonics,
velocities,
tMeans,
tStdevs,
)
metadata = dgs.Metadata(duration=duration)
dgs.encode(gaussians, metadata, output_path)
print(f"\nWrote {output_path}")
print(f"Duration: {duration}s")
print(f"Total gaussians: {total}")
print(f"Segments: {num_segments}")
print(f"Average gaussians per segment: {total / num_segments:.0f}")
if __name__ == "__main__":
create_circular_orb(
output_path="circular_orb.dgs",
duration=5.0,
radius=1.0,
orb_radius=0.12,
num_segments=400,
gaussians_per_segment=800
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment