vibe-openscad

tier-3-butt-hinge / gpt-5-mini-2025-08-07

7 run / 累計 1170.2s / 累計 $0.1175

openai gpt 5 mini 2025-08-07 bare

bare (provider default effort)

run detail →
render
bare render of tier-3-butt-hinge
loading STL…
success 234.9s · 592/9787t · $0.0197
SCAD (165 lines)
// openscad
// Small butt hinge for furniture (assembled, 180° open)
// - Two leaves (30x25x2 mm) with alternating knuckles (3 left, 2 right)
// - Pin: Ø4 mm, length 32 mm (1 mm protruding each end)
// - Knuckle outer Ø8 mm, inner bore Ø4.6 mm (Ø4 pin + 0.3 mm clearance)
// - Knuckle positions: Y = [-12, -6, 0, 6, 12] (segments of 6 mm)
// - Left knuckles: Y = [-12, 0, 12] ; Right knuckles: Y = [-6, 6]
// - Countersunk M3 holes (3 per leaf) at Y = [-8, 0, 8], X away from knuckles

$fn = 64;

// Parameters
plate_len = 30;       // Y direction
plate_w = 25;         // X direction (from hinge outward)
thickness = 2;        // Z thickness
half_th = thickness / 2;

knuckle_seg = 6;      // each knuckle segment length along Y
knuckle_positions = [-12, -6, 0, 6, 12];
left_knuckles = [-12, 0, 12];
right_knuckles = [-6, 6];

knuckle_r_out = 4;    // outer radius (Ø8)
knuckle_r_in = 2.3;   // inner bore radius (Ø4.6)

pin_dia = 4;
pin_r = pin_dia / 2;
pin_len = 32;         // along Y (extends 1 mm beyond knuckles each end)

screw_hole_positions = [-8, 0, 8];
screw_countersink_diam = 6;    // top diameter
screw_countersink_depth = 1;
screw_through_dia = 3.2;       // through hole
screw_offset_from_outer_edge = 5; // mm inward from outer edge along X

// Main assembly
union() {
    // Left leaf (x < 0)
    color("silver") left_leaf();

    // Right leaf (x > 0)
    color("silver") right_leaf();

    // Pin (separate part)
    color("gray") pin();
}


// Modules

module left_leaf() {
    // Base plate positioned from X = -25 .. 0, center at X = -12.5
    translate([-plate_w/2, 0, 0])
        difference() {
            // Plate body
            translate([0, 0, 0]) cube([plate_w, plate_len, thickness], center=true);

            // Add knuckles (full cylinders) for left leaf (unioned with plate)
            // We'll union knuckles after creating a union, so place here by union block
            // But since we are in difference(), we need to union the knuckles with the plate first.
            // To simplify, rebuild as union of plate+knuckles then subtract holes below.
        }

    // Build left leaf as union then subtract screw holes and knuckle bores
    translate([-plate_w/2, 0, 0])
        difference() {
            // Union of plate and knuckles
            union() {
                // Plate
                cube([plate_w, plate_len, thickness], center=true);

                // Knuckles (full cylinders along Y) at left_knuckles
                for (ypos = left_knuckles) {
                    translate([0, ypos, 0])
                        rotate([90, 0, 0])
                            cylinder(h=knuckle_seg, r=knuckle_r_out, center=true);
                }
            }

            // Subtract knuckle inner bores (for the left knuckles)
            for (ypos = left_knuckles) {
                translate([0, ypos, 0])
                    rotate([90, 0, 0])
                        cylinder(h= (plate_len + 20), r=knuckle_r_in, center=true);
            }

            // Subtract screw countersink + through holes (on side away from knuckles)
            // For left plate, outer edge at X = -plate_w. We placed plate with center at x=0 in this block,
            // so actual plate X range is [-plate_w/2 .. +plate_w/2] before earlier translate.
            // But we already translated leaf by -plate_w/2 above in outer placement,
            // so here in this block cube was centered and then the whole leaf was translated earlier.
            // To place holes relative to leaf local coordinates (centered), compute local X:
            // Local plate center is at X=0 here; outer edge (away from hinge) is at X = -plate_w/2.
            // Put holes at X = -plate_w/2 + screw_offset_from_outer_edge
            local_hole_x = -plate_w/2 + screw_offset_from_outer_edge;

            for (hy = screw_hole_positions) {
                // Through hole (cylinder along Z)
                translate([local_hole_x, hy, 0])
                    cylinder(h = thickness + 4, r = screw_through_dia/2, center=true);

                // Countersink frustum: place from z = 0 (midplane) up to z = +1 (top surface is +1)
                // Our plate center is z=0, thickness 2 -> top at +1, bottom at -1.
                translate([local_hole_x, hy, 0])
                    // frustum starting at z = 0 up to z = 1 (center=false means base at translate z)
                    // So we need to shift z=0 exactly at plate midplane; this frustum will go from 0..1
                    // which corresponds to center->top half of plate. That's desired: countersink depth=1.
                    cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
            }
        }

    // Now translate the whole left leaf so its center is at X = -12.5 (i.e., spanning -25 .. 0)
    translate([-plate_w/2, 0, 0]) // this matches the earlier top-level placement pattern
        /* no-op to keep transform stack consistent */;
}

// Right leaf module
module right_leaf() {
    // Build right leaf as union then subtract holes and bores, similar to left but mirrored in X
    translate([plate_w/2, 0, 0])
        difference() {
            union() {
                cube([plate_w, plate_len, thickness], center=true);

                // Knuckles for right leaf
                for (ypos = right_knuckles) {
                    translate([0, ypos, 0])
                        rotate([90, 0, 0])
                            cylinder(h=knuckle_seg, r=knuckle_r_out, center=true);
                }
            }

            // Subtract knuckle inner bores (for the right knuckles)
            for (ypos = right_knuckles) {
                translate([0, ypos, 0])
                    rotate([90, 0, 0])
                        cylinder(h= (plate_len + 20), r=knuckle_r_in, center=true);
            }

            // Screw holes on outer side (away from hinge) for right leaf
            // Local plate center at X=0; outer edge at +plate_w/2
            local_hole_x = plate_w/2 - screw_offset_from_outer_edge;

            for (hy = screw_hole_positions) {
                translate([local_hole_x, hy, 0])
                    cylinder(h = thickness + 4, r = screw_through_dia/2, center=true);

                translate([local_hole_x, hy, 0])
                    cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
            }
        }

    // Place it so it spans X = 0 .. +25
    translate([plate_w/2, 0, 0])
        /* no-op to keep transform stack consistent */;
}

// Pin module (separate part)
module pin() {
    // Pin is along Y, center at origin, length pin_len -> extends from Y = -pin_len/2 .. +pin_len/2
    // This will protrude 1 mm beyond knuckles which span Y = -15 .. +15 (pin length 32 => ±16)
    rotate([90, 0, 0]) // rotate cylinder (Z->Y)
        translate([0, 0, 0]) // center already at origin
            cylinder(h = pin_len, r = pin_r, center=true);
}
パラメータ (12)
スライダを動かすと openscad-wasm で再レンダリング

prompt

OpenSCAD で、家具用の小型バット蝶番(butt hinge)を作ってください。
3 つのパーツ(左板、右板、ピン軸)が組み合わさって動作する完成品を、
「開いた状態(180°)」で 1 つの SCAD ファイルに配置して出力します。

寸法と配置:
- 板(leaf)2 枚: 30mm × 25mm × 厚さ 2mm の鉄板形状
  - 縦 30mm の辺がピン軸に沿う(ヒンジ軸方向)
  - 横 25mm の辺が回転で開く方向に伸びる
- ピン軸: 直径 4mm の円柱、長さ 32mm(両端 1mm ずつ knuckle から飛び出す)
- knuckle(筒部): 縦 30mm を 5 等分(各 6mm)に区切り、左板に 3 個・右板に 2 個を
  互い違いに配置(左板は外側 2 個 + 中央 1 個、右板は中間 2 個)
- knuckle 外径 8mm、内径はピン軸 + 0.3mm クリアランス(= 4.6mm 穴)
- 左板と右板はピン軸を共有して回転可能。180° 開いた状態で、両板の
  平らな面が同一平面に来るように配置する
- 各板の knuckle から離れた側に、M3 用の皿穴を 3 個ずつ
  (穴ピッチは板の縦方向に 8mm 間隔、皿穴は表面から見て直径 6mm × 深さ 1mm
  のテーパ + 直径 3.2mm の貫通穴)

座標系:
- ピン軸の中心線を Y 軸に重ねる(ピン軸は +Y 方向)
- 板の平らな面は Z 軸に直交し、左板が x<0 側、右板が x>0 側に伸びる(180° 開)
- knuckle は X=0 を中心とし、Y 方向に 6mm ずつ並ぶ

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

variant: effort

low render_error render
render_error
60.5s · 592/3500t · $0.0071
openscad stl render failed (exit 1): ERROR: Parser error: syntax error in file ../../../../../tmp/render-Hoc1yL/input.scad, line 96
Can't parse file '/tmp/render-Hoc1yL/input.scad'!

SCAD (174 lines)
// OpenSCAD: Butt hinge (小型バット蝶番) — 3 部品を 180° 開いた状態で配置
// Units: mm

// Parameters
plate_len = 30;      // Y direction (hinge axis)
plate_w = 25;        // X direction (extends outward from hinge axis)
plate_th = 2;        // Z thickness

pin_diam = 4;
pin_r = pin_diam/2;
pin_len = 32;        // includes 1mm protrusion at each end beyond knuckles

