Skip to content

Instantly share code, notes, and snippets.

@brentvatne
Created January 30, 2026 05:58
Show Gist options
  • Select an option

  • Save brentvatne/d27007e6b4bd4af59f3352b0de56a79c to your computer and use it in GitHub Desktop.

Select an option

Save brentvatne/d27007e6b4bd4af59f3352b0de56a79c to your computer and use it in GitHub Desktop.
// ============================================
// DIGITAKT 2 BATTCAVE - Wedge Stand Design
// ============================================
/* [Device Dimensions] */
device_width = 215;
device_depth = 176;
device_height = 63;
/* [Stand Design] */
tilt_angle = 12;
wall = 5; // Outer wall thickness
base_floor = 8; // Solid bottom thickness
ledge_width = 10; // How far the ledge extends under the device
ledge_thickness = 5; // Ledge thickness
lip_height = 10; // Lip height above ledge (front and sides)
front_height = 55; // Height at front of stand
/* [Power Bank - Anker 165W] */
pb_length = 157;
pb_width = 54;
pb_height = 49;
/* [W+ Link Transmitter] */
wl_size = 65;
wl_height = 28;
/* [Aesthetics] */
corner_r = 8;
/* [Hidden] */
$fn = $preview ? 32 : 64;
epsilon = 0.01;
// ============================================
// CALCULATED
// ============================================
clearance = 0.5;
// Device pocket (where device body sits)
pocket_w = device_width + clearance * 2;
pocket_d = device_depth + clearance * 2;
// Stand outer dimensions (wall + ledge on each side)
stand_width = pocket_w + wall * 2;
stand_depth = pocket_d + wall * 2;
// Wedge heights
tilt_rise = stand_depth * tan(tilt_angle);
back_height = front_height + tilt_rise;
// Where the tilted ledge surface sits (Z at front edge)
ledge_z = front_height - ledge_thickness - lip_height;
// Opening through the shelf (for battery access when device removed)
// All 4 sides have shelf for support
shelf_opening_w = pocket_w - ledge_width * 2;
shelf_opening_d = pocket_d - ledge_width * 2;
echo(str("=== Digitakt 2 BattCave ==="));
echo(str("Stand: ", stand_width, " x ", stand_depth, " mm"));
echo(str("Pocket: ", pocket_w, " x ", pocket_d, " mm"));
echo(str("Ledge opening: ", shelf_opening_w, " x ", shelf_opening_d, " mm"));
// Debug: Key Z heights
echo(str("--- Geometry Debug ---"));
echo(str("ledge_z (shelf bottom at front): ", ledge_z));
echo(str("ledge_z + ledge_thickness (shelf top at front): ", ledge_z + ledge_thickness));
echo(str("Shelf top at back: ", ledge_z + ledge_thickness + pocket_d * tan(tilt_angle)));
echo(str("lip_height: ", lip_height));
echo(str("Expected lip top at back: ", ledge_z + ledge_thickness + pocket_d * tan(tilt_angle) + lip_height));
echo(str("back_height (wedge top at back): ", back_height));
echo(str("Wrap width: 10mm, Wrap should be ", lip_height, "mm tall"));
// ============================================
// MODULES
// ============================================
module rounded_rect_2d(w, d, r) {
offset(r) offset(-r) square([w, d]);
}
// Wedge with rounded corners
module wedge_shape(w, d, h_front, h_back, r) {
intersection() {
linear_extrude(h_back + 10)
rounded_rect_2d(w, d, r);
hull() {
cube([w, epsilon, h_front]);
translate([0, d - epsilon, 0])
cube([w, epsilon, h_back]);
}
}
}
module battcave() {
difference() {
// === OUTER WEDGE SHELL ===
wedge_shape(stand_width, stand_depth, front_height, back_height, corner_r);
// === HOLLOW OUT BATTERY COMPARTMENT ===
// Only up to BELOW the ledge
translate([wall, wall, base_floor])
cube([stand_width - wall*2, stand_depth - wall*2, ledge_z - base_floor]);
// === CUT THE DEVICE POCKET (above the ledge) ===
// This is where the device body sits - full pocket size
translate([wall, wall, ledge_z + ledge_thickness])
rotate([tilt_angle, 0, 0])
cube([pocket_w, pocket_d + 50, back_height]);
// === CUT OPENING THROUGH THE LEDGE ===
// Smaller than device - device edges REST on the remaining ledge
translate([wall + ledge_width, wall + ledge_width, ledge_z - 10])
rotate([tilt_angle, 0, 0])
cube([shelf_opening_w, shelf_opening_d, ledge_thickness + 20]);
// === TRIM TILTED GEOMETRY ===
translate([-10, -50, 0])
cube([stand_width + 20, 50, back_height + 50]);
translate([-10, stand_depth, 0])
cube([stand_width + 20, 50, back_height + 50]);
translate([-50, -10, 0])
cube([50, stand_depth + 20, back_height + 50]);
translate([stand_width, -10, 0])
cube([50, stand_depth + 20, back_height + 50]);
// === REAR CABLE HOLE (rounded rectangle) ===
cable_hole_w = 50;
cable_hole_h = 25;
cable_hole_r = 8; // Corner radius
translate([stand_width/2 - cable_hole_w/2, stand_depth + epsilon, base_floor + 15])
rotate([90, 0, 0])
linear_extrude(wall + epsilon*2)
offset(cable_hole_r) offset(-cable_hole_r)
square([cable_hole_w, cable_hole_h]);
// === REMOVE BACK LIP (except 10mm wrap at right corner) ===
// Cut must start at shelf surface level at the back, NOT at front shelf level
// The shelf tilts upward from front to back, so we calculate Z at the cut position
back_lip_wrap = 10;
cut_w = pocket_w - back_lip_wrap;
cut_d = wall + ledge_width + 50;
cut_h = back_height * 2;
// Calculate the Y position where cut starts (inner back edge of shelf)
back_lip_cut_y = stand_depth - wall - ledge_width;
// Calculate shelf top Z at that Y position (accounting for tilt from front inner edge)
back_lip_cut_z = ledge_z + ledge_thickness + (back_lip_cut_y - wall) * tan(tilt_angle);
translate([wall, back_lip_cut_y, back_lip_cut_z])
rotate([tilt_angle, 0, 0]) // Follow the tilt to match lip geometry
hull() {
// Main cut body (shortened to leave room for rounded corner)
cube([cut_w - corner_r, cut_d, cut_h]);
// Rounded corner (cylinder creates the 8mm radius)
translate([cut_w - corner_r, corner_r, 0])
cylinder(r=corner_r, h=cut_h, $fn=32);
}
}
}
// ============================================
// RENDER
// ============================================
color("DarkSlateGray") battcave();
// Ghost Digitakt 2 - sitting ON the tilted ledge
%translate([wall + clearance, wall + clearance, ledge_z + ledge_thickness + 0.5])
rotate([tilt_angle, 0, 0])
cube([device_width, device_depth, device_height]);
// Ghost power bank (in battery compartment)
%translate([wall + 10, stand_depth/2 - pb_width/2, base_floor + 1])
color("DimGray", 0.5)
cube([pb_length, pb_width, pb_height]);
// Ghost W+ Link
%translate([stand_width - wall - wl_size - 10, wall + 10, base_floor + 1])
color("SteelBlue", 0.5)
cube([wl_size, wl_size, wl_height]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment