// Carved Pumpkin Ornament, consisting of three parts (bottom, top, stem). // By Curt McDowell 2022-10-31 // Licensed under the Creative Commons - Attribution - ShareAlike. // https://creativecommons.org/licenses/by-sa/4.0/ Part = "all"; // [all, stem_only, bottom_only, top_only] Pumpkin_RW = 100; // Horizontal radius Pumpkin_RH = 90; // Vertical radius Halves_Cut_H = 87; // How high to cut in half for 3D printing Skin_T = 2; // Skin thickness Ribs = 20; Rib_D = 2; Facets_H = 48; Facets_R = 400; Eye_Size = 60; Eye_Round_R = 6; Eye_Angle = -68; Eye_Sep_Angle = 55; Nose_Size = 50; Nose_Round_R = 10; Nose_Angle = -88; Mouth_Scale_W = 20; Mouth_Scale_H = 18; Mouth_Angle = -123; Stem_W_Bot = 24; Stem_W_Top = 8; Stem_H = 35; Stem_Facets_W = 13; Stem_Facets_H = 40; Stem_Lean_X = 1 / 80; Stem_Lean_Y = 1 / 72; Stem_Cut = 4; Stem_Twist_Angle = 72; module _end_params(); //// Pumpkin, constructed as one large polyhedron. // Return one ring of points around horizontal circumference of pumpkin. // Function of {0 .. 360} is superimposed on circle to form ripple. ring = function(scale_w, scale_rib_d) let (rib_fn = function(n) -cos(n * Ribs / 2) ^ 18 * Rib_D) [ for (r = [0 : Facets_R - 1]) let (a1 = 360 * r / Facets_R) [(Pumpkin_RW + rib_fn(a1) * scale_rib_d) * scale_w * cos(a1), (Pumpkin_RW + rib_fn(a1) * scale_rib_d) * scale_w * sin(a1), 0] ]; // Height function maps from {-1 .. 1} to {-Pumpkin_RH .. Pumpkin_RH} // Spherical is for testing. Pumpical has concave bottom. height_spherical = function(n) Pumpkin_RH * n; height_pumpical = function(n) Pumpkin_RH * (1.21 * (n - 0.05) - 0.22 * (n - 0.05) ^ 7); height_fn = height_pumpical; // Points are in rings of points from bottom to top. // Top-most and bottom-most rings are degenerate (single point). pump_ps = [ for (f = [0 : Facets_H]) let (n = (2 * f - Facets_H) / Facets_H, // n E {-1 .. 1} h = height_fn(n), scale_w = sqrt(1 - n * n), scale_rib = sqrt(1 - n * n) / (1 + n * n)) for (pts = ring(scale_w, scale_rib)) pts + [0, 0, h], ]; // Faces are ribbons of trapezoids forming rings around circumference pump_fs = [ for (f = [0 : Facets_H - 1]) for (r = [0 : Facets_R - 1]) [(f + 0) * Facets_R + (r + 1) % Facets_R, (f + 0) * Facets_R + (r + 0) % Facets_R, (f + 1) * Facets_R + (r + 0) % Facets_R, (f + 1) * Facets_R + (r + 1) % Facets_R] ]; module pumpkin() polyhedron(points = pump_ps, faces = pump_fs, convexity = 5); module hollow_pumpkin() difference() { pumpkin(); scale([(Pumpkin_RW - Skin_T) / Pumpkin_RW, (Pumpkin_RW - Skin_T) / Pumpkin_RW, (Pumpkin_RH - Skin_T) / Pumpkin_RH]) pumpkin(); } //// Carving // Rounded equilateral triangle with centroid at [0, 0] module rounded_eq_tri(side_len, round_r) let (s2 = side_len / 2, h = side_len * sqrt(3) / 2) minkowski() { polygon([[-s2, -h / 3], [s2, -h / 3], [0, h * 2 / 3]]); circle(r = round_r, $fn = 30); } module cutter(angle_up, angle_side) rotate([0, 0, angle_side]) rotate([angle_up, 180, 0]) translate([0, 0, -Pumpkin_RW * 2]) linear_extrude(Pumpkin_RW * 2, scale = 0) children(); mouth_left = [ [-1, 3], [-1.5, 1.5], [-3.5, 1.5], [-4, 3], [-8, 4], [-5, 1], [-3, 0], [-1.5, 0], [-1, 1.5] ]; module mouth() minkowski() { polygon([each mouth_left, for (i = [len(mouth_left) - 1 : -1 : 0]) [-mouth_left[i][0], mouth_left[i][1]]]); circle(0.2, $fn = 20); } module hollow_pumpkin_carved() difference() { hollow_pumpkin(); cutter(Eye_Angle, -Eye_Sep_Angle / 2) rounded_eq_tri(Eye_Size, Eye_Round_R); cutter(Eye_Angle, Eye_Sep_Angle / 2) rounded_eq_tri(Eye_Size, Eye_Round_R); cutter(Nose_Angle, 0) rounded_eq_tri(Nose_Size, Nose_Round_R); cutter(Mouth_Angle, 0) scale([Mouth_Scale_W, Mouth_Scale_H]) mouth(); } //// Stem, constructed as one large polyhedron stem_DW = Stem_W_Bot - Stem_W_Top; stem_curve = function(n) Stem_W_Bot - (1 - exp(-8 * n)) * stem_DW; // Points form layers of cross-sections that are similar regular polygons. // However, as layers get higher, the polygons shrink, twist, shift // sideways, and tilt down. stem_ps = [ for (h = [0 : Stem_Facets_H]) for (f = [0 : Stem_Facets_W - 1]) let (a = 360 * f / Stem_Facets_W, n = h / Stem_Facets_H, ta = a + n * Stem_Twist_Angle) [cos(ta) * stem_curve(n) + Stem_Lean_X * h ^ 2, sin(ta) * stem_curve(n) + Stem_Lean_Y * h ^ 2, h * Stem_H / Stem_Facets_H - h * cos(ta) * Stem_Cut / Stem_H] ]; // Faces stem_fs = [ [ for (f = [0 : Stem_Facets_W - 1]) f // Bottom ], for (h = [0 : Stem_Facets_H - 1]) // Sides for (f = [0 : Stem_Facets_W - 1]) [ (h + 0) * Stem_Facets_W + (f + 0) % Stem_Facets_W, (h + 1) * Stem_Facets_W + (f + 0) % Stem_Facets_W, (h + 1) * Stem_Facets_W + (f + 1) % Stem_Facets_W, (h + 0) * Stem_Facets_W + (f + 1) % Stem_Facets_W ], [ for (f = [Stem_Facets_W - 1 : -1 : 0]) // Top Stem_Facets_H * Stem_Facets_W + f ] ]; module stem() polyhedron(points = stem_ps, faces = stem_fs); //// Versions for display and 3D printing if (Part == "all") { hollow_pumpkin_carved(); translate([0, 0, height_fn(1)]) stem(); } else if (Part == "stem_only") stem(); else if (Part == "bottom_only") intersection() { translate([0, 0, Pumpkin_RH]) hollow_pumpkin_carved(); translate([-Pumpkin_RW, -Pumpkin_RW, 0]) cube([2 * Pumpkin_RW, 2 * Pumpkin_RW, Halves_Cut_H]); } else if (Part == "top_only") intersection() { translate([0, 0, Pumpkin_RH]) rotate([0, 180, 0]) hollow_pumpkin_carved(); translate([-Pumpkin_RW, -Pumpkin_RW, 0]) cube([2 * Pumpkin_RW, 2 * Pumpkin_RW, Pumpkin_RH * 2 - Halves_Cut_H]); }