knuckle_count_total = 5;
knuckle_spacing = plate_len / (knuckle_count_total-1); // 6 mm
knuckle_positions = [-12, -6, 0, 6, 12]; // centers along Y

knuckle_od = 8;
knuckle_id = pin_diam + 0.3; // 4.6 mm
knuckle_rad_od = knuckle_od/2;
knuckle_rad_id = knuckle_id/2;

knuckle_offset_x = 4; // left knuckles center at x = -knuckle_offset_x, right at +knuckle_offset_x
knuckle_z = plate_th; // knuckle thickness in Z equals plate thickness

// Countersink (M3) parameters
csink_top_diam = 6;
csink_top_r = csink_top_diam/2;
csink_depth = 1;
csink_hole_diam = 3.2;
csink_hole_r = csink_hole_diam/2;
csink_taper_bottom_r = csink_hole_r;

// Hole layout on each plate
hole_count = 3;
hole_pitch = 8; // along Y
// place holes centered around Y=0: [-8, 0, 8]
hole_positions_y = [-hole_pitch, 0, hole_pitch];
// holes located toward the far edge of each plate (away from hinge): x = +/- plate_w/2
hole_offset_x = plate_w/2 -  (plate_w/2); // we'll compute absolute position per side

$fn = 60; // resolution

module plate_with_knuckles(side = "left") {
    // side: "left" => occupies x from -plate_w to 0 (extends to negative X)
    //       "right" => occupies x from 0 to +plate_w
    side_sign = side == "left" ? -1 : 1;
    plate_x = side_sign * plate_w;
    // Plate body: positioned so inner edge is at x=0
    translate([side_sign * plate_w/2, 0, 0]) // center plate in X and Y=0, Z=plate_th/2
        difference() {
            // main plate slab
            translate([-plate_w/2, -plate_len/2, 0])
                cube([plate_w, plate_len, plate_th], center=false);
            // subtract knuckle inner holes where knuckles belong to this plate (nothing to subtract from slab)
            // subtract countersink + through-holes
            for (py = hole_positions_y) {
                hx = side_sign * (plate_w/2 -  (plate_w*0.2)); // place holes somewhat inset from edge (20% of width)
                hy = py;
                // move coordinate to plate slab coordinate
                translate([hx, hy, 0])
                    difference() {
                        // countersink tapered frustum: from csink_top_r at top surface (z=plate_th) down to csink_taper_bottom_r at z=plate_th - csink_depth
                        translate([0,0,0])
                            union() {
                                // remove tapered frustum (carve into plate)
                                translate([0,0,0])
                                    rotate([0,0,0])
                                        translate([0,0,0])
                                            // create tapered cone by subtracting cylinder from cone-like shape
                                            // simpler: use cylinder with top radius csink_top_r and bottom radius csink_taper_bottom_r via hull of two cylinders
                                            difference() {
                                                // Make a hull between two circles to get frustum
                                                translate([0,0,plate_th])
                                                    cylinder(h=0.001, r=csink_top_r, center=false);
                                                translate([0,0,plate_th - csink_depth])
                                                    cylinder(h=0.001, r=csink_taper_bottom_r, center=false);
                                            }
                                    ;
                                // through-hole (will actually subtract below)
                            }
                        // now subtract both the frustum and the through cylinder
                        // To perform subtraction we need to place the subtracting shapes in global coordinates; we'll instead perform subtraction in parent difference by replicating here:
                    }
            }
        }
    ;
    // The above approach attempted to subtract holes inside the plate but simpler: build plate then subtract holes/knuckles via separate operations in caller.
}

// Instead we'll construct plates as unions and then subtract holes and knuckle inner bores with overall difference for each plate.

module single_plate(side = "left") {
    side_sign = side == "left" ? -1 : 1;
    // Plate slab
    plate = translate([side_sign * plate_w/2, 0, plate_th/2])
                cube([plate_w, plate_len, plate_th], center=false);
    // Knuckles (outer solid tubes) attached to slab: only on inner edge
    knuckle_centers = side == "left" ? [-12, 0, 12] : [-6, 6];
    knuckles = union();
    for (py = knuckle_centers) {
        // knuckle center at x = side_sign * (-knuckle_offset_x)? For left we want center at x = -knuckle_offset_x
        kx = side == "left" ? -knuckle_offset_x : knuckle_offset_x;
        knuckles = knuckles
            union() {
                translate([kx, py, 0])
                    // create tube by subtracting inner cylinder from outer cylinder, thickness in Z = plate_th
                    difference() {
                        // outer cylinder (make as short box then hull to cylinders to keep Z thickness)
                        translate([-knuckle_rad_od, -knuckle_rad_od, 0])
                            // center the cylinder properly: we'll create cylinder along Z
                            translate([0,0,plate_th/2])
                                cylinder(h=plate_th, r=knuckle_rad_od, center=true);
                        // subtract inner bore
                        translate([0,0,plate_th/2])
                            translate([0,0,0]) // placeholder
                                translate([0,0,0])
                                    cylinder(h=plate_th + 0.2, r=knuckle_rad_id, center=true);
                    }
            };
    }
    // countersunk holes (subtract)
    cs_subtracts = union();
    for (py = hole_positions_y) {
        hx = side_sign * (plate_w/2 - plate_w*0.15); // placed inset 15% from outer edge
        hy = py;
        // position: hole axis along Z, top at z=plate_th
        // create through cylinder for hole and tapered frustum for countersink
        // frustum: hull between two small discs
        frustum = hull() {
            translate([hx, hy, plate_th]) cylinder(h=0.001, r=csink_top_r);
            translate([hx, hy, plate_th - csink_depth]) cylinder(h=0.001, r=csink_taper_bottom_r);
        }
        cs_subtracts = cs_subtracts
            union() {
                frustum;
                translate([hx, hy, -1]) cylinder(h=plate_th+2, r=csink_hole_r, center=false);
            };
    }

    // assemble plate with knuckles, then subtract countersinks and also subtract knuckle bores that pass through to allow pin clearance
    difference() {
        union() {
            plate;
            knuckles;
        }
        // subtract countersinks and through holes
        cs_subtracts;
        // subtract inner bores through knuckles (long cylinders)
        for (py = (side=="left" ? [-12,0,12] : [-6,6])) {
            kx = side == "left" ? -knuckle_offset_x : knuckle_offset_x;
            translate([kx, py, -1]) cylinder(h=plate_th + 2, r=knuckle_rad_id, center=false);
        }
    }
}

// Pin (shaft)
module pin() {
    // centerline along Y, centered at Y=0, X=0. Extend in Y by pin_len with center at 0 -> from -pin_len/2 to +pin_len/2
    translate([0, 0, plate_th/2]) // align pin centered in Z through knuckles
        rotate([90,0,0]) // rotate so cylinder axis along Y (cylinder default along Z)
            cylinder(h=pin_len, r=pin_r, center=true);
}

// Build final assembly: left plate at x negative, right at x positive, pin at center
// Ensure plates are placed so their flat faces (Z faces) coincide in same plane (both lie in XY plane at z=0..2mm)
union() {
    // left plate
    single_plate("left");
    // right plate
    single_plate("right");
    // pin (placed through knuckles). Ensure it extends 1mm beyond knuckle ends (knuckles span 30mm along Y; pin length 32mm)
    // The pin is already centered at Y=0 with length 32mm (=> extends 1mm beyond each end).
    pin();
}
medium (default) success
medium
234.9s · 592/9787t · $0.0197
SCAD (165 lines)
// openscad
// Small butt hinge for furniture (assembled, 180° open)
// - Two leaves (30x25x2 mm) with alternating knuckles (3 left, 2 right)
// - Pin: Ø4 mm, length 32 mm (1 mm protruding each end)
// - Knuckle outer Ø8 mm, inner bore Ø4.6 mm (Ø4 pin + 0.3 mm clearance)
// - Knuckle positions: Y = [-12, -6, 0, 6, 12] (segments of 6 mm)
// - Left knuckles: Y = [-12, 0, 12] ; Right knuckles: Y = [-6, 6]
// - Countersunk M3 holes (3 per leaf) at Y = [-8, 0, 8], X away from knuckles

$fn = 64;

// Parameters
plate_len = 30;       // Y direction
plate_w = 25;         // X direction (from hinge outward)
thickness = 2;        // Z thickness
half_th = thickness / 2;

knuckle_seg = 6;      // each knuckle segment length along Y
knuckle_positions = [-12, -6, 0, 6, 12];
left_knuckles = [-12, 0, 12];
right_knuckles = [-6, 6];

knuckle_r_out = 4;    // outer radius (Ø8)
knuckle_r_in = 2.3;   // inner bore radius (Ø4.6)

pin_dia = 4;
pin_r = pin_dia / 2;
pin_len = 32;         // along Y (extends 1 mm beyond knuckles each end)

screw_hole_positions = [-8, 0, 8];
screw_countersink_diam = 6;    // top diameter
screw_countersink_depth = 1;
screw_through_dia = 3.2;       // through hole
screw_offset_from_outer_edge = 5; // mm inward from outer edge along X

// Main assembly
union() {
    // Left leaf (x < 0)
    color("silver") left_leaf();

    // Right leaf (x > 0)
    color("silver") right_leaf();

    // Pin (separate part)
    color("gray") pin();
}


// Modules

module left_leaf() {
    // Base plate positioned from X = -25 .. 0, center at X = -12.5
    translate([-plate_w/2, 0, 0])
        difference() {
            // Plate body
            translate([0, 0, 0]) cube([plate_w, plate_len, thickness], center=true);

            // Add knuckles (full cylinders) for left leaf (unioned with plate)
            // We'll union knuckles after creating a union, so place here by union block
            // But since we are in difference(), we need to union the knuckles with the plate first.
            // To simplify, rebuild as union of plate+knuckles then subtract holes below.
        }

