3d-printing/lib/label-decorations/label-decorations.scad
Felipe M. e13e743d9d
Add battery symbol and switch box label preset to "PILAS"
Add sym_battery to the shared label decoration library: an upright cell
with a solid terminal nub and "+"/"-" marks in the hollow body. Register
it in symbol_shape and add it to the customizer dropdowns of both the bin
label and the box label.

Switch the gridfinity-box-label customizer defaults to a centered "PILAS"
text flanked by two battery symbols, replacing the M2-M5 screw preset.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018WMwnWKAqyrQveSoyi6o93
2026-09-09 22:31:46 +02:00

428 lines
15 KiB
OpenSCAD
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Shared label primitives used by gridfinity-bin-label and gridfinity-box-label.
// Provides:
// - symbol_shape(name, d, stroke): 2D screw/bit/washer/nut symbols
// - label_text(...) and label_symbol(...): positioned, extruded helpers
// - text_x(...) and symbol_x(...): horizontal alignment math
// - RoundedCube(size, radius): rounded cuboid base helper
// Tool for rounded cubes
module RoundedCube(size, radius) {
offsetfix = radius * 2;
width = size[0] - offsetfix;
height = size[1] - offsetfix;
depth = size[2];
translate([radius, radius, 0])
linear_extrude(height = depth)
offset(r = radius) square([width, height]);
}
// 2D screw-head symbols, centered at origin, fitting within a circle of diameter d
module sym_hex_rounded(d, stroke) {
// Hex socket cap screw (rounded outer head with hex socket cutout)
difference() {
circle(d = d);
rotate(30) circle(d = d * 0.55, $fn = 6);
}
}
module sym_hex_flat(d, stroke) {
// Hex head bolt (flat hexagonal head with central thread hole)
difference() {
rotate(30) circle(d = d, $fn = 6);
circle(d = d * 0.3);
}
}
module sym_phillips(d, stroke) {
difference() {
circle(d = d);
union() {
square([stroke, d * 0.75], center = true);
square([d * 0.75, stroke], center = true);
}
}
}
module sym_slotted(d, stroke) {
difference() {
circle(d = d);
square([d * 0.85, stroke], center = true);
}
}
module sym_torx(d, stroke) {
difference() {
circle(d = d);
for (a = [0:60:300])
rotate(a) square([stroke, d * 0.75], center = true);
}
}
module sym_square(d, stroke) {
difference() {
circle(d = d);
rotate(45) square(d * 0.45, center = true);
}
}
module sym_pozidriv(d, stroke) {
// Phillips cross plus a smaller diagonal cross
difference() {
circle(d = d);
union() {
square([stroke, d * 0.75], center = true);
square([d * 0.75, stroke], center = true);
rotate(45) square([stroke * 0.7, d * 0.55], center = true);
rotate(45) square([d * 0.55, stroke * 0.7], center = true);
}
}
}
// Side-profile screw silhouettes (head + threaded shaft), centered in a d×d bounding box
module sym_side_cap(d, stroke) {
// Hex socket cap screw: cylindrical head with a slim socket marker
sw = d * 0.28; sh = d * 0.6;
hw = d * 0.55; hh = d * 0.4;
translate([0, -d / 2]) {
translate([-sw / 2, 0]) square([sw, sh]);
difference() {
translate([-hw / 2, sh]) square([hw, hh]);
translate([-hw * 0.2, sh + hh * 0.55])
square([hw * 0.4, stroke * 0.7]);
}
}
}
module sym_side_flat(d, stroke) {
// Countersunk flat head: trapezoid that flares from shaft to flat top
sw = d * 0.28; sh = d * 0.6;
htop = d * 0.7; hbot = d * 0.32; hh = d * 0.4;
translate([0, -d / 2]) {
translate([-sw / 2, 0]) square([sw, sh]);
translate([0, sh])
polygon([
[-hbot / 2, 0], [hbot / 2, 0],
[htop / 2, hh], [-htop / 2, hh]
]);
translate([-htop * 0.4, sh + hh - stroke * 0.9])
square([htop * 0.8, stroke * 0.6]);
}
}
module sym_side_pan(d, stroke) {
// Pan head: rounded dome with broader skirt
sw = d * 0.28; sh = d * 0.55;
hw = d * 0.7; hh = d * 0.45;
r = hh * 0.55;
translate([0, -d / 2]) {
translate([-sw / 2, 0]) square([sw, sh]);
translate([0, sh])
hull() {
translate([-hw / 2 + r, hh - r]) circle(r = r);
translate([hw / 2 - r, hh - r]) circle(r = r);
translate([-hw / 2, 0]) square([hw, 0.01]);
}
}
}
module sym_side_button(d, stroke) {
// Button head: low rounded dome
sw = d * 0.28; sh = d * 0.7;
hw = d * 0.65; hh = d * 0.3;
r = hh * 0.95;
translate([0, -d / 2]) {
translate([-sw / 2, 0]) square([sw, sh]);
translate([0, sh])
hull() {
translate([-hw / 2 + r, 0]) circle(r = r);
translate([hw / 2 - r, 0]) circle(r = r);
translate([-hw / 2, 0]) square([hw, 0.01]);
}
}
}
module sym_side_hex(d, stroke) {
// Hex bolt: head profile shows flat top with chamfered corners
sw = d * 0.28; sh = d * 0.55;
hw = d * 0.7; hh = d * 0.45;
cf = hh * 0.3;
translate([0, -d / 2]) {
translate([-sw / 2, 0]) square([sw, sh]);
translate([0, sh])
polygon([
[-hw / 2, 0], [hw / 2, 0],
[hw / 2, hh - cf], [hw / 2 - cf, hh],
[-hw / 2 + cf, hh], [-hw / 2, hh - cf]
]);
}
}
// Drive-bit symbols: just the drive shape, without a surrounding circle. Useful
// when the symbol shares the label face with text and the empty circle would
// waste space. The shapes are scaled up to roughly fill the d×d bounding box.
module sym_bit_phillips(d, stroke) {
bs = stroke * 1.4;
union() {
square([bs, d * 0.95], center = true);
square([d * 0.95, bs], center = true);
}
}
module sym_bit_slotted(d, stroke) {
square([d * 0.95, stroke * 1.4], center = true);
}
module sym_bit_hex(d, stroke) {
rotate(30) circle(d = d * 0.95, $fn = 6);
}
module sym_bit_torx(d, stroke) {
bs = stroke * 1.4;
for (a = [0:60:300])
rotate(a) square([bs, d * 0.95], center = true);
}
module sym_bit_square(d, stroke) {
rotate(45) square(d * 0.65, center = true);
}
module sym_bit_pozidriv(d, stroke) {
bs = stroke * 1.4;
union() {
square([bs, d * 0.95], center = true);
square([d * 0.95, bs], center = true);
rotate(45) square([bs * 0.7, d * 0.7], center = true);
rotate(45) square([d * 0.7, bs * 0.7], center = true);
}
}
module sym_washer(d, stroke) {
difference() {
circle(d = d, $fn = 64);
circle(d = d - stroke * 2.4, $fn = 64);
}
}
module sym_nut(d, stroke) {
// Top-down nut: hex outline with a large central thread hole.
// Bigger hole than sym_hex_flat to read as a nut, not a bolt head.
difference() {
rotate(30) circle(d = d, $fn = 6);
circle(d = d * 0.5, $fn = 64);
}
}
module sym_printer(d, stroke) {
// Stylized paper-printer icon: a narrow paper input rectangle on top, a
// rounded-top body in the middle with an indicator dot on the right, and
// an output tray on the bottom. Each piece is drawn as a hollow outline
// and the three are unioned before the inner area is subtracted, so where
// the shapes overlap the internal lines disappear and the silhouette
// reads as a single printer. Centered in a d×d bounding box.
s = stroke * 0.85;
cr = d * 0.09;
cr_in = cr - s;
paper_w = d * 0.42; paper_h = d * 0.32;
paper_x = (d - paper_w) / 2;
paper_y = d * 0.62;
body_w = d * 0.86; body_h = d * 0.45;
body_x = (d - body_w) / 2;
body_y = d * 0.32;
tray_w = d * 0.62; tray_h = d * 0.4;
tray_x = (d - tray_w) / 2;
tray_y = 0;
translate([-d / 2, -d / 2]) {
difference() {
// Outer silhouette: union of paper, body (rounded top), tray
union() {
translate([paper_x, paper_y]) square([paper_w, paper_h]);
translate([body_x, body_y]) square([body_w, body_h - cr]);
translate([body_x, body_y + body_h - cr])
hull() {
translate([cr, 0]) circle(r = cr);
translate([body_w - cr, 0]) circle(r = cr);
square([body_w, 0.001]);
}
translate([tray_x, tray_y]) square([tray_w, tray_h]);
}
// Inner cutout: each piece inset by s, also unioned so any
// overlapping wall segments between pieces dissolve.
union() {
translate([paper_x + s, paper_y + s])
square([paper_w - 2 * s, paper_h - 2 * s]);
translate([body_x + s, body_y + s])
square([body_w - 2 * s, body_h - cr - s]);
translate([body_x, body_y + body_h - cr])
hull() {
translate([cr, 0]) circle(r = cr_in);
translate([body_w - cr, 0]) circle(r = cr_in);
translate([s, 0]) square([body_w - 2 * s, 0.001]);
}
translate([tray_x + s, tray_y + s])
square([tray_w - 2 * s, tray_h - 2 * s]);
}
}
// Indicator dot inside the body (sits in the hollow interior).
translate([body_x + body_w - d * 0.13, body_y + body_h * 0.62])
circle(d = s * 1.6);
}
}
module sym_magnet(d, stroke) {
// Horseshoe magnet: solid U-shape silhouette with horizontal pole-tip
// slits across each leg near the open mouth. Centered in a d×d bounding
// box; the open mouth points down (-y).
outer_w = d * 0.88;
arm_w = d * 0.26;
arc_r = outer_w / 2;
arc_r_in = arc_r - arm_w;
arm_h = d - arc_r;
inner_w = outer_w - 2 * arm_w;
leg_x_l = (d - outer_w) / 2;
leg_x_r = leg_x_l + outer_w - arm_w;
tip_y = d * 0.16;
slit_h = stroke * 0.7;
translate([-d / 2, -d / 2]) {
difference() {
union() {
translate([leg_x_l, 0]) square([arm_w, arm_h]);
translate([leg_x_r, 0]) square([arm_w, arm_h]);
translate([d / 2, arm_h])
difference() {
intersection() {
circle(r = arc_r);
translate([-arc_r, 0]) square([outer_w, arc_r]);
}
translate([0, -0.01])
intersection() {
circle(r = arc_r_in);
translate([-arc_r_in, 0])
square([inner_w, arc_r_in + 0.02]);
}
}
}
translate([leg_x_l - 0.01, tip_y])
square([arm_w + 0.02, slit_h]);
translate([leg_x_r - 0.01, tip_y])
square([arm_w + 0.02, slit_h]);
}
}
}
module sym_battery(d, stroke) {
// Side-on battery cell: a hollow rounded body with a solid positive
// terminal nub on top. A "+" mark and a "-" mark sit in the hollow
// interior so the icon reads as a battery, not a plain box. Centered in
// a d x d bounding box; the terminal points up (+y).
s = stroke * 0.85;
cr = d * 0.09;
cap_w = d * 0.26; cap_h = d * 0.1;
body_w = d * 0.62; body_h = d - cap_h;
body_x = (d - body_w) / 2;
cap_x = (d - cap_w) / 2;
mark_len = body_w * 0.42;
mark_t = s;
translate([-d / 2, -d / 2]) {
// Solid terminal nub.
translate([cap_x, body_h]) square([cap_w, cap_h]);
// Hollow body outline.
difference() {
translate([body_x + cr, cr])
offset(r = cr) square([body_w - 2 * cr, body_h - 2 * cr]);
translate([body_x + s + cr, s + cr])
offset(r = cr) square([body_w - 2 * s - 2 * cr,
body_h - 2 * s - 2 * cr]);
}
// Polarity marks inside the hollow interior.
translate([d / 2, body_h * 0.68]) {
square([mark_len, mark_t], center = true);
square([mark_t, mark_len], center = true);
}
translate([d / 2, body_h * 0.3])
square([mark_len, mark_t], center = true);
}
}
module symbol_shape(name, d, stroke) {
if (name == "hex_rounded") sym_hex_rounded(d, stroke);
else if (name == "hex_flat") sym_hex_flat(d, stroke);
else if (name == "phillips") sym_phillips(d, stroke);
else if (name == "slotted") sym_slotted(d, stroke);
else if (name == "torx") sym_torx(d, stroke);
else if (name == "square") sym_square(d, stroke);
else if (name == "pozidriv") sym_pozidriv(d, stroke);
else if (name == "bit_phillips") sym_bit_phillips(d, stroke);
else if (name == "bit_slotted") sym_bit_slotted(d, stroke);
else if (name == "bit_hex") sym_bit_hex(d, stroke);
else if (name == "bit_torx") sym_bit_torx(d, stroke);
else if (name == "bit_square") sym_bit_square(d, stroke);
else if (name == "bit_pozidriv") sym_bit_pozidriv(d, stroke);
else if (name == "side_cap") sym_side_cap(d, stroke);
else if (name == "side_flat") sym_side_flat(d, stroke);
else if (name == "side_pan") sym_side_pan(d, stroke);
else if (name == "side_button") sym_side_button(d, stroke);
else if (name == "side_hex") sym_side_hex(d, stroke);
else if (name == "washer") sym_washer(d, stroke);
else if (name == "nut") sym_nut(d, stroke);
else if (name == "printer") sym_printer(d, stroke);
else if (name == "magnet") sym_magnet(d, stroke);
else if (name == "battery") sym_battery(d, stroke);
}
// Horizontal anchor for a text baseline within a labelX-wide rectangle.
// Match the original bin-label positioning: "left" aligns to x=0, "right" to x=labelX.
function text_x(align, dx, labelX) =
(align == "left") ? 0 + dx :
(align == "center") ? (labelX / 2) + dx :
(align == "right") ? labelX + dx : 0;
// Horizontal anchor for a symbol's center, keeping its bounding box inside the label.
function symbol_x(align, size, dx, labelX) =
(align == "left") ? (size / 2) + dx :
(align == "center") ? (labelX / 2) + dx :
(align == "right") ? labelX - (size / 2) + dx : 0;
// Split a string on "\n" into a list of lines.
function split_lines(s, i = 0, current = "", acc = []) =
(i >= len(s)) ? concat(acc, [current]) :
(s[i] == "\n") ? split_lines(s, i + 1, "", concat(acc, [current])) :
split_lines(s, i + 1, str(current, s[i]), acc);
// Render text at (x, y, z), extruded by extrude_h. Embedded "\n" splits the
// string into multiple lines stacked vertically and centered as a block on y.
// line_spacing is the distance between line centers in mm; if 0 it defaults
// to size * 1.2.
module label_text(text_str, x, y, z, size, font, style, align, extrude_h,
line_spacing = 0) {
if (text_str != "") {
lines = split_lines(text_str);
num = len(lines);
spacing = (line_spacing > 0) ? line_spacing : size * 1.2;
y_top = y + (num - 1) * spacing / 2;
translate([0, 0, z])
linear_extrude(extrude_h)
for (i = [0 : num - 1])
if (lines[i] != "")
translate([x, y_top - i * spacing])
text(lines[i], size,
font = str(font, ":", style),
halign = align, valign = "center");
}
}
// Render a single symbol at (x, y, z), extruded by extrude_h. Skips when name == "none".
module label_symbol(name, x, y, z, size, stroke, extrude_h) {
if (name != "none")
translate([x, y, z])
linear_extrude(extrude_h)
symbol_shape(name, size, stroke);
}