Initializing 3D Canvas...

The Sweep

1 min read1 page

A Sweep operation extrudes a 2D Profile Curve along a 3D Rail Curve (also called a path). The Rail defines the trajectory, and the Profile defines the cross-section of the resulting 3D geometry.

Sweep Inputs:

1. Provide a continuous Rail Curve (spline, polyline, etc.). 2. Provide a closed or open Profile Curve. 3. The profile is typically placed at the start of the rail.
python
1# Defining curves for Sweep
2# A sweep requires at least two inputs:
3# 1. Rail Curve: The path the shape follows.
4# 2. Profile Curve: The cross-section shape to extrude.
5
6def define_sweep_inputs():
7 rail = NurbsCurve(points=[(0,0,0), (1,2,0), (2,1,2)])
8 profile = Polygon(radius=0.5, sides=6) # Hexagon
9
10 return rail, profile
Rail Complexity
4.00
1 min read1 page

To move the profile along the rail, we need a local coordinate system (a frame) at every point. The Frenet-Serret formulas provide a mathematical way to define this frame using the curve's derivatives. However, this frame twists wildly when the curve's curvature changes direction (inflection points).

Frenet Frames:

1. Tangent (T): Direction the curve is going. 2. Normal (N): Direction the curve is bending towards. 3. Binormal (B): Orthogonal to both T and N. 4. Problem: N flips 180° at inflection points, causing sudden twisting!
python
1# Frenet-Serret Frame Calculation
2def get_frenet_frame(curve, t):
3 """Calculates T, N, B vectors at parameter t."""
4
5 # Tangent (1st derivative)
6 T = curve.derivative(t).normalize()
7
8 # Normal (2nd derivative)
9 N = curve.second_derivative(t).normalize()
10
11 # Binormal (Cross product)
12 B = T.cross(N).normalize()
13
14 return T, N, B
Position along Curve
0.50
2 min read1 page

To avoid the flipping and twisting of Frenet frames, CAD software usually uses Parallel Transport (Rotation Minimizing Frames). It calculates the first frame, and then sequentially rotates it just enough to align with the new tangent at each step, minimizing any twist around the axis.

Parallel Transport:

1. Calculate an initial frame at start. 2. Step forward along the curve. 3. Find rotation needed to align previous tangent to current tangent. 4. Apply that exact rotation to the previous frame's Normal and Binormal.
python
1# Parallel Transport Frame (Rotation Minimizing Frame)
2def parallel_transport(curve, points):
3 """Generates twist-free frames along a curve."""
4 frames = [initial_frenet_frame(curve, 0)]
5
6 for i in range(1, len(points)):
7 # Get previous frame and current/next tangents
8 prev_frame = frames[i-1]
9 t0 = prev_frame.tangent
10 t1 = get_tangent(curve, points[i])
11
12 # Axis of rotation between tangents
13 axis = t0.cross(t1)
14 angle = math.acos(t0.dot(t1))
15
16 # Rotate previous Normal/Binormal around Axis by Angle
17 new_frame = rotate_frame(prev_frame, axis, angle)
18 frames.append(new_frame)
19
20 return frames
Position along Curve
0.50
2 min read1 page

With the twist-free frames calculated along the rail, we map the 2D coordinates of the Profile Curve onto the 3D axes (Normal and Binormal) of each frame. This places a copy of the profile at every evaluated point.

Profile Mapping:

1. Take 2D (x, y) coordinates of the profile. 2. Multiply x by the frame's Normal vector. 3. Multiply y by the frame's Binormal vector. 4. Add the frame's position (origin).
python
1# Orienting Profile along Rail
2def sweep_profiles(rail, profile, frames):
3 """Orients the profile 2D points to the 3D frames."""
4 oriented_profiles = []
5
6 for frame in frames:
7 current_profile = []
8 for point_2d in profile:
9 # Map 2D x,y to the frame's Normal and Binormal vectors
10 # Map origin to frame.position
11 p_3d = frame.position + (frame.normal * point_2d.x) + (frame.binormal * point_2d.y)
12
13 current_profile.append(p_3d)
14
15 oriented_profiles.append(current_profile)
16
17 return oriented_profiles
Profile Density
10.00
2 min read1 page

