A jack-o'-lantern is a good first exercise for signed distance functions (SDFs): one formula gives the ribbed body, a second gives the hollow, and a few polygons carve the face. This uses manifold3d and plain Python, with no CAD program.
1. A ribbed pumpkin from one formula
A squashed ellipsoid is (rho/R)^2 + (z/H)^2 = 1. To get ribs, let the radius change with the angle: R(t) = R * (1 + 0.09 * cos(10 t)). level_set samples a function on a grid and returns a mesh where it crosses zero. Positive means inside.
import math from manifold3d import Manifold R, H = 30.0, 25.0 def pumpkin(scale): def f(x, y, z): rho, t = math.hypot(x, y), math.atan2(y, x) Rt = R * scale * (1 + 0.09 * math.cos(10 * t)) d = math.sqrt((rho / Rt) ** 2 + (z / (H * scale)) ** 2) return (1 - d) * 20 return f box = [-36, -36, -30, 36, 36, 30] # minx, miny, minz, maxx, maxy, maxz outer = Manifold.level_set(pumpkin(1.0), box, 0.55) # 0.55 mm grid inner = Manifold.level_set(pumpkin(0.9), box, 0.55) shell = outer - inner # hollow, about 2.5 mm wall shell = shell ^ Manifold.cube([200, 200, 200]).translate([-100, -100, -17]) # flat open base The ^ keeps only the part above z = -17. That gives an open base for an LED tealight and a flat face to print on.
2. Carve the face
Draw each shape as a 2D polygon in the x/z plane, extrude it 50 mm and boolean-subtract it from the front wall.
from manifold3d import CrossSection def poly(pts): area = sum(pts[k][0] * pts[(k + 1) % len(pts)][1] - pts[(k + 1) % len(pts)][0] * pts[k][1] for k in range(len(pts))) pts = pts if area > 0 else pts[::-1] # must be counter-clockwise return CrossSection([pts]).extrude(50).rotate([90, 0, 0]) eye_l = poly([(-17, 6), (-6, 6), (-11.5, 15)]) eye_r = poly([(6, 6), (17, 6), (11.5, 15)]) nose = poly([(-3, 2), (3, 2), (0, -3.5)]) mouth = poly([(-17, -3), (-12, -9), (-8, -5), (-4, -11), (0, -6), (4, -11), (8, -5), (12, -9), (17, -3), (13, -11), (0, -13), (-13, -11)]) for cut in (eye_l, eye_r, nose, mouth): shell = shell - cut 3. A stem with a hang hole
stem = Manifold.cylinder(9, 4.2, 3.2, 24).translate([0, 0, H * 0.9 - 2]) hole = Manifold.cylinder(12, 1.6, 1.6, 20).rotate([90, 0, 0]).translate([0, 6, H * 0.9 + 4.5]) pumpkin_mesh = shell + (stem - hole) Gotchas
-
level_setcalls your function with three numbers(x, y, z), not one tuple. - A clockwise polygon gives an empty
CrossSection, and the boolean silently removes the whole shell. The signed-area check inpolyfixes that. - Keep the bottom of the mouth well above the cut plane. A mouth that reaches the base leaves a thin floppy lip.
- Change the rib count (6, 8, 12) and the squash
H / Rto get a whole family of pumpkins from the same code.
I have not test printed these yet. The meshes come out as a single manifold solid, and the renders look right. The finished lantern and a 3-pack with three shapes and faces are on Cults3D for $1.27.