    // Build left leaf as union then subtract screw holes and knuckle bores
    translate([-plate_w/2, 0, 0])
        difference() {
            // Union of plate and knuckles
            union() {
                // Plate
                cube([plate_w, plate_len, thickness], center=true);

                // Knuckles (full cylinders along Y) at left_knuckles
                for (ypos = left_knuckles) {
                    translate([0, ypos, 0])
                        rotate([90, 0, 0])
                            cylinder(h=knuckle_seg, r=knuckle_r_out, center=true);
                }
            }

            // Subtract knuckle inner bores (for the left knuckles)
            for (ypos = left_knuckles) {
                translate([0, ypos, 0])
                    rotate([90, 0, 0])
                        cylinder(h= (plate_len + 20), r=knuckle_r_in, center=true);
            }

            // Subtract screw countersink + through holes (on side away from knuckles)
            // For left plate, outer edge at X = -plate_w. We placed plate with center at x=0 in this block,
            // so actual plate X range is [-plate_w/2 .. +plate_w/2] before earlier translate.
            // But we already translated leaf by -plate_w/2 above in outer placement,
            // so here in this block cube was centered and then the whole leaf was translated earlier.
            // To place holes relative to leaf local coordinates (centered), compute local X:
            // Local plate center is at X=0 here; outer edge (away from hinge) is at X = -plate_w/2.
            // Put holes at X = -plate_w/2 + screw_offset_from_outer_edge
            local_hole_x = -plate_w/2 + screw_offset_from_outer_edge;

            for (hy = screw_hole_positions) {
                // Through hole (cylinder along Z)
                translate([local_hole_x, hy, 0])
                    cylinder(h = thickness + 4, r = screw_through_dia/2, center=true);

                // Countersink frustum: place from z = 0 (midplane) up to z = +1 (top surface is +1)
                // Our plate center is z=0, thickness 2 -> top at +1, bottom at -1.
                translate([local_hole_x, hy, 0])
                    // frustum starting at z = 0 up to z = 1 (center=false means base at translate z)
                    // So we need to shift z=0 exactly at plate midplane; this frustum will go from 0..1
                    // which corresponds to center->top half of plate. That's desired: countersink depth=1.
                    cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
            }
        }

    // Now translate the whole left leaf so its center is at X = -12.5 (i.e., spanning -25 .. 0)
    translate([-plate_w/2, 0, 0]) // this matches the earlier top-level placement pattern
        /* no-op to keep transform stack consistent */;
}

// Right leaf module
module right_leaf() {
    // Build right leaf as union then subtract holes and bores, similar to left but mirrored in X
    translate([plate_w/2, 0, 0])
        difference() {
            union() {
                cube([plate_w, plate_len, thickness], center=true);

                // Knuckles for right leaf
                for (ypos = right_knuckles) {
                    translate([0, ypos, 0])
                        rotate([90, 0, 0])
                            cylinder(h=knuckle_seg, r=knuckle_r_out, center=true);
                }
            }

            // Subtract knuckle inner bores (for the right knuckles)
            for (ypos = right_knuckles) {
                translate([0, ypos, 0])
                    rotate([90, 0, 0])
                        cylinder(h= (plate_len + 20), r=knuckle_r_in, center=true);
            }

            // Screw holes on outer side (away from hinge) for right leaf
            // Local plate center at X=0; outer edge at +plate_w/2
            local_hole_x = plate_w/2 - screw_offset_from_outer_edge;

            for (hy = screw_hole_positions) {
                translate([local_hole_x, hy, 0])
                    cylinder(h = thickness + 4, r = screw_through_dia/2, center=true);

                translate([local_hole_x, hy, 0])
                    cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
            }
        }

    // Place it so it spans X = 0 .. +25
    translate([plate_w/2, 0, 0])
        /* no-op to keep transform stack consistent */;
}

// Pin module (separate part)
module pin() {
    // Pin is along Y, center at origin, length pin_len -> extends from Y = -pin_len/2 .. +pin_len/2
    // This will protrude 1 mm beyond knuckles which span Y = -15 .. +15 (pin length 32 => ±16)
    rotate([90, 0, 0]) // rotate cylinder (Z->Y)
        translate([0, 0, 0]) // center already at origin
            cylinder(h = pin_len, r = pin_r, center=true);
}
high success
high
409.4s · 592/20688t · $0.0415
SCAD (127 lines)
// Butt hinge (butt hinge) OpenSCAD model
// 3 parts (left leaf, right leaf, pin) assembled in 180° open position
// Units: mm

$fn = 64; // global resolution for round parts

// --- Parameters ---
board_length      = 30;      // Y direction (hinge axis)
board_width       = 25;      // X direction (opening direction)
board_thickness   = 2;       // Z direction (plate thickness)

knuckle_segments  = 5;
knuckle_length    = board_length / knuckle_segments; // 6 mm each
knuckle_od        = 8;       // outer diameter
knuckle_id        = 4.6;     // inner diameter (pin + 0.3 mm clearance)

pin_diameter      = 4;
pin_length        = 32;      // 1 mm protruding both ends beyond knuckles

// Knuckle centers along Y (centered at Y=0)
knuckle_positions = [ -12, -6, 0, 6, 12 ];
left_knuckles     = [ -12, 0, 12 ];
right_knuckles    = [ -6, 6 ];

// Screw (M3) countersunk holes
hole_pitch        = 8;
hole_y            = [-8, 0, 8]; // three holes (8 mm pitch)
hole_margin       = 5;          // from outer edge of plate
hole_x_left       = -board_width + hole_margin; // -20
hole_x_right      =  board_width - hole_margin; // +20
countersink_diam  = 6;
countersink_depth = 1;
through_diam      = 3.2;

// --- Helper modules ---

module countersunk_hole_at(x, y) {
    // Countersink from top surface (z = board_thickness) downward by countersink_depth,
    // then a through hole of diameter through_diam for the remaining thickness.
    union() {
        // through hole (bottom part): from z=0 to z=(board_thickness - countersink_depth)
        translate([x, y, 0])
            cylinder(h = board_thickness - countersink_depth, r = through_diam/2, center = false, $fn=32);

        // countersink frustum: from z=(board_thickness - countersink_depth) to z=board_thickness
        translate([x, y, board_thickness - countersink_depth])
            cylinder(h = countersink_depth, r1 = through_diam/2, r2 = countersink_diam/2, center = false, $fn=32);
    }
}

// --- Part modules ---

module left_leaf() {
    difference() {
        union() {
            // left plate: occupies x in [-board_width .. 0], y in [-board_length/2 .. +board_length/2], z in [0 .. board_thickness]
            translate([-board_width, -board_length/2, 0])
                cube([board_width, board_length, board_thickness], center = false);

            // left knuckles (half-tubes for x <= 0)
            for (pos = left_knuckles) {
                intersection() {
                    // knuckle (outer tube minus inner bore), oriented along Y
                    translate([0, pos, 0]) rotate([-90,0,0])
                        difference() {
                            // outer cylinder
                            cylinder(h = knuckle_length, r = knuckle_od/2, center = true, $fn=64);
                            // inner bore (slightly longer to ensure clean subtraction)
                            cylinder(h = knuckle_length + 2, r = knuckle_id/2, center = true, $fn=64);
                        }
                    // clip to x <= 0 (keep negative X half)
                    translate([-200, pos - knuckle_length/2 - 1, -200])
                        cube([200, knuckle_length + 2, 400], center = false);
                }
            }
        }

        // subtract countersunk screw holes (on side away from knuckle)
        for (ypos = hole_y) {
            countersunk_hole_at(hole_x_left, ypos);
        }
    }
}

module right_leaf() {
    difference() {
        union() {
            // right plate: occupies x in [0 .. board_width]
            translate([0, -board_length/2, 0])
                cube([board_width, board_length, board_thickness], center = false);

            // right knuckles (half-tubes for x >= 0)
            for (pos = right_knuckles) {
                intersection() {
                    // knuckle (outer tube minus inner bore), oriented along Y
                    translate([0, pos, 0]) rotate([-90,0,0])
                        difference() {
                            cylinder(h = knuckle_length, r = knuckle_od/2, center = true, $fn=64);
                            cylinder(h = knuckle_length + 2, r = knuckle_id/2, center = true, $fn=64);
                        }
                    // clip to x >= 0 (keep positive X half)
                    translate([0, pos - knuckle_length/2 - 1, -200])
                        cube([200, knuckle_length + 2, 400], center = false);
                }
            }
        }

        // subtract countersunk screw holes (on side away from knuckle)
        for (ypos = hole_y) {
            countersunk_hole_at(hole_x_right, ypos);
        }
    }
}

module hinge_pin() {
    // Pin axis must be along +Y direction with centerline at Y=0.
    // Create cylinder centered at origin and rotate its axis to +Y.
    rotate([-90, 0, 0])
        cylinder(h = pin_length, r = pin_diameter/2, center = true, $fn = 64);
}

// --- Assembly (180° open: both leaves coplanar in XY plane, pin along Y) ---

// Optionally color parts for visual distinction:
color([0.8,0.8,0.8]) left_leaf();    // light gray left leaf
color([0.6,0.6,0.6]) right_leaf();   // darker gray right leaf
color([0.2,0.2,0.8]) hinge_pin();    // blue pin
maxnot run
xhighnot run

iteration: iter-png

