The Revolve
A Revolve operation (or Lathe) takes a 2D Profile Curve and spins it around a 3D axis. The first step is defining this profile in local coordinates. Usually, the Y-axis acts as the vertical height, and the X-axis acts as the horizontal radius from the center of rotation.
Profile Setup:
1# Defining the Profile Curve for Revolve2# A revolve operation takes a 2D profile curve and sweeps it in a circle.34def define_revolve_profile():5 # A list of 2D points (x, y) where x is radius, y is height6 # Commonly drawn in the positive X half-plane7 profile = [8 Point(1.0, 0.0), # Base outer edge9 Point(1.5, 2.0), # Widest part10 Point(0.5, 4.0), # Neck11 Point(0.8, 5.0) # Rim12 ]13 return profile
To move the 2D profile into 3D, we must define an Axis of Rotation. This is an infinite line in 3D space, defined by an Origin Point and a normalized Direction Vector.
Axis Definition:
(Ox, Oy, Oz). 2. Specify a Direction vector (Dx, Dy, Dz). 3. Normalize the direction vector so its length is exactly 1.0.1# 3D Axis of Rotation2class Axis:3 def __init__(self, origin, direction):4 self.origin = origin # e.g. Point(0, 0, 0)5 self.direction = direction # e.g. Vector(0, 1, 0)6 self.direction.normalize()78# The revolve sweeps the profile around this specific line in 3D space.
To generate the swept geometry, we must rotate every point of the profile around the axis. Rotating around an arbitrary 3D line requires a combination of translation (moving the axis to the origin), rotation (using Rodrigues' rotation formula or quaternions), and translating back.
Arbitrary Rotation:
1# Rotation Matrix around Arbitrary Axis2def rotate_around_axis(point, axis_origin, axis_dir, angle_rad):3 """Rotates a 3D point around an arbitrary 3D line."""4 import numpy as np56 # 1. Translate point so axis origin is at (0,0,0)7 p_translated = point - axis_origin89 # 2. Build Rodrigues' rotation formula matrix10 cos_a = math.cos(angle_rad)11 sin_a = math.sin(angle_rad)12 ux, uy, uz = axis_dir.x, axis_dir.y, axis_dir.z1314 # (Matrix math omitted for brevity)15 R = build_rodrigues_matrix(ux, uy, uz, cos_a, sin_a)1617 # 3. Rotate translated point18 p_rotated = np.dot(R, p_translated)1920 # 4. Translate back21 return p_rotated + axis_origin
By dividing the total rotation angle (usually 360 degrees) into discrete segments, we repeat the rotation process to generate a grid of vertices in 3D space. Each point on the original profile traces a circular path around the axis.
Vertex Generation:
[segment_index][profile_index].1# Sweeping Vertices in Circles2def generate_revolve_vertices(profile, axis, segments, angle_total=2*math.pi):3 """Calculates all 3D points for the revolved shell."""4 all_vertices = []56 # Angle step per segment7 step = angle_total / segments89 for i in range(segments + 1):10 current_angle = i * step1112 # Rotate the entire profile by current_angle13 current_profile = []14 for p in profile:15 p_rot = rotate_around_axis(p, axis.origin, axis.direction, current_angle)16 current_profile.append(p_rot)1718 all_vertices.append(current_profile)1920 return all_vertices
The grid of vertices generated by the sweep must be connected to form a surface. We iterate through the 2D grid, connecting sets of 4 adjacent points into "Quads", which are then split into two triangles for hardware rendering.
Quad Meshing:
grid[segment_idx][profile_idx]. 2. Find 4 corners: (i,j), (i,j+1), (i+1,j), (i+1,j+1). 3. Flatten 2D indices to 1D index: idx = i * profile_length + j. 4. Create 2 triangles per quad ensuring consistent counter-clockwise winding.1# Mesh Topology Indexing for Revolve2def build_revolve_mesh(vertex_grid, num_segments, num_profile_pts):3 """Connects the vertex grid into a solid mesh surface."""4 faces = []56 # Iterate through grid cells (quads)7 for i in range(num_segments):8 for j in range(num_profile_pts - 1):9 # Calculate 1D array indices for the 4 corners of the quad10 current = (i * num_profile_pts) + j11 next_p = current + 112 above = ((i + 1) * num_profile_pts) + j13 above_n = above + 11415 # Split quad into two triangles (Winding order matters!)16 faces.append([current, next_p, above_n])17 faces.append([current, above_n, above])1819 return faces
When a revolve completes a full 360-degree sweep, the last segment touches the first. If we duplicate the vertices at 0° and 360°, lighting engines will treat them as a sharp edge because vertex normals won't average across the gap. We must weld the seam by indexing the last segment directly to the vertices of the first segment.
Seam Welding:
1# Seam Welding for Full Revolves2def weld_revolve_seam(vertices, faces, num_segments, num_profile_pts):3 """Fuses the last segment back to the first for a seamless 360 shell."""45 # We do NOT generate a new set of vertices for segment N.6 # Instead, when i == num_segments - 1, we connect to i == 0.78 for i in range(num_segments):9 for j in range(num_profile_pts - 1):10 current = (i * num_profile_pts) + j11 next_p = current + 11213 # If at the last segment, wrap 'above' index back to 014 if i == num_segments - 1:15 above = j16 else:17 above = ((i + 1) * num_profile_pts) + j1819 above_n = above + 12021 faces.append([current, next_p, above_n])22 faces.append([current, above_n, above])2324 return Mesh(vertices, faces)
The revolve algorithm is extremely powerful for generating axially symmetric objects like bottles, vases, engine parts, and architectural columns.
Interactive Revolve:
1# Revolute Shell Viewer2def generate_wine_glass():3 """Example application: Generating a wine glass profile."""4 profile = [5 Point(0.8, 0.0), # Base outer6 Point(0.2, 0.2), # Base inner7 Point(0.1, 1.5), # Stem8 Point(0.6, 2.5), # Bowl bottom9 Point(1.2, 3.5), # Bowl middle10 Point(1.0, 4.5), # Rim11 ]1213 axis = Axis(Point(0,0,0), Vector(0,1,0))14 return revolve(profile, axis, segments=32)