Models
honeycomb-wall.scad
Source: honeycomb-wall.scad
// honeycomb-wall.scad: a parametric honeycomb storage wall.
//
// Fills a print bed with honeycomb cells, or builds a custom layout column
// by column, with optional gaps, solid cells and flat edges. Cell sizes come
// from hsw.scad, shared with the inserts and clips.
include <hsw.scad>
/* [Grid size] */
// Largest wall that fits the print bed (mm). The defaults suit a Bambu Lab
// A1 (about 10 x 10 cells); a Prusa i3 MK3 takes 240 x 208.
max_grid_width = 211;
max_grid_height = 248;
/* [Layout] */
// 0 fills the grid size; the others are column-by-column layouts
column_data_index = 0; // [0:Fill the grid size, 1:Example layout, 2:IKEA 365+ drybox filament end, 3:IKEA 365+ drybox other end]
// Cells in each column, left to right, for each layout
column_data = [
[],
[3, 5, 8, 6, 4, 3, 4, 5, 6],
[7, 6, 5, 7, 7, 6, 5, 7],
[4, 5, 4, 5, 4, 5, 4, 5],
];
// Start the half-cell stagger on the first column instead of the second
flip_staggering = true;
// Move columns up or down, in cells (custom layouts can then exceed the grid size)
include_offsets = false;
column_offsets = [0, -2, -3, -1, 0, 2, 4];
// Leave cells out: for each column, the rows to skip, counting from 1
include_gaps = false;
column_gaps = [[], [3, 4], [4, 5], [2]];
/* [Solid cells] */
// Close the front of chosen cells with a solid face. Tip: add solid_height to
// hsw_depth to keep the same insert depth behind it.
include_solids = false;
solid_height = 1;
// For each column, the rows to close, counting from 1
column_solids = [[], [], [for (i = [3:7]) i], [for (i = [2:7]) i]];
/* [Flat edges] */
edge_left = false;
edge_top = false;
edge_right = false;
edge_bottom = false;
$fn = $preview ? 32 : 64;
// ---------------- Layout ----------------
fill = column_data_index == 0;
columns = column_data[column_data_index];
// A flat edge cuts a cell in half, so it makes room for half a cell more.
half_x = (edge_left ? 0.5 : 0) + (edge_right ? 0.5 : 0);
half_y = (edge_top ? 0.5 : 0) + (edge_bottom ? 0.5 : 0);
column_count = fill ? floor(max_grid_width / hsw_pitch_x() + half_x) : len(columns);
// Rows in the unshifted columns, and in the columns shifted half a cell down
rows_even = fill ? floor(max_grid_height / hsw_pitch_y() + half_y) : max(columns);
rows_odd = fill ? floor(max_grid_height / hsw_pitch_y() - 0.5 + half_y) : max(columns) - 0.5;
// How far down the grid reaches, for trimming the bottom edge
grid_height = hsw_pitch_y() * max(rows_even - 0.5, rows_odd);
function shifted(col) = (col + (flip_staggering ? 1 : 0)) % 2;
function rows_in(col) = shifted(col) == 0 ? rows_even : rows_odd;
function listed(list, col, row) = col < len(list) && len(search(row + 1, list[col])) > 0;
// ---------------- Build ----------------
translate([edge_left ? 0 : hsw_cell_side(), edge_top ? 0 : -hsw_pitch_y() / 2, 0])
for (col = [0:column_count - 1]) {
n = fill ? rows_even : columns[col];
lift = include_offsets && col < len(column_offsets) ? column_offsets[col] : 0;
for (row = [0:n - 1])
if (row < rows_in(col) && !(include_gaps && listed(column_gaps, col, row))) {
y = -hsw_pitch_y() * (row + lift + shifted(col) / 2);
last = row + 1 == rows_in(col);
translate([hsw_pitch_x() * col, y, 0])
wall_cell(
left = edge_left && col == 0,
top = edge_top && row == 0 && shifted(col) == 0,
right = edge_right && col == column_count - 1,
// only a last cell that runs past the bottom, centre still inside
bottom = edge_bottom && last && hsw_pitch_y() - y > grid_height + 0.001 && hsw_pitch_y() / 2 - y <= grid_height,
solid = include_solids && listed(column_solids, col, row)
);
}
}
// A cell, cut in half and closed with a flat wall on each side that asks,
// and closed at the front if solid.
module wall_cell(left, top, right, bottom, solid) {
h = hsw_pitch_y();
d = 2 * hsw_cell_side();
difference() {
union() {
hsw_cell();
if (solid)
translate([0, 0, hsw_depth - solid_height])
linear_extrude(solid_height) hsw_hexagon(hsw_cell_flats());
if (left) translate([0, -h / 2, 0]) hsw_edge_wall(h);
if (right) translate([0, h / 2, 0]) rotate([0, 0, 180]) hsw_edge_wall(h);
if (top) translate([-d / 2, 0, 0]) rotate([0, 0, -90]) hsw_edge_wall(d);
if (bottom) translate([d / 2, 0, 0]) rotate([0, 0, 90]) hsw_edge_wall(d);
}
if (left) translate([-d / 2, -h, -1]) cube([d / 2, 2 * h, hsw_depth + 2]);
if (right) translate([0, -h, -1]) cube([d / 2, 2 * h, hsw_depth + 2]);
if (top) translate([-d / 2, 0, -1]) cube([d, h, hsw_depth + 2]);
if (bottom) translate([-d / 2, -h, -1]) cube([d, h, hsw_depth + 2]);
}
}
attachments/hook.scad
Source: attachments/hook.scad
// hook.scad: a screw-on wall hook.
//
// A strip bent into a J: a back plate against the wall, a bend into a
// horizontal arm, and a bend up into the lip. The two free ends are rounded
// across the strip's width. Edges are filleted or chamfered.
/* [Size] */
// Back plate length, from the bend to the start of its rounded top (mm)
back_length = 90;
// Strip width (mm)
width = 60;
// Strip thickness (mm)
thickness = 15;
// Arm length between the two bends (mm)
arm_length = 40;
// Inside radius of both bends (mm)
bend_radius = 10;
/* [Edges] */
edge_style = "fillet"; // [fillet, chamfer]
// Fillet radius (mm)
fillet = 1;
// Chamfer size (mm)
chamfer = 3;
/* [Screws] */
// Screw hole diameter (mm)
screw_diameter = 6; // [5, 6, 8, 10]
// Countersink the holes on the hook side
countersink = false;
$fn = $preview ? 32 : 64;
// The strip's cross-section: x across the thickness, y across the width.
module section() {
if (edge_style == "chamfer") {
c = min(chamfer, thickness / 2 - 0.01, width / 2 - 0.01);
polygon([[c, 0], [thickness - c, 0], [thickness, c], [thickness, width - c],
[thickness - c, width], [c, width], [0, width - c], [0, c]]);
} else {
f = min(fillet, thickness / 2 - 0.01, width / 2 - 0.01);
translate([f, f]) offset(r = f) square([thickness - 2 * f, width - 2 * f]);
}
}
// A straight run of strip along +z, from z = 0 to len.
module run(len) {
linear_extrude(len) section();
}
// A rounded end across the strip's width, standing on z = 0.
module rounded_end() {
r = width / 2;
if (edge_style == "chamfer") {
c = min(chamfer, thickness / 2 - 0.01);
intersection() {
translate([0, r, 0]) rotate([0, 90, 0]) hull() {
cylinder(r = r - c, h = thickness);
translate([0, 0, c]) cylinder(r = r, h = thickness - 2 * c);
}
translate([0, 0, 0]) cube([thickness, width, r]);
}
} else {
f = min(fillet, thickness / 2 - 0.01);
intersection() {
translate([f, r, 0]) rotate([0, 90, 0]) minkowski() {
cylinder(r = r - f, h = thickness - 2 * f);
sphere(f, $fn = 16);
}
cube([thickness, width, r]);
}
}
}
// A quarter bend of strip: the section swept 90 degrees about an axis along
// y, inside radius bend_radius.
module bend() {
rotate([90, 0, 0]) rotate_extrude(angle = 90)
translate([bend_radius, 0]) rotate(0) mirror([0, 1]) section();
}
module screw_holes() {
for (z = [back_length / 3, back_length])
translate([0, width / 2, z]) rotate([0, 90, 0]) {
translate([0, 0, -1]) cylinder(d = screw_diameter, h = thickness + 2);
if (countersink)
translate([0, 0, -0.01]) cylinder(d1 = screw_diameter * 1.7, d2 = screw_diameter, h = thickness / 4);
}
}
// The back plate stands at x = 0..thickness, wall side at x = thickness; the
// hook opens toward -x.
module hook() {
r = bend_radius;
difference() {
union() {
run(back_length);
translate([0, 0, back_length]) rounded_end();
// bend at the bottom of the back plate, into the arm
translate([-r, 0, 0]) rotate([0, 0, 0]) mirror([0, 0, 1]) bend();
// the arm, running toward -x
translate([-r, 0, -r]) rotate([0, -90, 0]) translate([-thickness, 0, 0]) run(arm_length);
// bend up into the lip
translate([-r - arm_length, 0, 0]) rotate([0, 0, 180]) translate([0, -width, 0]) mirror([0, 0, 1]) bend();
// the lip's rounded top
translate([-2 * r - arm_length - thickness, 0, 0]) rounded_end();
}
screw_holes();
}
echo(str("Hook: ", back_length + width / 2 + r + thickness, " mm tall, ", arm_length + 2 * (r + thickness), " mm deep"));
}
hook();
attachments/hook.scad on GitHub
attachments/plier_holder.scad
Source: attachments/plier_holder.scad
use <rounded_triangle.scad>
$fn = $preview ? 32 : 64; // Smoothness of circles
z_fight = $preview ? 0.05 : 0; // Small offset to prevent z-fighting in preview mode
// =============================================================
// Parameters
// =============================================================
triangle_height = 25;
triangle_base = 14;
tip_diameter = 3;
length = 50;
screw_diameter = 3.5;
// =============================================================
// Main build
// =============================================================
difference() {
// Extrude a rounded triangle
translate([-triangle_base / 2, 0, 0])
linear_extrude(height=length)
rounded_triangle(triangle_base, triangle_height, tip_diameter);
// Chamfer the bottom edges
for (i = [0:1])
mirror([i, 0, 0])
translate([-triangle_base / 2, 0, length / 2])
rotate([0, 0, 25])
cube([triangle_base / 4, triangle_base / 2, length + z_fight], center=true);
// Mass cut off at the bottom to reduce material use
translate([0, 0, length / 2])
scale([1, 2.5, 1])
cylinder(h=length + z_fight, d=tip_diameter * 2, center=true, $fn=32);
// Mass cut off at the bottom to reduce material use
translate([0, triangle_height * 0.8, length / 2])
scale([0.5, 2, 1])
cylinder(h=length + z_fight, d=tip_diameter, center=true, $fn=32);
// Head bore
translate([0, triangle_height / 2, length / 2 + screw_diameter * 3])
cylinder(h=length + z_fight, d=screw_diameter * 1.67, center=true, $fn=32);
// Screw hole
translate([0, triangle_height / 2, length / 2])
cylinder(h=length + z_fight, d=screw_diameter, center=true, $fn=32);
}
attachments/plier_holder.scad on GitHub
attachments/tool_holder.scad
Source: attachments/tool_holder.scad
// tool_holder.scad: a screw-on rack of tool holders.
//
// A flange strip with a screw hole at each end carries a row of holders:
// angled hex pegs with a bore down the middle (for bits, spokes or anything
// slim), or rings that hold a screwdriver by its shank.
/* [Rack] */
// What the rack holds
holder = "peg"; // [peg, ring]
// Number of holders
count = 8;
// Distance from the first holder to the last (mm)
span = 142;
/* [Flange] */
flange_height = 10;
flange_thickness = 3;
// Screw hole diameter (mm)
mount_hole = 5;
/* [Pegs] */
// Across corners of the hex peg (mm)
peg_diameter = 6;
peg_length = 40;
// Upward tilt from horizontal (degrees)
peg_tilt = 15;
// Bore down the peg's axis (mm)
peg_bore = 2.5;
/* [Rings] */
// Shank hole (mm)
ring_hole = 11;
ring_wall = 3;
// Ring height (mm)
ring_height = 12;
// Gap between the ring and the flange (mm)
ring_standoff = 15;
$fn = $preview ? 32 : 64;
pitch = count > 1 ? span / (count - 1) : 0;
flange_length = span + max(holder == "ring" ? ring_hole + 2 * ring_wall : peg_diameter, 0) + 4 * mount_hole;
// The flange lies in the XZ plane, wall side at y = 0 and holder side at
// y = -flange_thickness; holders stand out toward -y.
module flange() {
difference() {
translate([-flange_length / 2, -flange_thickness, -flange_height / 2])
cube([flange_length, flange_thickness, flange_height]);
for (s = [-1, 1])
translate([s * (flange_length / 2 - mount_hole), 1, 0]) rotate([90, 0, 0])
cylinder(d = mount_hole, h = flange_thickness + 2);
}
}
// A hex peg rising at peg_tilt from the flange face, its root cut flush with
// the flange so it sits flat on it.
module peg() {
difference() {
rotate([90 - peg_tilt, 0, 0]) rotate([0, 0, 30]) translate([0, 0, -peg_diameter])
cylinder(d = peg_diameter, h = peg_length + peg_diameter, $fn = 6);
rotate([90 - peg_tilt, 0, 0]) translate([0, 0, -peg_diameter - 1])
cylinder(d = peg_bore, h = peg_length + peg_diameter + 2);
// keep only what stands in front of the flange face
translate([-peg_diameter, 0, -2 * peg_length]) cube([2 * peg_diameter, 2 * peg_length, 4 * peg_length]);
}
}
// A ring for a screwdriver shank, on an arm out from the flange.
module ring() {
od = ring_hole + 2 * ring_wall;
y = -(ring_standoff + od / 2);
difference() {
union() {
translate([0, y, -ring_height / 2]) cylinder(d = od, h = ring_height);
translate([-ring_wall, y, -ring_height / 2]) cube([2 * ring_wall, -y, ring_height]);
}
translate([0, y, -ring_height / 2 - 1]) cylinder(d = ring_hole, h = ring_height + 2);
}
}
module tool_rack() {
union() {
flange();
for (i = [0:count - 1])
translate([-span / 2 + i * pitch, -flange_thickness, 0])
if (holder == "ring") ring(); else peg();
}
}
tool_rack();
attachments/tool_holder.scad on GitHub
hsw-clips/hsw_clip.scad
Source: hsw-clips/hsw_clip.scad
include <BOSL2/std.scad>
include <BOSL2/rounding.scad>
include <BOSL2_utils.scad>
include <hswx.scad>
$fn = $preview ? 32 : 64;
generate_clipconnector = false;
generate_clip = true;
generate_clip_rect_base = false;
generate_clip_hex_base = false;
// A double clip to hold two pieces of HSW together
module hsw_clipconnector(
slop = 0, // some extra gap so the fit is not so tight
rectbase = 0, // put a rectangular base on top and give it a height
hexbase = 0 // put a hexbase on top and give it a height
) {
bridge_x = hswx_outside - hswx_inside;
bridge_y = (hswx_inside / 2) - slop;
difference() {
union() {
// two clips with base
hsw_clip(center=true, rotate=180, type=0, rectbase=rectbase, slop=slop, hexbase=hexbase);
translate([hswx_outside, 0, 0])
hsw_clip(center=true, rotate=0, type=0, rectbase=rectbase, slop=slop, hexbase=hexbase);
translate([hswx_outside / 2 - bridge_x / 2, -bridge_y / 2, 0])
cube([bridge_x, bridge_y, height]);
}
height = max(rectbase, hexbase);
// carve out the stress-reliever between them to bend
translate([hswx_outside / 2, hswx_inside / 2, height])
rotate([90, 0, 0])
cylinder(h=hswx_inside, d=height);
}
}
plug_base_thickness = 1;
plug_base_lip = 1;
plug_stud_thickness = 1.7;
plug_stud_height = hswx_lock_depth - 2;
plug_spring_base = 3;
plug_spring_depth = 5;
plug_spring_thickness = 1.4;
module hsw_clip(
center = false, // center or start from [0,0,0]
rotate = 0, // rotate result
type = 0, // type not used -- may someday specify weak v. strong directional
hexbase = 0, // put a hexbase on top and give it a height
rectbase = 0, // put a rectangular base on top and give it a height
slop = 0
) // introduce slop to clip length if too tight
{
hswx_inside = hswx_inside - slop;
outline = [
[
[-plug_base_lip, 0],
[slop, 0, 0.1], // origin
[slop, hswx_lock_depth], // strong-side base
[-hswx_lock_lip, hswx_lock_lip + hswx_lock_depth, 0.5], // strong lock
[-hswx_lock_lip, hswx_depth, 1.5],
[plug_stud_thickness, hswx_depth], // spring
[plug_spring_base, hswx_depth, 3.5],
[hswx_inside / 2, hswx_depth - plug_spring_depth - 0.5, 5.0], // upper spring midpoint
[hswx_inside - plug_spring_base, hswx_depth, 3.5],
[hswx_inside - plug_stud_thickness, hswx_depth],
[hswx_inside, hswx_depth, 2.0], // weak lock
[hswx_inside + hswx_lock_lip, hswx_lock_depth + hswx_lock_lip, 0.5],
[hswx_inside, hswx_lock_depth], // weak-side base
[hswx_inside, 0.1, 0.1],
[hswx_inside + 0.1, 0],
[hswx_inside - plug_stud_thickness - 0.5, 0],
[hswx_inside - plug_stud_thickness, 0.5, 1.0],
/**/
[hswx_inside - plug_stud_thickness, hswx_depth - plug_spring_thickness - 1.4, 1.5],
[hswx_inside - plug_spring_base + .1, hswx_depth - plug_spring_thickness - 0.1, 1.0],
[hswx_inside / 2, hswx_depth - plug_spring_depth - plug_spring_thickness, 6.0],
[plug_spring_base, hswx_depth - plug_spring_thickness, 2.0],
[plug_stud_thickness, hswx_depth / 3, 1.0],
[hswx_inside / 3.4, 0],
[plug_stud_thickness, 0],
],
];
translate(center ? [0, 0, 0] : [hswx_inside / 2, hswx_inside / 4, 0])
rotate([0, 0, rotate])
union() {
translate([-hswx_inside / 2, -hswx_inside / 4, 0])
rotate([-90, 0, 0])
linear_extrude(hswx_inside / 2)
polygon(round_corners(path=points(outline[type]), radius=rads(outline[type]), $fn=90));
if (hexbase) {
cyl(l=hexbase, d=hswx_od - 1, $fn=6, anchor=BOTTOM, spin=30, rounding2=hexbase / 2);
}
if (rectbase) {
//rounding
cuboid(size=[hswx_outside, hswx_inside / 2, rectbase], rounding=rectbase / 2, edges=[TOP + LEFT, TOP + RIGHT], anchor=BOTTOM + CENTER);
}
}
}
// hsw_clip(center=true, rotate=0); //, type=0, hexbase=3);
if (generate_clipconnector)
rotate([90, 0, 0])
hsw_clipconnector(slop=0.05, rectbase=generate_clip_rect_base ? 3 : 0, hexbase=generate_clip_hex_base ? 3 : 0);
if (generate_clip)
hsw_clip(center=true, rectbase=generate_clip_rect_base ? 3 : 0, hexbase=generate_clip_hex_base ? 3 : 0);