vibe-openscad

tier-2-offset-handle-mug / o3-2025-04-16

7 run / 累計 641.3s / 累計 $0.3760

openai o3 2025-04-16 bare

bare (provider default effort)

run detail →
render
bare render of tier-2-offset-handle-mug
loading STL…
success 119.6s · 273/6719t · $0.0543
SCAD (68 lines)
// Mug with oriented handle
$fn = 128;

// ----------------- Parameters -----------------
mug_outer_d       = 80;   // mm
mug_inner_d       = 70;   // mm
mug_height        = 90;   // mm
bottom_thickness  = 6;    // mm

handle_inner_h    = 30;   // mm (vertical opening)
handle_inner_w    = 25;   // mm (radial opening)
handle_wall       = 5;    // mm (handle thickness)
handle_depth      = 15;   // mm (depth along Y)

// ------------------------------------------------
mug_outer_r  = mug_outer_d/2;
mug_inner_r  = mug_inner_d/2;

handle_outer_h = handle_inner_h + 2*handle_wall;   // 40 mm
handle_outer_r = handle_outer_h/2;                 // 20 mm
handle_inner_r = handle_inner_h/2;                 // 15 mm

// ----------------- Modules -----------------
module mug_body(){
    difference(){
        cylinder(h = mug_height, r = mug_outer_r, center = false);
        translate([0,0,bottom_thickness])
            cylinder(h = mug_height - bottom_thickness, r = mug_inner_r, center = false);
    }
}

module handle_outer_2d(){
    union(){
        // semicircle
        translate([handle_outer_r,0])
            circle(r = handle_outer_r);
        // rectangle joining to straight side
        translate([0,-handle_outer_r])
            square([handle_outer_r, handle_outer_h]);
    }
}

module handle_inner_2d(){
    union(){
        translate([handle_wall + handle_inner_r,0])
            circle(r = handle_inner_r);
        translate([handle_wall,-handle_inner_r])
            square([handle_inner_r, handle_inner_h]);
    }
}

module handle(){
    // Build 2D D-shape and extrude, then rotate so Z is vertical
    rotate([90,0,0])
        linear_extrude(height = handle_depth, center = true)
            difference(){
                handle_outer_2d();
                handle_inner_2d();
            }
}

// ----------------- Assembly -----------------
union(){
    mug_body();
    // Position handle on +X side, centered in height
    translate([mug_outer_r - 0.01, 0, mug_height/2])
        handle();
}
パラメータ (8)
スライダを動かすと openscad-wasm で再レンダリング

prompt

OpenSCAD で、向きが指定されたマグカップを作ってください。

本体:
- 外径 80mm、内径 70mm(肉厚 5mm)、高さ 90mm の円筒形マグ
- 底面の厚みは 6mm
- 内側は飲み物を入れられるよう中空

取手(handle):
- マグの **+X 軸方向の側面のみ** に付ける(他の方向には付けない)
- 取手は D 字形状(外側が半円、本体側が直線)で、本体外周にしっかり接続する
- 取手の内側の空間は高さ 30mm × 幅 25mm(指 3 本ぐらい入る)
- 取手はマグの高さ方向の中央付近に付ける
- 本体と取手は確実に union され、マニフォールド(隙間や宙吊りなし)を保つ

完成したコード全体を ```openscad ... ``` のフェンスで囲んで出力してください。コードのみで、追加の説明は不要です。

variant: effort

low success
low
51.3s · 273/3192t · $0.0261
SCAD (67 lines)
// Parameters
$fn = 128;

//------------------
// Mug body
//------------------
module mug_body() {
    outer_radius = 40;        // 80 mm outer diameter
    inner_radius = 35;        // 70 mm inner diameter
    height       = 90;
    bottom_thk   = 6;
    
    difference() {
        // Outer shell
        cylinder(h = height, r = outer_radius);
        
        // Inner cavity (raised by bottom thickness)
        translate([0, 0, bottom_thk])
            cylinder(h = height - bottom_thk, r = inner_radius);
    }
}

//------------------
// Handle
//------------------
module handle() {
    inner_h   = 30;   // inner vertical clearance
    inner_w   = 25;   // inner horizontal clearance
    wall_thk  = 5;    // handle wall thickness
    depth     = 10;   // thickness (depth) of handle along Y
    
    // 2-D inner void of the handle (D-shape) in the X–Z plane
    module inner_shape() {
        union() {
            // Rectangle part
            translate([0, -inner_h/2])
                square([inner_w, inner_h], center = false);
            // Semicircular end
            translate([inner_w, 0])
                circle(r = inner_h/2);
        }
    }
    
    // 2-D outer profile (offset of inner shape)
    module outer_shape() {
        offset(delta = wall_thk) inner_shape();
    }
    
    // Build 3-D handle by extruding the 2-D profiles along Y
    translate([40, 0, 45])  // attach to +X side, centered vertically
    difference() {
        // Solid outer shell
        linear_extrude(height = depth, center = true)
            outer_shape();
        // Hollow out the finger space
        linear_extrude(height = depth, center = true)
            inner_shape();
    }
}

//------------------
// Assembly
//------------------
union() {
    mug_body();
    handle();
}
medium (default) success
medium
119.6s · 273/6719t · $0.0543
SCAD (68 lines)
// Mug with oriented handle
$fn = 128;

// ----------------- Parameters -----------------
mug_outer_d       = 80;   // mm
mug_inner_d       = 70;   // mm
mug_height        = 90;   // mm
bottom_thickness  = 6;    // mm

handle_inner_h    = 30;   // mm (vertical opening)
handle_inner_w    = 25;   // mm (radial opening)
handle_wall       = 5;    // mm (handle thickness)
handle_depth      = 15;   // mm (depth along Y)

// ------------------------------------------------
mug_outer_r  = mug_outer_d/2;
mug_inner_r  = mug_inner_d/2;

handle_outer_h = handle_inner_h + 2*handle_wall;   // 40 mm
handle_outer_r = handle_outer_h/2;                 // 20 mm
handle_inner_r = handle_inner_h/2;                 // 15 mm

// ----------------- Modules -----------------
module mug_body(){
    difference(){
        cylinder(h = mug_height, r = mug_outer_r, center = false);
        translate([0,0,bottom_thickness])
            cylinder(h = mug_height - bottom_thickness, r = mug_inner_r, center = false);
    }
}

module handle_outer_2d(){
    union(){
        // semicircle
        translate([handle_outer_r,0])
            circle(r = handle_outer_r);
        // rectangle joining to straight side
        translate([0,-handle_outer_r])
            square([handle_outer_r, handle_outer_h]);
    }
}

module handle_inner_2d(){
    union(){
        translate([handle_wall + handle_inner_r,0])
            circle(r = handle_inner_r);
        translate([handle_wall,-handle_inner_r])
            square([handle_inner_r, handle_inner_h]);
    }
}

module handle(){
    // Build 2D D-shape and extrude, then rotate so Z is vertical
    rotate([90,0,0])
        linear_extrude(height = handle_depth, center = true)
            difference(){
                handle_outer_2d();
                handle_inner_2d();
            }
}

// ----------------- Assembly -----------------
union(){
    mug_body();
    // Position handle on +X side, centered in height
    translate([mug_outer_r - 0.01, 0, mug_height/2])
        handle();
}
high success
high
188.1s · 273/15619t · $0.1255
SCAD (64 lines)
// Mug with oriented D-shaped handle ( +X direction )

$fn = 100;                        // resolution for round parts

// ------- Mug parameters -------
outer_d   = 80;                   // outer diameter
inner_d   = 70;                   // inner diameter
height    = 90;                   // total height
bottom_th = 6;                    // bottom thickness
wall_th   = (outer_d - inner_d)/2; // side wall thickness (5 mm)

// ------- Handle parameters -------
handle_inner_h = 30;              // inner height (Z)
handle_inner_w = 25;              // inner width (Y)
handle_wall    = wall_th;         // handle wall thickness

handle_h   = handle_inner_h + 2*handle_wall;            // total handle height
outer_r    = handle_inner_w/2 + handle_wall;            // outer semicircle radius
inner_r    = outer_r - handle_wall;                     // inner semicircle radius
offset_x   = handle_wall;                               // flat-side thickness

fudge = 0.01; // tiny overlap to guarantee union

// ------- 2D D-shape generator -------
module Dshape(r, off){
    union(){
        // flat-side rectangle
        translate([0, -r]) square([off, 2*r], center=false);
        // semicircle (only x >= 0 half)
        intersection(){
            translate([off, 0]) circle(r=r);
            translate([0, -r]) square([2*r + off, 2*r], center=false);
        }
    }
}