A standard sweep keeps the profile size constant. By applying a mathematical function (or a secondary "scale rail") to the profile's 2D coordinates before orienting them, we can dynamically vary the thickness of the sweep along its length.

Dynamic Scaling:

1. Determine the 't' parameter (0 to 1) along the rail. 2. Calculate scale factor at 't' (e.g. using a Graph Mapper or math function). 3. Multiply 2D profile points by scale. 4. Orient scaled profile to 3D frame.
python
1# Adding Scale Variation
2def sweep_with_scale(rail, profile, frames, scale_factor=1.0):
3 """Applies dynamic scaling along the rail."""
4 oriented_profiles = []
5
6 for i, frame in enumerate(frames):
7 # Calculate t parameter [0.0 to 1.0]
8 t = i / (len(frames) - 1)
9
10 # Example: Scale pulses based on a sine wave
11 current_scale = 1.0 + math.sin(t * math.pi * 4) * scale_factor
12
13 current_profile = []
14 for point_2d in profile:
15 # Apply scale before frame orientation
16 sx = point_2d.x * current_scale
17 sy = point_2d.y * current_scale
18
19 p_3d = frame.position + (frame.normal * sx) + (frame.binormal * sy)
20 current_profile.append(p_3d)
21
22 oriented_profiles.append(current_profile)
23
24 return oriented_profiles
Pulse Frequency
2.00
Pulse Amplitude
0.50
3 min read1 page

Once we have the mathematical coordinates of the profiles positioned along the rail, we must weave them together to form a solid surface. We do this by connecting adjacent points on adjacent profiles to form Quads (which are split into two triangles for rendering).

Meshing:

1. Flatten all profile points into a 1D vertex array. 2. Connect point P[i][j] to P[i][j+1], P[i+1][j+1], and P[i+1][j]. 3. Ensure the last point of a closed profile connects back to the first point.
python
1# Generating the Skin (Mesh Topology)
2def generate_sweep_mesh(profiles):
3 """Stitches oriented profiles together into a quad mesh."""
4 vertices = []
5 faces = []
6
7 num_profiles = len(profiles)
8 num_points = len(profiles[0])
9
10 # 1. Flatten all points into a single vertex list
11 for profile in profiles:
12 vertices.extend(profile)
13
14 # 2. Connect vertices to form quads (2 triangles)
15 for i in range(num_profiles - 1):
16 for j in range(num_points):
17 # Calculate indices for the quad corners
18 current = (i * num_points) + j
19 next_pt = (i * num_points) + ((j + 1) % num_points) # Wrap around
20
21 above = current + num_points
22 above_next = next_pt + num_points
23
24 # Triangle 1: current, next, above
25 faces.append([current, next_pt, above])
26 # Triangle 2: next, above_next, above
27 faces.append([next_pt, above_next, above])
28
29 return Mesh(vertices, faces)
Sweep Segments
15.00
Show Wireframe
1.00
2 min read1 page

If the Profile Curve is a closed shape, the resulting Sweep will be a tube with open ends. To turn it into a solid, watertight manifold (a "Brep" or Solid Mesh), we must generate "Caps" at the first and last profiles.

Capping algorithm:

1. Find the centroid (average center) of the first/last profile. 2. Connect the centroid to every point on the edge, creating a triangle fan. 3. Ensure the winding order is reversed for the start cap so normals point outwards!
python
1# Capping the Sweep
2def generate_cap(profile_points, reverse_normals=False):
3 """Creates a planar cap for the open ends of the sweep."""
4 # 1. Calculate centroid of the profile
5 centroid = calculate_centroid(profile_points)
6
7 vertices = [centroid] + profile_points
8 faces = []
9
10 # 2. Create triangle fan from centroid to edge points
11 num_pts = len(profile_points)
12 for i in range(num_pts):
13 p1 = i + 1
14 p2 = ((i + 1) % num_pts) + 1
15
16 # Reverse winding order for the start cap so normals face outwards
17 if reverse_normals:
18 faces.append([0, p2, p1])
19 else:
20 faces.append([0, p1, p2])
21
22 return Mesh(vertices, faces)
Show Caps
0.00