i1 success
iter 1
82.7s · 3132/5169t · $0.0111
diff vs bare: +97/-101
  // openscad
- // Small butt hinge for furniture (assembled, 180° open)
- // - Two leaves (30x25x2 mm) with alternating knuckles (3 left, 2 right)
- // - Pin: Ø4 mm, length 32 mm (1 mm protruding each end)
- // - Knuckle outer Ø8 mm, inner bore Ø4.6 mm (Ø4 pin + 0.3 mm clearance)
- // - Knuckle positions: Y = [-12, -6, 0, 6, 12] (segments of 6 mm)
- // - Left knuckles: Y = [-12, 0, 12] ; Right knuckles: Y = [-6, 6]
- // - Countersunk M3 holes (3 per leaf) at Y = [-8, 0, 8], X away from knuckles
+ // Corrected small butt hinge (assembled, 180° open)
+ // - Two leaves (30 x 25 x 2 mm), pin along Y, leaves lie flat in same plane (Z)
+ // - Knuckles: 5 segments (each 6 mm long along Y) at Y = [-12, -6, 0, 6, 12]
+ //   Left leaf knuckles at [-12, 0, 12], right leaf at [-6, 6]
+ // - Knuckle outer Ø8 mm, inner bore Ø4.6 mm
+ // - Pin Ø4 mm, length 32 mm (1 mm protruding each end)
+ // - Countersunk M3 holes (3 per leaf) at Y = [-8, 0, 8], 5 mm from outer edge
+ //   Countersink Ø6 mm x depth 1 mm (from top surface) + through Ø3.2 mm
  
  $fn = 64;
  
- // Parameters
- plate_len = 30;       // Y direction
- plate_w = 25;         // X direction (from hinge outward)
- thickness = 2;        // Z thickness
+ // Dimensions
+ plate_len = 30;       // along Y
+ plate_w = 25;         // along X (from hinge outward)
+ thickness = 2;        // along Z
  half_th = thickness / 2;
  
- knuckle_seg = 6;      // each knuckle segment length along Y
+ knuckle_seg = 6;      // each segment length along Y
  knuckle_positions = [-12, -6, 0, 6, 12];
  left_knuckles = [-12, 0, 12];
  right_knuckles = [-6, 6];
  
  knuckle_r_out = 4;    // outer radius (Ø8)
  knuckle_r_in = 2.3;   // inner bore radius (Ø4.6)
  
  pin_dia = 4;
  pin_r = pin_dia / 2;
- pin_len = 32;         // along Y (extends 1 mm beyond knuckles each end)
+ pin_len = 32;         // along Y
  
  screw_hole_positions = [-8, 0, 8];
  screw_countersink_diam = 6;    // top diameter
  screw_countersink_depth = 1;
  screw_through_dia = 3.2;       // through hole
  screw_offset_from_outer_edge = 5; // mm inward from outer edge along X
  