// ------- Mug body -------
module mug(){
    difference(){
        cylinder(d=outer_d, h=height);                           // outer shell
        translate([0, 0, bottom_th])
            cylinder(d=inner_d, h=height - bottom_th);           // inner cavity
    }
}

// ------- Handle -------
module handle(){
    difference(){
        // outer volume
        linear_extrude(height=handle_h, convexity=10)
            Dshape(outer_r, offset_x);
        // inner void
        translate([0, 0, handle_wall])
            linear_extrude(height=handle_inner_h, convexity=10)
                Dshape(inner_r, offset_x + handle_wall);
    }
}

// ------- Assembly -------
union(){
    mug();
    translate([outer_d/2 - fudge, 0, (height - handle_h)/2])
        handle();
}
maxnot run
xhighnot run

iteration: iter-png

i1 success
iter 1
77.6s · 1553/7210t · $0.0608
diff vs bare: +38/-32
- // Mug with oriented handle
+ // Oriented mug with D-shaped handle
  $fn = 128;
  
- // ----------------- Parameters -----------------
- mug_outer_d       = 80;   // mm
- mug_inner_d       = 70;   // mm
- mug_height        = 90;   // mm
- bottom_thickness  = 6;    // mm
+ // ---------- Mug parameters ----------
+ mug_outer_d      = 80;          // mm
+ mug_inner_d      = 70;          // mm
+ mug_height       = 90;          // mm
+ bottom_thickness = 6;           // mm
+ wall_thickness   = (mug_outer_d - mug_inner_d) / 2;   // 5 mm
  
- handle_inner_h    = 30;   // mm (vertical opening)
- handle_inner_w    = 25;   // mm (radial opening)
- handle_wall       = 5;    // mm (handle thickness)
- handle_depth      = 15;   // mm (depth along Y)
+ // ---------- Handle parameters ----------
+ handle_open_h = 30;             // inner void height (Z)
+ handle_open_w = 25;             // inner void radial width (X)
+ handle_wall   = wall_thickness; // thickness around void (5 mm)
+ handle_depth  = 15;             // thickness in Y
  
- // ------------------------------------------------
- mug_outer_r  = mug_outer_d/2;
- mug_inner_r  = mug_inner_d/2;
+ // Derived
+ handle_outer_h = handle_open_h + 2*handle_wall;       // 40
+ handle_inner_r = handle_open_h / 2;                   // 15
+ handle_outer_r = handle_outer_h / 2;                  // 20
  
- handle_outer_h = handle_inner_h + 2*handle_wall;   // 40 mm
- handle_outer_r = handle_outer_h/2;                 // 20 mm
- handle_inner_r = handle_inner_h/2;                 // 15 mm
- 
- // ----------------- Modules -----------------
+ // ---------- Modules ----------
  module mug_body(){
      difference(){
-         cylinder(h = mug_height, r = mug_outer_r, center = false);
+         cylinder(h = mug_height, r = mug_outer_d/2);
          translate([0,0,bottom_thickness])
-             cylinder(h = mug_height - bottom_thickness, r = mug_inner_r, center = false);
+             cylinder(h = mug_height - bottom_thickness, r = mug_inner_d/2);
      }
  }
  
+ // Outer 2-D D-shape (full handle cross-section)
  module handle_outer_2d(){
      union(){
-         // semicircle
-         translate([handle_outer_r,0])
-             circle(r = handle_outer_r);
-         // rectangle joining to straight side
-         translate([0,-handle_outer_r])
+         // straight side against mug
+         translate([0, -handle_outer_h/2])
              square([handle_outer_r, handle_outer_h]);
+         // curved outer side
+         translate([handle_outer_r, 0])
+             circle(r = handle_outer_r);
      }
  }
  
+ // Inner 2-D void to create finger space
  module handle_inner_2d(){
      union(){
-         translate([handle_wall + handle_inner_r,0])
+         // rectangular part of void
+         translate([handle_wall, -handle_open_h/2])
+             square([handle_open_w - handle_inner_r, handle_open_h]);
+         // semicircular part of void
+         translate([handle_wall + handle_open_w - handle_inner_r, 0])
              circle(r = handle_inner_r);
-         translate([handle_wall,-handle_inner_r])
-             square([handle_inner_r, handle_inner_h]);
      }
  }
  
  module handle(){
-     // Build 2D D-shape and extrude, then rotate so Z is vertical
+     // Build 3-D handle by extruding the 2-D profile,
+     // then rotate so Z is vertical and Y is handle depth
      rotate([90,0,0])
          linear_extrude(height = handle_depth, center = true)
              difference(){
                  handle_outer_2d();
                  handle_inner_2d();
              }
  }
  
