// 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); }