- // Main assembly
+ // Main assembly: left (x<0), right (x>0), pin along Y at X=0
  union() {
-     // Left leaf (x < 0)
+     // Left leaf: spans X = -plate_w .. 0, Y = -plate_len/2 .. +plate_len/2, Z = -half_th .. +half_th
      color("silver") left_leaf();
  
-     // Right leaf (x > 0)
+     // Right leaf: spans X = 0 .. +plate_w
      color("silver") right_leaf();
  
-     // Pin (separate part)
+     // Pin (separate part), centered at origin, axis along Y
      color("gray") pin();
  }
  
  
  // Modules
  
  module left_leaf() {
-     // Base plate positioned from X = -25 .. 0, center at X = -12.5
-     translate([-plate_w/2, 0, 0])
-         difference() {
-             // Plate body
-             translate([0, 0, 0]) cube([plate_w, plate_len, thickness], center=true);
+     // Base plate
+     translate([-plate_w, -plate_len/2, -half_th])
+         union() {
+             cube([plate_w, plate_len, thickness]); // X: -plate_w..0
  
-             // Add knuckles (full cylinders) for left leaf (unioned with plate)
-             // We'll union knuckles after creating a union, so place here by union block
-             // But since we are in difference(), we need to union the knuckles with the plate first.
-             // To simplify, rebuild as union of plate+knuckles then subtract holes below.
+             // Left knuckles (cylinders along Y), centered at X = 0
+             for (ypos = left_knuckles) {
+                 // place cylinder so its centerline is at X=0 (so translate by +0 in this local coords),
+                 // but since plate origin is at X = -plate_w, knuckle X in world = 0 => local X = plate_w
+                 translate([plate_w, ypos + plate_len/2, 0])  // adjust Y because cube origin is at -plate_len/2
+                     // rotate so cylinder axis is along Y: default is along Z, rotate X by 90 degrees
+                     rotate([90, 0, 0])
+                         cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
+             }
+ 
+             // Subtract inner bores of left knuckles (make them tubular)
+             // Use difference outside union by doing a subtraction block after union
          }
  
-     // Build left leaf as union then subtract screw holes and knuckle bores
-     translate([-plate_w/2, 0, 0])
-         difference() {
-             // Union of plate and knuckles
+     // Subtract bores and screw holes (perform difference)
+     // We'll reconstruct with difference: plate+knuckles minus bores and screw holes
+     // Build plate+knuckles again and subtract
+     translate([0,0,0]) difference() {
+         // plate+knuckles
+         translate([-plate_w, -plate_len/2, -half_th]) {
              union() {
-                 // Plate
-                 cube([plate_w, plate_len, thickness], center=true);
+                 cube([plate_w, plate_len, thickness]);
  
-                 // Knuckles (full cylinders along Y) at left_knuckles
                  for (ypos = left_knuckles) {
-                     translate([0, ypos, 0])
+                     translate([plate_w, ypos + plate_len/2, 0])
                          rotate([90, 0, 0])
-                             cylinder(h=knuckle_seg, r=knuckle_r_out, center=true);
+                             cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
                  }
              }
+         }
  
-             // Subtract knuckle inner bores (for the left knuckles)
-             for (ypos = left_knuckles) {
-                 translate([0, ypos, 0])
-                     rotate([90, 0, 0])
-                         cylinder(h= (plate_len + 20), r=knuckle_r_in, center=true);
-             }
+         // subtract knuckle bores: long cylinders along Y centered at X=0 (world coords)
+         for (ypos = left_knuckles) {
+             // Bore center at X=0, Y=ypos
+             translate([0, ypos, -10]) // start below plates
+                 rotate([90, 0, 0])
+                     cylinder(h = 20 + plate_len + 40, r = knuckle_r_in, center=false);
+         }
  
-             // Subtract screw countersink + through holes (on side away from knuckles)
-             // For left plate, outer edge at X = -plate_w. We placed plate with center at x=0 in this block,
-             // so actual plate X range is [-plate_w/2 .. +plate_w/2] before earlier translate.
-             // But we already translated leaf by -plate_w/2 above in outer placement,
-             // so here in this block cube was centered and then the whole leaf was translated earlier.
-             // To place holes relative to leaf local coordinates (centered), compute local X:
-             // Local plate center is at X=0 here; outer edge (away from hinge) is at X = -plate_w/2.
-             // Put holes at X = -plate_w/2 + screw_offset_from_outer_edge
-             local_hole_x = -plate_w/2 + screw_offset_from_outer_edge;
+         // subtract screw through holes and countersinks (on outward face X = -plate_w)
+         for (hy = screw_hole_positions) {
+             hole_x = -plate_w + screw_offset_from_outer_edge;
+             // through hole (extend beyond plate thickness)
+             translate([hole_x, hy, -half_th - 1])
+                 cylinder(h = thickness + 2, r = screw_through_dia/2, center=false);
+             // countersink from top surface down screw_countersink_depth
+             translate([hole_x, hy, half_th - screw_countersink_depth])
+                 cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
+         }
+     }
+ }
  
-             for (hy = screw_hole_positions) {
-                 // Through hole (cylinder along Z)
-                 translate([local_hole_x, hy, 0])
-                     cylinder(h = thickness + 4, r = screw_through_dia/2, center=true);
+ module right_leaf() {
+     // Base plate spanning X=0..+plate_w
+     translate([0, -plate_len/2, -half_th])
+         union() {
+             cube([plate_w, plate_len, thickness]); // X: 0..plate_w
  
-                 // Countersink frustum: place from z = 0 (midplane) up to z = +1 (top surface is +1)
-                 // Our plate center is z=0, thickness 2 -> top at +1, bottom at -1.
-                 translate([local_hole_x, hy, 0])
-                     // frustum starting at z = 0 up to z = 1 (center=false means base at translate z)
-                     // So we need to shift z=0 exactly at plate midplane; this frustum will go from 0..1
-                     // which corresponds to center->top half of plate. That's desired: countersink depth=1.
-                     cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
+             // Right knuckles at world X=0 -> local X = 0
+             for (ypos = right_knuckles) {
+                 translate([0, ypos + plate_len/2, 0])
+                     rotate([90, 0, 0])
+                         cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
              }
          }
  
-     // Now translate the whole left leaf so its center is at X = -12.5 (i.e., spanning -25 .. 0)
-     translate([-plate_w/2, 0, 0]) // this matches the earlier top-level placement pattern
-         /* no-op to keep transform stack consistent */;
- }
- 
- // Right leaf module
- module right_leaf() {
-     // Build right leaf as union then subtract holes and bores, similar to left but mirrored in X
-     translate([plate_w/2, 0, 0])
-         difference() {
+     // Subtract bores and screw holes
+     translate([0,0,0]) difference() {
+         translate([0, -plate_len/2, -half_th]) {
              union() {
-                 cube([plate_w, plate_len, thickness], center=true);
+                 cube([plate_w, plate_len, thickness]);
  
-                 // Knuckles for right leaf
                  for (ypos = right_knuckles) {
-                     translate([0, ypos, 0])
+                     translate([0, ypos + plate_len/2, 0])
                          rotate([90, 0, 0])
-                             cylinder(h=knuckle_seg, r=knuckle_r_out, center=true);
+                             cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
                  }
              }
+         }
  
-             // Subtract knuckle inner bores (for the right knuckles)
-             for (ypos = right_knuckles) {
-                 translate([0, ypos, 0])
-                     rotate([90, 0, 0])
-                         cylinder(h= (plate_len + 20), r=knuckle_r_in, center=true);
-             }
- 
-             // Screw holes on outer side (away from hinge) for right leaf
-             // Local plate center at X=0; outer edge at +plate_w/2
-             local_hole_x = plate_w/2 - screw_offset_from_outer_edge;
- 
-             for (hy = screw_hole_positions) {
-                 translate([local_hole_x, hy, 0])
-                     cylinder(h = thickness + 4, r = screw_through_dia/2, center=true);
- 
-                 translate([local_hole_x, hy, 0])
-                     cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
-             }
+         // subtract knuckle bores at world X=0
+         for (ypos = right_knuckles) {
+             translate([0, ypos, -10])
+                 rotate([90, 0, 0])
+                     cylinder(h = 20 + plate_len + 40, r = knuckle_r_in, center=false);
          }
  
-     // Place it so it spans X = 0 .. +25
-     translate([plate_w/2, 0, 0])
-         /* no-op to keep transform stack consistent */;
+         // screw holes on outer face X = +plate_w
+         for (hy = screw_hole_positions) {
+             hole_x = plate_w - screw_offset_from_outer_edge;
+             translate([hole_x, hy, -half_th - 1])
+                 cylinder(h = thickness + 2, r = screw_through_dia/2, center=false);
+             translate([hole_x, hy, half_th - screw_countersink_depth])
+                 cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
+         }
+     }
  }
  
- // Pin module (separate part)
  module pin() {
-     // Pin is along Y, center at origin, length pin_len -> extends from Y = -pin_len/2 .. +pin_len/2
-     // This will protrude 1 mm beyond knuckles which span Y = -15 .. +15 (pin length 32 => ±16)
-     rotate([90, 0, 0]) // rotate cylinder (Z->Y)
-         translate([0, 0, 0]) // center already at origin
+     // Pin axis along Y, center at origin. Rotate cylinder (Z->Y).
+     translate([0, 0, 0])
+         rotate([90, 0, 0])
              cylinder(h = pin_len, r = pin_r, center=true);
  }
SCAD (161 lines)
// openscad
// Corrected small butt hinge (assembled, 180° open)
// - Two leaves (30 x 25 x 2 mm), pin along Y, leaves lie flat in same plane (Z)
// - Knuckles: 5 segments (each 6 mm long along Y) at Y = [-12, -6, 0, 6, 12]
//   Left leaf knuckles at [-12, 0, 12], right leaf at [-6, 6]
// - Knuckle outer Ø8 mm, inner bore Ø4.6 mm
// - Pin Ø4 mm, length 32 mm (1 mm protruding each end)
// - Countersunk M3 holes (3 per leaf) at Y = [-8, 0, 8], 5 mm from outer edge
//   Countersink Ø6 mm x depth 1 mm (from top surface) + through Ø3.2 mm

$fn = 64;

// Dimensions
plate_len = 30;       // along Y
plate_w = 25;         // along X (from hinge outward)
thickness = 2;        // along Z
half_th = thickness / 2;

knuckle_seg = 6;      // each segment length along Y
knuckle_positions = [-12, -6, 0, 6, 12];
left_knuckles = [-12, 0, 12];
right_knuckles = [-6, 6];

knuckle_r_out = 4;    // outer radius (Ø8)
knuckle_r_in = 2.3;   // inner bore radius (Ø4.6)

pin_dia = 4;
pin_r = pin_dia / 2;
pin_len = 32;         // along Y

screw_hole_positions = [-8, 0, 8];
screw_countersink_diam = 6;    // top diameter
screw_countersink_depth = 1;
screw_through_dia = 3.2;       // through hole
screw_offset_from_outer_edge = 5; // mm inward from outer edge along X

// Main assembly: left (x<0), right (x>0), pin along Y at X=0
union() {
    // Left leaf: spans X = -plate_w .. 0, Y = -plate_len/2 .. +plate_len/2, Z = -half_th .. +half_th
    color("silver") left_leaf();

    // Right leaf: spans X = 0 .. +plate_w
    color("silver") right_leaf();

    // Pin (separate part), centered at origin, axis along Y
    color("gray") pin();
}


// Modules

module left_leaf() {
    // Base plate
    translate([-plate_w, -plate_len/2, -half_th])
        union() {
            cube([plate_w, plate_len, thickness]); // X: -plate_w..0

            // Left knuckles (cylinders along Y), centered at X = 0
            for (ypos = left_knuckles) {
                // place cylinder so its centerline is at X=0 (so translate by +0 in this local coords),
                // but since plate origin is at X = -plate_w, knuckle X in world = 0 => local X = plate_w
                translate([plate_w, ypos + plate_len/2, 0])  // adjust Y because cube origin is at -plate_len/2
                    // rotate so cylinder axis is along Y: default is along Z, rotate X by 90 degrees
                    rotate([90, 0, 0])
                        cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
            }

            // Subtract inner bores of left knuckles (make them tubular)
            // Use difference outside union by doing a subtraction block after union
        }

    // Subtract bores and screw holes (perform difference)
    // We'll reconstruct with difference: plate+knuckles minus bores and screw holes
    // Build plate+knuckles again and subtract
    translate([0,0,0]) difference() {
        // plate+knuckles
        translate([-plate_w, -plate_len/2, -half_th]) {
            union() {
                cube([plate_w, plate_len, thickness]);

                for (ypos = left_knuckles) {
                    translate([plate_w, ypos + plate_len/2, 0])
                        rotate([90, 0, 0])
                            cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
                }
            }
        }

        // subtract knuckle bores: long cylinders along Y centered at X=0 (world coords)
        for (ypos = left_knuckles) {
            // Bore center at X=0, Y=ypos
            translate([0, ypos, -10]) // start below plates
                rotate([90, 0, 0])
                    cylinder(h = 20 + plate_len + 40, r = knuckle_r_in, center=false);
        }

        // subtract screw through holes and countersinks (on outward face X = -plate_w)
        for (hy = screw_hole_positions) {
            hole_x = -plate_w + screw_offset_from_outer_edge;
            // through hole (extend beyond plate thickness)
            translate([hole_x, hy, -half_th - 1])
                cylinder(h = thickness + 2, r = screw_through_dia/2, center=false);
            // countersink from top surface down screw_countersink_depth
            translate([hole_x, hy, half_th - screw_countersink_depth])
                cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
        }
    }
}

module right_leaf() {
    // Base plate spanning X=0..+plate_w
    translate([0, -plate_len/2, -half_th])
        union() {
            cube([plate_w, plate_len, thickness]); // X: 0..plate_w

            // Right knuckles at world X=0 -> local X = 0
            for (ypos = right_knuckles) {
                translate([0, ypos + plate_len/2, 0])
                    rotate([90, 0, 0])
                        cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
            }
        }

    // Subtract bores and screw holes
    translate([0,0,0]) difference() {
        translate([0, -plate_len/2, -half_th]) {
            union() {
                cube([plate_w, plate_len, thickness]);

                for (ypos = right_knuckles) {
                    translate([0, ypos + plate_len/2, 0])
                        rotate([90, 0, 0])
                            cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
                }
            }
        }

        // subtract knuckle bores at world X=0
        for (ypos = right_knuckles) {
            translate([0, ypos, -10])
                rotate([90, 0, 0])
                    cylinder(h = 20 + plate_len + 40, r = knuckle_r_in, center=false);
        }

        // screw holes on outer face X = +plate_w
        for (hy = screw_hole_positions) {
            hole_x = plate_w - screw_offset_from_outer_edge;
            translate([hole_x, hy, -half_th - 1])
                cylinder(h = thickness + 2, r = screw_through_dia/2, center=false);
            translate([hole_x, hy, half_th - screw_countersink_depth])
                cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
        }
    }
}

module pin() {
    // Pin axis along Y, center at origin. Rotate cylinder (Z->Y).
    translate([0, 0, 0])
        rotate([90, 0, 0])
            cylinder(h = pin_len, r = pin_r, center=true);
}
i2 render_error render
render_error
89.9s · 3007/5356t · $0.0115
openscad stl render failed (exit 1): ERROR: Parser error: syntax error in file ../../../../../tmp/render-wURp5d/input.scad, line 49
Can't parse file '/tmp/render-wURp5d/input.scad'!

diff vs i1: +57/-75
  // openscad
  // Corrected small butt hinge (assembled, 180° open)
  // - Two leaves (30 x 25 x 2 mm), pin along Y, leaves lie flat in same plane (Z)
  // - Knuckles: 5 segments (each 6 mm long along Y) at Y = [-12, -6, 0, 6, 12]
  //   Left leaf knuckles at [-12, 0, 12], right leaf at [-6, 6]
  // - Knuckle outer Ø8 mm, inner bore Ø4.6 mm
  // - Pin Ø4 mm, length 32 mm (1 mm protruding each end)
+ // - Left and right are separate parts (not boolean-unioned) so they can rotate about pin
  // - Countersunk M3 holes (3 per leaf) at Y = [-8, 0, 8], 5 mm from outer edge
  //   Countersink Ø6 mm x depth 1 mm (from top surface) + through Ø3.2 mm
  
  $fn = 64;
  
  // Dimensions
  plate_len = 30;       // along Y
  plate_w = 25;         // along X (from hinge outward)
  thickness = 2;        // along Z
  half_th = thickness / 2;
  
  knuckle_seg = 6;      // each segment length along Y
  knuckle_positions = [-12, -6, 0, 6, 12];
  left_knuckles = [-12, 0, 12];
  right_knuckles = [-6, 6];
  
  knuckle_r_out = 4;    // outer radius (Ø8)
  knuckle_r_in = 2.3;   // inner bore radius (Ø4.6)
  
  pin_dia = 4;
  pin_r = pin_dia / 2;
  pin_len = 32;         // along Y
  
  screw_hole_positions = [-8, 0, 8];
  screw_countersink_diam = 6;    // top diameter
  screw_countersink_depth = 1;
  screw_through_dia = 3.2;       // through hole
  screw_offset_from_outer_edge = 5; // mm inward from outer edge along X
  
- // Main assembly: left (x<0), right (x>0), pin along Y at X=0
- union() {
-     // Left leaf: spans X = -plate_w .. 0, Y = -plate_len/2 .. +plate_len/2, Z = -half_th .. +half_th
-     color("silver") left_leaf();
+ // Assembly: call parts separately so they remain distinct in the scene
+ color("silver") left_leaf();
+ color("silver") right_leaf();
+ color("gray") pin();
  
-     // Right leaf: spans X = 0 .. +plate_w
-     color("silver") right_leaf();
  
-     // Pin (separate part), centered at origin, axis along Y
-     color("gray") pin();
- }
- 
- 
  // Modules
  
  module left_leaf() {
      // Base plate
-     translate([-plate_w, -plate_len/2, -half_th])
-         union() {
-             cube([plate_w, plate_len, thickness]); // X: -plate_w..0
+     plate = translate([-plate_w, -plate_len/2, -half_th])
+             cube([plate_w, plate_len, thickness]);
  
-             // Left knuckles (cylinders along Y), centered at X = 0
-             for (ypos = left_knuckles) {
-                 // place cylinder so its centerline is at X=0 (so translate by +0 in this local coords),
-                 // but since plate origin is at X = -plate_w, knuckle X in world = 0 => local X = plate_w
-                 translate([plate_w, ypos + plate_len/2, 0])  // adjust Y because cube origin is at -plate_len/2
-                     // rotate so cylinder axis is along Y: default is along Z, rotate X by 90 degrees
-                     rotate([90, 0, 0])
-                         cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
-             }
+     // Knuckle box representing the plate half-space (X <= 0)
+     knuckle_plate_box = translate([-plate_w, -plate_len/2, -100])
+                         cube([plate_w, plate_len, 200]);
  
-             // Subtract inner bores of left knuckles (make them tubular)
-             // Use difference outside union by doing a subtraction block after union
-         }
- 
-     // Subtract bores and screw holes (perform difference)
-     // We'll reconstruct with difference: plate+knuckles minus bores and screw holes
-     // Build plate+knuckles again and subtract
-     translate([0,0,0]) difference() {
-         // plate+knuckles
-         translate([-plate_w, -plate_len/2, -half_th]) {
-             union() {
-                 cube([plate_w, plate_len, thickness]);
- 
-                 for (ypos = left_knuckles) {
-                     translate([plate_w, ypos + plate_len/2, 0])
+     // Build knuckle pieces attached to left plate by intersecting full knuckle cylinders with knuckle_plate_box
+     knuckles = union();
+     for (ypos = left_knuckles) {
+         knuckle_cyl = translate([0, ypos, 0])
                          rotate([90, 0, 0])
                              cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
-                 }
-             }
+         knuckles = knuckles 
+                 + intersection() { knuckle_cyl; knuckle_plate_box; };
+     }
+ 
+     // Compose plate + knuckles, then subtract bores and screw holes
+     difference() {
+         union() {
+             plate;
+             knuckles;
          }
  
-         // subtract knuckle bores: long cylinders along Y centered at X=0 (world coords)
+         // knuckle bores (along Y), subtract from left knuckles
          for (ypos = left_knuckles) {
-             // Bore center at X=0, Y=ypos
-             translate([0, ypos, -10]) // start below plates
+             translate([0, ypos, 0])
                  rotate([90, 0, 0])
-                     cylinder(h = 20 + plate_len + 40, r = knuckle_r_in, center=false);
+                     cylinder(h = 60, r = knuckle_r_in, center=true);
          }
  
-         // subtract screw through holes and countersinks (on outward face X = -plate_w)
+         // screw through holes and countersinks (from top Z = +half_th)
          for (hy = screw_hole_positions) {
              hole_x = -plate_w + screw_offset_from_outer_edge;
-             // through hole (extend beyond plate thickness)
+             // through hole (along Z)
              translate([hole_x, hy, -half_th - 1])
                  cylinder(h = thickness + 2, r = screw_through_dia/2, center=false);
-             // countersink from top surface down screw_countersink_depth
+             // countersink frustum: small radius at bottom, large at top
              translate([hole_x, hy, half_th - screw_countersink_depth])
-                 cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
+                 cylinder(h = screw_countersink_depth, r1 = screw_through_dia/2, r2 = screw_countersink_diam/2, center=false);
          }
      }
  }
  
  module right_leaf() {
-     // Base plate spanning X=0..+plate_w
-     translate([0, -plate_len/2, -half_th])
-         union() {
-             cube([plate_w, plate_len, thickness]); // X: 0..plate_w
+     // Base plate
+     plate = translate([0, -plate_len/2, -half_th])
+             cube([plate_w, plate_len, thickness]);
  
-             // Right knuckles at world X=0 -> local X = 0
-             for (ypos = right_knuckles) {
-                 translate([0, ypos + plate_len/2, 0])
-                     rotate([90, 0, 0])
-                         cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
-             }
-         }
+     // Knuckle box representing the plate half-space (X >= 0)
+     knuckle_plate_box = translate([0, -plate_len/2, -100])
+                         cube([plate_w, plate_len, 200]);
  
-     // Subtract bores and screw holes
-     translate([0,0,0]) difference() {
-         translate([0, -plate_len/2, -half_th]) {
-             union() {
-                 cube([plate_w, plate_len, thickness]);
- 
-                 for (ypos = right_knuckles) {
-                     translate([0, ypos + plate_len/2, 0])
+     // Build knuckle pieces attached to right plate by intersecting full knuckle cylinders with knuckle_plate_box
+     knuckles = union();
+     for (ypos = right_knuckles) {
+         knuckle_cyl = translate([0, ypos, 0])
                          rotate([90, 0, 0])
                              cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
-                 }
-             }
+         knuckles = knuckles 
+                 + intersection() { knuckle_cyl; knuckle_plate_box; };
+     }
+ 
+     // Compose plate + knuckles, then subtract bores and screw holes
+     difference() {
+         union() {
+             plate;
+             knuckles;
          }
  
-         // subtract knuckle bores at world X=0
+         // knuckle bores (along Y), subtract from right knuckles
          for (ypos = right_knuckles) {
-             translate([0, ypos, -10])
+             translate([0, ypos, 0])
                  rotate([90, 0, 0])
-                     cylinder(h = 20 + plate_len + 40, r = knuckle_r_in, center=false);
+                     cylinder(h = 60, r = knuckle_r_in, center=true);
          }
  
-         // screw holes on outer face X = +plate_w
+         // screw through holes and countersinks (from top Z = +half_th)
          for (hy = screw_hole_positions) {
              hole_x = plate_w - screw_offset_from_outer_edge;
+             // through hole (along Z)
              translate([hole_x, hy, -half_th - 1])
                  cylinder(h = thickness + 2, r = screw_through_dia/2, center=false);
+             // countersink frustum: small radius at bottom, large at top
              translate([hole_x, hy, half_th - screw_countersink_depth])
-                 cylinder(h = screw_countersink_depth, r1 = screw_countersink_diam/2, r2 = screw_through_dia/2, center=false);
+                 cylinder(h = screw_countersink_depth, r1 = screw_through_dia/2, r2 = screw_countersink_diam/2, center=false);
          }
      }
  }
  
  module pin() {
-     // Pin axis along Y, center at origin. Rotate cylinder (Z->Y).
+     // Pin axis along Y, centered at origin. Pin length extends 1 mm beyond knuckle ends.
      translate([0, 0, 0])
          rotate([90, 0, 0])
              cylinder(h = pin_len, r = pin_r, center=true);
  }
SCAD (143 lines)
// openscad
// Corrected small butt hinge (assembled, 180° open)
// - Two leaves (30 x 25 x 2 mm), pin along Y, leaves lie flat in same plane (Z)
// - Knuckles: 5 segments (each 6 mm long along Y) at Y = [-12, -6, 0, 6, 12]
//   Left leaf knuckles at [-12, 0, 12], right leaf at [-6, 6]
// - Knuckle outer Ø8 mm, inner bore Ø4.6 mm
// - Pin Ø4 mm, length 32 mm (1 mm protruding each end)
// - Left and right are separate parts (not boolean-unioned) so they can rotate about pin
// - Countersunk M3 holes (3 per leaf) at Y = [-8, 0, 8], 5 mm from outer edge
//   Countersink Ø6 mm x depth 1 mm (from top surface) + through Ø3.2 mm

$fn = 64;

// Dimensions
plate_len = 30;       // along Y
plate_w = 25;         // along X (from hinge outward)
thickness = 2;        // along Z
half_th = thickness / 2;

knuckle_seg = 6;      // each segment length along Y
knuckle_positions = [-12, -6, 0, 6, 12];
left_knuckles = [-12, 0, 12];
right_knuckles = [-6, 6];

knuckle_r_out = 4;    // outer radius (Ø8)
knuckle_r_in = 2.3;   // inner bore radius (Ø4.6)

pin_dia = 4;
pin_r = pin_dia / 2;
pin_len = 32;         // along Y

screw_hole_positions = [-8, 0, 8];
screw_countersink_diam = 6;    // top diameter
screw_countersink_depth = 1;
screw_through_dia = 3.2;       // through hole
screw_offset_from_outer_edge = 5; // mm inward from outer edge along X

// Assembly: call parts separately so they remain distinct in the scene
color("silver") left_leaf();
color("silver") right_leaf();
color("gray") pin();


// Modules

module left_leaf() {
    // Base plate
    plate = translate([-plate_w, -plate_len/2, -half_th])
            cube([plate_w, plate_len, thickness]);

    // Knuckle box representing the plate half-space (X <= 0)
    knuckle_plate_box = translate([-plate_w, -plate_len/2, -100])
                        cube([plate_w, plate_len, 200]);

    // Build knuckle pieces attached to left plate by intersecting full knuckle cylinders with knuckle_plate_box
    knuckles = union();
    for (ypos = left_knuckles) {
        knuckle_cyl = translate([0, ypos, 0])
                        rotate([90, 0, 0])
                            cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
        knuckles = knuckles 
                + intersection() { knuckle_cyl; knuckle_plate_box; };
    }

    // Compose plate + knuckles, then subtract bores and screw holes
    difference() {
        union() {
            plate;
            knuckles;
        }

        // knuckle bores (along Y), subtract from left knuckles
        for (ypos = left_knuckles) {
            translate([0, ypos, 0])
                rotate([90, 0, 0])
                    cylinder(h = 60, r = knuckle_r_in, center=true);
        }

        // screw through holes and countersinks (from top Z = +half_th)
        for (hy = screw_hole_positions) {
            hole_x = -plate_w + screw_offset_from_outer_edge;
            // through hole (along Z)
            translate([hole_x, hy, -half_th - 1])
                cylinder(h = thickness + 2, r = screw_through_dia/2, center=false);
            // countersink frustum: small radius at bottom, large at top
            translate([hole_x, hy, half_th - screw_countersink_depth])
                cylinder(h = screw_countersink_depth, r1 = screw_through_dia/2, r2 = screw_countersink_diam/2, center=false);
        }
    }
}

module right_leaf() {
    // Base plate
    plate = translate([0, -plate_len/2, -half_th])
            cube([plate_w, plate_len, thickness]);

    // Knuckle box representing the plate half-space (X >= 0)
    knuckle_plate_box = translate([0, -plate_len/2, -100])
                        cube([plate_w, plate_len, 200]);

    // Build knuckle pieces attached to right plate by intersecting full knuckle cylinders with knuckle_plate_box
    knuckles = union();
    for (ypos = right_knuckles) {
        knuckle_cyl = translate([0, ypos, 0])
                        rotate([90, 0, 0])
                            cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
        knuckles = knuckles 
                + intersection() { knuckle_cyl; knuckle_plate_box; };
    }

    // Compose plate + knuckles, then subtract bores and screw holes
    difference() {
        union() {
            plate;
            knuckles;
        }

        // knuckle bores (along Y), subtract from right knuckles
        for (ypos = right_knuckles) {
            translate([0, ypos, 0])
                rotate([90, 0, 0])
                    cylinder(h = 60, r = knuckle_r_in, center=true);
        }

        // screw through holes and countersinks (from top Z = +half_th)
        for (hy = screw_hole_positions) {
            hole_x = plate_w - screw_offset_from_outer_edge;
            // through hole (along Z)
            translate([hole_x, hy, -half_th - 1])
                cylinder(h = thickness + 2, r = screw_through_dia/2, center=false);
            // countersink frustum: small radius at bottom, large at top
            translate([hole_x, hy, half_th - screw_countersink_depth])
                cylinder(h = screw_countersink_depth, r1 = screw_through_dia/2, r2 = screw_countersink_diam/2, center=false);
        }
    }
}

module pin() {
    // Pin axis along Y, centered at origin. Pin length extends 1 mm beyond knuckle ends.
    translate([0, 0, 0])
        rotate([90, 0, 0])
            cylinder(h = pin_len, r = pin_r, center=true);
}
i3 success
iter 3
58.0s · 2183/3135t · $0.0068
diff vs i2: +59/-70
  // openscad
- // Corrected small butt hinge (assembled, 180° open)
- // - Two leaves (30 x 25 x 2 mm), pin along Y, leaves lie flat in same plane (Z)
- // - Knuckles: 5 segments (each 6 mm long along Y) at Y = [-12, -6, 0, 6, 12]
- //   Left leaf knuckles at [-12, 0, 12], right leaf at [-6, 6]
- // - Knuckle outer Ø8 mm, inner bore Ø4.6 mm
- // - Pin Ø4 mm, length 32 mm (1 mm protruding each end)
- // - Left and right are separate parts (not boolean-unioned) so they can rotate about pin
- // - Countersunk M3 holes (3 per leaf) at Y = [-8, 0, 8], 5 mm from outer edge
- //   Countersink Ø6 mm x depth 1 mm (from top surface) + through Ø3.2 mm
+ // Fixed small butt hinge (assembled, 180° open)
+ // Two leaves (30 x 25 x 2 mm), pin along Y, leaves lie flat in same plane (Z)
+ // Knuckles: 5 segments (each 6 mm long along Y) at Y = [-12, -6, 0, 6, 12]
+ // Left knuckles at [-12, 0, 12], right knuckles at [-6, 6]
+ // Knuckle outer Ø8 mm, inner bore Ø4.6 mm
+ // Pin Ø4 mm, length 32 mm (1 mm protruding each end)
+ // Countersunk M3 holes (3 per leaf) at Y = [-8, 0, 8], 5 mm from outer edge
+ // Countersink Ø6 mm x depth 1 mm + through Ø3.2 mm
+ // Coordinate system: pin centerline on Y axis, leaves extend in X (left x<0, right x>0)
  
  $fn = 64;
  
  // Dimensions
  plate_len = 30;       // along Y
  plate_w = 25;         // along X (from hinge outward)
  thickness = 2;        // along Z
  half_th = thickness / 2;
  
  knuckle_seg = 6;      // each segment length along Y
  knuckle_positions = [-12, -6, 0, 6, 12];
  left_knuckles = [-12, 0, 12];
  right_knuckles = [-6, 6];
  
  knuckle_r_out = 4;    // outer radius (Ø8)
  knuckle_r_in = 2.3;   // inner bore radius (Ø4.6)
  
  pin_dia = 4;
  pin_r = pin_dia / 2;
  pin_len = 32;         // along Y
  
  screw_hole_positions = [-8, 0, 8];
  screw_countersink_diam = 6;    // top diameter
  screw_countersink_depth = 1;
  screw_through_dia = 3.2;       // through hole
  screw_offset_from_outer_edge = 5; // mm inward from outer edge along X
  
- // Assembly: call parts separately so they remain distinct in the scene
+ // Parts (placed assembled, 180° open)
  color("silver") left_leaf();
  color("silver") right_leaf();
  color("gray") pin();
  
- 
- // Modules
- 
  module left_leaf() {
-     // Base plate
-     plate = translate([-plate_w, -plate_len/2, -half_th])
-             cube([plate_w, plate_len, thickness]);
- 
-     // Knuckle box representing the plate half-space (X <= 0)
-     knuckle_plate_box = translate([-plate_w, -plate_len/2, -100])
-                         cube([plate_w, plate_len, 200]);
- 
-     // Build knuckle pieces attached to left plate by intersecting full knuckle cylinders with knuckle_plate_box
-     knuckles = union();
-     for (ypos = left_knuckles) {
-         knuckle_cyl = translate([0, ypos, 0])
-                         rotate([90, 0, 0])
-                             cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
-         knuckles = knuckles 
-                 + intersection() { knuckle_cyl; knuckle_plate_box; };
-     }
- 
-     // Compose plate + knuckles, then subtract bores and screw holes
      difference() {
+         // Plate body + left knuckles (half of knuckle cylinders)
          union() {
-             plate;
-             knuckles;
+             // plate (X from -plate_w .. 0)
+             translate([-plate_w, -plate_len/2, -half_th])
+                 cube([plate_w, plate_len, thickness]);
+ 
+             // knuckles intersected with left plate half-space
+             for (ypos = left_knuckles) {
+                 intersection() {
+                     // full knuckle cylinder (axis along Y)
+                     translate([0, ypos, 0])
+                         rotate([90, 0, 0])
+                             cylinder(h = knuckle_seg, r = knuckle_r_out, center = true);
+                     // left plate half-space box to cut the half knuckle
+                     translate([-plate_w, -plate_len/2, -100])
+                         cube([plate_w, plate_len, 200]);
+                 }
+             }
          }
  
-         // knuckle bores (along Y), subtract from left knuckles
+         // subtract knuckle bores (through the knuckles)
          for (ypos = left_knuckles) {
              translate([0, ypos, 0])
                  rotate([90, 0, 0])
-                     cylinder(h = 60, r = knuckle_r_in, center=true);
+                     cylinder(h = 100, r = knuckle_r_in, center = true);
          }
  
-         // screw through holes and countersinks (from top Z = +half_th)
+         // subtract screw holes + countersinks (from top surface Z = +half_th)
          for (hy = screw_hole_positions) {
              hole_x = -plate_w + screw_offset_from_outer_edge;
-             // through hole (along Z)
+             // through hole
              translate([hole_x, hy, -half_th - 1])
-                 cylinder(h = thickness + 2, r = screw_through_dia/2, center=false);
-             // countersink frustum: small radius at bottom, large at top
+                 cylinder(h = thickness + 2, r = screw_through_dia / 2, center = false);
+             // countersink (frustum) from top
              translate([hole_x, hy, half_th - screw_countersink_depth])
-                 cylinder(h = screw_countersink_depth, r1 = screw_through_dia/2, r2 = screw_countersink_diam/2, center=false);
+                 cylinder(h = screw_countersink_depth, r1 = screw_through_dia / 2, r2 = screw_countersink_diam / 2, center = false);
          }
      }
  }
  
  module right_leaf() {
-     // Base plate
-     plate = translate([0, -plate_len/2, -half_th])
-             cube([plate_w, plate_len, thickness]);
- 
-     // Knuckle box representing the plate half-space (X >= 0)
-     knuckle_plate_box = translate([0, -plate_len/2, -100])
-                         cube([plate_w, plate_len, 200]);
- 
-     // Build knuckle pieces attached to right plate by intersecting full knuckle cylinders with knuckle_plate_box
-     knuckles = union();
-     for (ypos = right_knuckles) {
-         knuckle_cyl = translate([0, ypos, 0])
-                         rotate([90, 0, 0])
-                             cylinder(h = knuckle_seg, r = knuckle_r_out, center=true);
-         knuckles = knuckles 
-                 + intersection() { knuckle_cyl; knuckle_plate_box; };
-     }
- 
-     // Compose plate + knuckles, then subtract bores and screw holes
      difference() {
+         // Plate body + right knuckles (half of knuckle cylinders)
          union() {
-             plate;
-             knuckles;
+             // plate (X from 0 .. plate_w)
+             translate([0, -plate_len/2, -half_th])
+                 cube([plate_w, plate_len, thickness]);
+ 
+             // knuckles intersected with right plate half-space
+             for (ypos = right_knuckles) {
+                 intersection() {
+                     // full knuckle cylinder (axis along Y)
+                     translate([0, ypos, 0])
+                         rotate([90, 0, 0])
+                             cylinder(h = knuckle_seg, r = knuckle_r_out, center = true);
+                     // right plate half-space box to cut the half knuckle
+                     translate([0, -plate_len/2, -100])
+                         cube([plate_w, plate_len, 200]);
+                 }
+             }
          }
  
-         // knuckle bores (along Y), subtract from right knuckles
+         // subtract knuckle bores (through the knuckles)
          for (ypos = right_knuckles) {
              translate([0, ypos, 0])
                  rotate([90, 0, 0])
-                     cylinder(h = 60, r = knuckle_r_in, center=true);
+                     cylinder(h = 100, r = knuckle_r_in, center = true);
          }
  
-         // screw through holes and countersinks (from top Z = +half_th)
+         // subtract screw holes + countersinks (from top surface Z = +half_th)
          for (hy = screw_hole_positions) {
              hole_x = plate_w - screw_offset_from_outer_edge;
-             // through hole (along Z)
+             // through hole
              translate([hole_x, hy, -half_th - 1])
-                 cylinder(h = thickness + 2, r = screw_through_dia/2, center=false);
-             // countersink frustum: small radius at bottom, large at top
+                 cylinder(h = thickness + 2, r = screw_through_dia / 2, center = false);
+             // countersink (frustum) from top
              translate([hole_x, hy, half_th - screw_countersink_depth])
-                 cylinder(h = screw_countersink_depth, r1 = screw_through_dia/2, r2 = screw_countersink_diam/2, center=false);
+                 cylinder(h = screw_countersink_depth, r1 = screw_through_dia / 2, r2 = screw_countersink_diam / 2, center = false);
          }
      }
  }
  
  module pin() {
      // Pin axis along Y, centered at origin. Pin length extends 1 mm beyond knuckle ends.
      translate([0, 0, 0])
          rotate([90, 0, 0])
-             cylinder(h = pin_len, r = pin_r, center=true);
+             cylinder(h = pin_len, r = pin_r, center = true);
  }
SCAD (132 lines)
// openscad
// Fixed small butt hinge (assembled, 180° open)
// Two leaves (30 x 25 x 2 mm), pin along Y, leaves lie flat in same plane (Z)
// Knuckles: 5 segments (each 6 mm long along Y) at Y = [-12, -6, 0, 6, 12]
// Left knuckles at [-12, 0, 12], right knuckles at [-6, 6]
// Knuckle outer Ø8 mm, inner bore Ø4.6 mm
// Pin Ø4 mm, length 32 mm (1 mm protruding each end)
// Countersunk M3 holes (3 per leaf) at Y = [-8, 0, 8], 5 mm from outer edge
// Countersink Ø6 mm x depth 1 mm + through Ø3.2 mm
// Coordinate system: pin centerline on Y axis, leaves extend in X (left x<0, right x>0)

$fn = 64;

// Dimensions
plate_len = 30;       // along Y
plate_w = 25;         // along X (from hinge outward)
thickness = 2;        // along Z
half_th = thickness / 2;

knuckle_seg = 6;      // each segment length along Y
knuckle_positions = [-12, -6, 0, 6, 12];
left_knuckles = [-12, 0, 12];
right_knuckles = [-6, 6];

knuckle_r_out = 4;    // outer radius (Ø8)
knuckle_r_in = 2.3;   // inner bore radius (Ø4.6)

pin_dia = 4;
pin_r = pin_dia / 2;
pin_len = 32;         // along Y

screw_hole_positions = [-8, 0, 8];
screw_countersink_diam = 6;    // top diameter
screw_countersink_depth = 1;
screw_through_dia = 3.2;       // through hole
screw_offset_from_outer_edge = 5; // mm inward from outer edge along X

// Parts (placed assembled, 180° open)
color("silver") left_leaf();
color("silver") right_leaf();
color("gray") pin();

module left_leaf() {
    difference() {
        // Plate body + left knuckles (half of knuckle cylinders)
        union() {
            // plate (X from -plate_w .. 0)
            translate([-plate_w, -plate_len/2, -half_th])
                cube([plate_w, plate_len, thickness]);

            // knuckles intersected with left plate half-space
            for (ypos = left_knuckles) {
                intersection() {
                    // full knuckle cylinder (axis along Y)
                    translate([0, ypos, 0])
                        rotate([90, 0, 0])
                            cylinder(h = knuckle_seg, r = knuckle_r_out, center = true);
                    // left plate half-space box to cut the half knuckle
                    translate([-plate_w, -plate_len/2, -100])
                        cube([plate_w, plate_len, 200]);
                }
            }
        }

        // subtract knuckle bores (through the knuckles)
        for (ypos = left_knuckles) {
            translate([0, ypos, 0])
                rotate([90, 0, 0])
                    cylinder(h = 100, r = knuckle_r_in, center = true);
        }

        // subtract screw holes + countersinks (from top surface Z = +half_th)
        for (hy = screw_hole_positions) {
            hole_x = -plate_w + screw_offset_from_outer_edge;
            // through hole
            translate([hole_x, hy, -half_th - 1])
                cylinder(h = thickness + 2, r = screw_through_dia / 2, center = false);
            // countersink (frustum) from top
            translate([hole_x, hy, half_th - screw_countersink_depth])
                cylinder(h = screw_countersink_depth, r1 = screw_through_dia / 2, r2 = screw_countersink_diam / 2, center = false);
        }
    }
}

module right_leaf() {
    difference() {
        // Plate body + right knuckles (half of knuckle cylinders)
        union() {
            // plate (X from 0 .. plate_w)
            translate([0, -plate_len/2, -half_th])
                cube([plate_w, plate_len, thickness]);

            // knuckles intersected with right plate half-space
            for (ypos = right_knuckles) {
                intersection() {
                    // full knuckle cylinder (axis along Y)
                    translate([0, ypos, 0])
                        rotate([90, 0, 0])
                            cylinder(h = knuckle_seg, r = knuckle_r_out, center = true);
                    // right plate half-space box to cut the half knuckle
                    translate([0, -plate_len/2, -100])
                        cube([plate_w, plate_len, 200]);
                }
            }
        }

        // subtract knuckle bores (through the knuckles)
        for (ypos = right_knuckles) {
            translate([0, ypos, 0])
                rotate([90, 0, 0])
                    cylinder(h = 100, r = knuckle_r_in, center = true);
        }

        // subtract screw holes + countersinks (from top surface Z = +half_th)
        for (hy = screw_hole_positions) {
            hole_x = plate_w - screw_offset_from_outer_edge;
            // through hole
            translate([hole_x, hy, -half_th - 1])
                cylinder(h = thickness + 2, r = screw_through_dia / 2, center = false);
            // countersink (frustum) from top
            translate([hole_x, hy, half_th - screw_countersink_depth])
                cylinder(h = screw_countersink_depth, r1 = screw_through_dia / 2, r2 = screw_countersink_diam / 2, center = false);
        }
    }
}

module pin() {
    // Pin axis along Y, centered at origin. Pin length extends 1 mm beyond knuckle ends.
    translate([0, 0, 0])
        rotate([90, 0, 0])
            cylinder(h = pin_len, r = pin_r, center = true);
}