- // ----------------- Assembly -----------------
+ // ---------- Assembly ----------
  union(){
+     // Mug body
      mug_body();
-     // Position handle on +X side, centered in height
-     translate([mug_outer_r - 0.01, 0, mug_height/2])
+ 
+     // Handle on +X side, centered vertically
+     translate([mug_outer_d/2 - 0.01, 0, (mug_height - handle_outer_h)/2])
          handle();
  }
SCAD (74 lines)
// Oriented mug with D-shaped handle
$fn = 128;

// ---------- Mug parameters ----------
mug_outer_d      = 80;          // mm
mug_inner_d      = 70;          // mm
mug_height       = 90;          // mm
bottom_thickness = 6;           // mm
wall_thickness   = (mug_outer_d - mug_inner_d) / 2;   // 5 mm

// ---------- Handle parameters ----------
handle_open_h = 30;             // inner void height (Z)
handle_open_w = 25;             // inner void radial width (X)
handle_wall   = wall_thickness; // thickness around void (5 mm)
handle_depth  = 15;             // thickness in Y

// Derived
handle_outer_h = handle_open_h + 2*handle_wall;       // 40
handle_inner_r = handle_open_h / 2;                   // 15
handle_outer_r = handle_outer_h / 2;                  // 20

// ---------- Modules ----------
module mug_body(){
    difference(){
        cylinder(h = mug_height, r = mug_outer_d/2);
        translate([0,0,bottom_thickness])
            cylinder(h = mug_height - bottom_thickness, r = mug_inner_d/2);
    }
}

// Outer 2-D D-shape (full handle cross-section)
module handle_outer_2d(){
    union(){
        // straight side against mug
        translate([0, -handle_outer_h/2])
            square([handle_outer_r, handle_outer_h]);
        // curved outer side
        translate([handle_outer_r, 0])
            circle(r = handle_outer_r);
    }
}

// Inner 2-D void to create finger space
module handle_inner_2d(){
    union(){
        // rectangular part of void
        translate([handle_wall, -handle_open_h/2])
            square([handle_open_w - handle_inner_r, handle_open_h]);
        // semicircular part of void
        translate([handle_wall + handle_open_w - handle_inner_r, 0])
            circle(r = handle_inner_r);
    }
}

module handle(){
    // Build 3-D handle by extruding the 2-D profile,
    // then rotate so Z is vertical and Y is handle depth
    rotate([90,0,0])
        linear_extrude(height = handle_depth, center = true)
            difference(){
                handle_outer_2d();
                handle_inner_2d();
            }
}

// ---------- Assembly ----------
union(){
    // Mug body
    mug_body();

    // Handle on +X side, centered vertically
    translate([mug_outer_d/2 - 0.01, 0, (mug_height - handle_outer_h)/2])
        handle();
}
i2 success
iter 2
35.6s · 1627/2468t · $0.0230
diff vs i1: +0/-0
  // Oriented mug with D-shaped handle
  $fn = 128;
  
  // ---------- Mug parameters ----------
  mug_outer_d      = 80;          // mm
  mug_inner_d      = 70;          // mm
  mug_height       = 90;          // mm
  bottom_thickness = 6;           // mm
  wall_thickness   = (mug_outer_d - mug_inner_d) / 2;   // 5 mm
  
  // ---------- Handle parameters ----------
  handle_open_h = 30;             // inner void height (Z)
  handle_open_w = 25;             // inner void radial width (X)
  handle_wall   = wall_thickness; // thickness around void (5 mm)
  handle_depth  = 15;             // thickness in Y
  
  // Derived
  handle_outer_h = handle_open_h + 2*handle_wall;       // 40
  handle_inner_r = handle_open_h / 2;                   // 15
  handle_outer_r = handle_outer_h / 2;                  // 20
  
  // ---------- Modules ----------
  module mug_body(){
      difference(){
          cylinder(h = mug_height, r = mug_outer_d/2);
          translate([0,0,bottom_thickness])
              cylinder(h = mug_height - bottom_thickness, r = mug_inner_d/2);
      }
  }
  
  // Outer 2-D D-shape (full handle cross-section)
  module handle_outer_2d(){
      union(){
          // straight side against mug
          translate([0, -handle_outer_h/2])
              square([handle_outer_r, handle_outer_h]);
          // curved outer side
          translate([handle_outer_r, 0])
              circle(r = handle_outer_r);
      }
  }
  
  // Inner 2-D void to create finger space
  module handle_inner_2d(){
      union(){
          // rectangular part of void
          translate([handle_wall, -handle_open_h/2])
              square([handle_open_w - handle_inner_r, handle_open_h]);
          // semicircular part of void
          translate([handle_wall + handle_open_w - handle_inner_r, 0])
              circle(r = handle_inner_r);
      }
  }
  
  module handle(){
      // Build 3-D handle by extruding the 2-D profile,
      // then rotate so Z is vertical and Y is handle depth
      rotate([90,0,0])
          linear_extrude(height = handle_depth, center = true)
              difference(){
                  handle_outer_2d();
                  handle_inner_2d();
              }
  }
  
  // ---------- Assembly ----------
  union(){
      // Mug body
      mug_body();
  
      // Handle on +X side, centered vertically
      translate([mug_outer_d/2 - 0.01, 0, (mug_height - handle_outer_h)/2])
          handle();
  }
SCAD (74 lines)
// Oriented mug with D-shaped handle
$fn = 128;

// ---------- Mug parameters ----------
mug_outer_d      = 80;          // mm
mug_inner_d      = 70;          // mm
mug_height       = 90;          // mm
bottom_thickness = 6;           // mm
wall_thickness   = (mug_outer_d - mug_inner_d) / 2;   // 5 mm

// ---------- Handle parameters ----------
handle_open_h = 30;             // inner void height (Z)
handle_open_w = 25;             // inner void radial width (X)
handle_wall   = wall_thickness; // thickness around void (5 mm)
handle_depth  = 15;             // thickness in Y

// Derived
handle_outer_h = handle_open_h + 2*handle_wall;       // 40
handle_inner_r = handle_open_h / 2;                   // 15
handle_outer_r = handle_outer_h / 2;                  // 20

// ---------- Modules ----------
module mug_body(){
    difference(){
        cylinder(h = mug_height, r = mug_outer_d/2);
        translate([0,0,bottom_thickness])
            cylinder(h = mug_height - bottom_thickness, r = mug_inner_d/2);
    }
}

// Outer 2-D D-shape (full handle cross-section)
module handle_outer_2d(){
    union(){
        // straight side against mug
        translate([0, -handle_outer_h/2])
            square([handle_outer_r, handle_outer_h]);
        // curved outer side
        translate([handle_outer_r, 0])
            circle(r = handle_outer_r);
    }
}

// Inner 2-D void to create finger space
module handle_inner_2d(){
    union(){
        // rectangular part of void
        translate([handle_wall, -handle_open_h/2])
            square([handle_open_w - handle_inner_r, handle_open_h]);
        // semicircular part of void
        translate([handle_wall + handle_open_w - handle_inner_r, 0])
            circle(r = handle_inner_r);
    }
}

module handle(){
    // Build 3-D handle by extruding the 2-D profile,
    // then rotate so Z is vertical and Y is handle depth
    rotate([90,0,0])
        linear_extrude(height = handle_depth, center = true)
            difference(){
                handle_outer_2d();
                handle_inner_2d();
            }
}

// ---------- Assembly ----------
union(){
    // Mug body
    mug_body();

    // Handle on +X side, centered vertically
    translate([mug_outer_d/2 - 0.01, 0, (mug_height - handle_outer_h)/2])
        handle();
}
i3 success
iter 3
49.6s · 1627/3604t · $0.0321
diff vs i2: +0/-0
  // Oriented mug with D-shaped handle
  $fn = 128;
  
  // ---------- Mug parameters ----------
  mug_outer_d      = 80;          // mm
  mug_inner_d      = 70;          // mm
  mug_height       = 90;          // mm
  bottom_thickness = 6;           // mm
  wall_thickness   = (mug_outer_d - mug_inner_d) / 2;   // 5 mm
  
  // ---------- Handle parameters ----------
  handle_open_h = 30;             // inner void height (Z)
  handle_open_w = 25;             // inner void radial width (X)
  handle_wall   = wall_thickness; // thickness around void (5 mm)
  handle_depth  = 15;             // thickness in Y
  
  // Derived
  handle_outer_h = handle_open_h + 2*handle_wall;       // 40
  handle_inner_r = handle_open_h / 2;                   // 15
  handle_outer_r = handle_outer_h / 2;                  // 20
  
  // ---------- Modules ----------
  module mug_body(){
      difference(){
          cylinder(h = mug_height, r = mug_outer_d/2);
          translate([0,0,bottom_thickness])
              cylinder(h = mug_height - bottom_thickness, r = mug_inner_d/2);
      }
  }
  
  // Outer 2-D D-shape (full handle cross-section)
  module handle_outer_2d(){
      union(){
          // straight side against mug
          translate([0, -handle_outer_h/2])
              square([handle_outer_r, handle_outer_h]);
          // curved outer side
          translate([handle_outer_r, 0])
              circle(r = handle_outer_r);
      }
  }
  
  // Inner 2-D void to create finger space
  module handle_inner_2d(){
      union(){
          // rectangular part of void
          translate([handle_wall, -handle_open_h/2])
              square([handle_open_w - handle_inner_r, handle_open_h]);
          // semicircular part of void
          translate([handle_wall + handle_open_w - handle_inner_r, 0])
              circle(r = handle_inner_r);
      }
  }
  
  module handle(){
      // Build 3-D handle by extruding the 2-D profile,
      // then rotate so Z is vertical and Y is handle depth
      rotate([90,0,0])
          linear_extrude(height = handle_depth, center = true)
              difference(){
                  handle_outer_2d();
                  handle_inner_2d();
              }
  }
  
  // ---------- Assembly ----------
  union(){
      // Mug body
      mug_body();
  
      // Handle on +X side, centered vertically
      translate([mug_outer_d/2 - 0.01, 0, (mug_height - handle_outer_h)/2])
          handle();
  }
SCAD (74 lines)
// Oriented mug with D-shaped handle
$fn = 128;

// ---------- Mug parameters ----------
mug_outer_d      = 80;          // mm
mug_inner_d      = 70;          // mm
mug_height       = 90;          // mm
bottom_thickness = 6;           // mm
wall_thickness   = (mug_outer_d - mug_inner_d) / 2;   // 5 mm

// ---------- Handle parameters ----------
handle_open_h = 30;             // inner void height (Z)
handle_open_w = 25;             // inner void radial width (X)
handle_wall   = wall_thickness; // thickness around void (5 mm)
handle_depth  = 15;             // thickness in Y

// Derived
handle_outer_h = handle_open_h + 2*handle_wall;       // 40
handle_inner_r = handle_open_h / 2;                   // 15
handle_outer_r = handle_outer_h / 2;                  // 20

// ---------- Modules ----------
module mug_body(){
    difference(){
        cylinder(h = mug_height, r = mug_outer_d/2);
        translate([0,0,bottom_thickness])
            cylinder(h = mug_height - bottom_thickness, r = mug_inner_d/2);
    }
}

// Outer 2-D D-shape (full handle cross-section)
module handle_outer_2d(){
    union(){
        // straight side against mug
        translate([0, -handle_outer_h/2])
            square([handle_outer_r, handle_outer_h]);
        // curved outer side
        translate([handle_outer_r, 0])
            circle(r = handle_outer_r);
    }
}

// Inner 2-D void to create finger space
module handle_inner_2d(){
    union(){
        // rectangular part of void
        translate([handle_wall, -handle_open_h/2])
            square([handle_open_w - handle_inner_r, handle_open_h]);
        // semicircular part of void
        translate([handle_wall + handle_open_w - handle_inner_r, 0])
            circle(r = handle_inner_r);
    }
}

module handle(){
    // Build 3-D handle by extruding the 2-D profile,
    // then rotate so Z is vertical and Y is handle depth
    rotate([90,0,0])
        linear_extrude(height = handle_depth, center = true)
            difference(){
                handle_outer_2d();
                handle_inner_2d();
            }
}

// ---------- Assembly ----------
union(){
    // Mug body
    mug_body();

    // Handle on +X side, centered vertically
    translate([mug_outer_d/2 - 0.01, 0, (mug_height - handle_outer_h)/2])
        handle();
}