/* ======================================================================= DUO3DPRINT - ULTIMATE PERSONALIZED DESK ORGANIZER MakerWorld Parametric Model Maker / OpenSCAD VERSION 1.5 PRO - PATCHED (all code-review fixes) Changes vs 1.2 PRO: (1) text_rotation / symbol_rotation now ROLL the decoration in the face plane instead of yawing it out of the wall. (2) Name & symbol are pushed out by front_plate_depth when the front plate is active, so they no longer sink into the plate. (3) text_x / text_z / symbol_x / symbol_z are clamped to the usable front face, so the defaults no longer overflow smaller boxes. (4) heart_symbol() no longer emits a duplicate seam vertex. (5) estimated_name_width() now accounts for script_width_scale AND script_shear, so auto-fit no longer lets sheared script names overrun name_max_width. (6) is_script_font() / custom font / selected_font() collapsed into a single data-driven lookup table (FONT_TABLE / CUSTOM_FONT_TABLE). (7) Name & symbol now bite 0.6 mm into the wall (was 0.04 mm) so a single-colour print fuses them properly; multicolour is unaffected. (8) Every symbol is normalised to symbol_size millimetres tall via resize(), so the size slider is consistent across all 70+ symbols. (9) "Crescent Moon" and "Cat Paw" now have their own geometry instead of aliasing "Moon" / "Paw". (10) Redundant inner None-guards removed from name_object()/symbol_object(). (11) Ultra quality prints an echo() warning about render time. (12) corner_radius review: - corner_radius_eff() clamps the radius to (smallest side)/2 - 1 so the footprint can no longer collapse to the 0.5 mm fallback in rounded_box() (which produced malformed, non-rectangular geometry). A large radius on a shallow box still, by definition, gives a stadium outline - that is what the value means. - new base_fillet: true quarter-round radius on the bottom outer edge (was a hard 90-deg edge - only the vertical corners were ever rounded). - the inner-cavity cut now starts at bottom-0.02 instead of exactly bottom, so its face is not coplanar with the floor top (coplanar difference -> preview z-fighting / non-manifold sliver). KNOWN LIMITATION: with ASYMMETRIC wall thicknesses the wall necks down toward min(wall) through the rounded corner, because a single offset radius drives all four inner corners. Keep the 4 walls equal for a uniform corner wall; a per-side inset cavity is a redesign. NOT changed (need your input - see notes below): - Font names: most Windows/macOS system fonts and unbundled Google fonts will NOT resolve on MakerWorld's Linux renderer and silently fall back. Liberation / DejaVu / Noto are the safe set. Trim the font_style list to what you have verified renders. - The font_style dropdown is one ~130-item line; consider splitting it into "Font category" + "Font" for a usable Customizer UI. ======================================================================= */ /* [01 - NAME & FONT] */ // Name name_text = "Laura"; // Font preset font_style = "Lobster"; // [None,Lobster,Pacifico,Pacifico Regular,Dancing Script,Great Vibes,Satisfy,Kaushan Script,Courgette,Allura,Alex Brush,Sacramento,Yellowtail,Cookie,Parisienne,Marck Script,Caveat,Caveat Brush,Leckerli One,Lobster Two,Oleo Script,Oleo Script Swash Caps,Berkshire Swash,Damion,Italianno,Merienda,Merienda One,Norican,Pattaya,Rancho,Rochester,Rouge Script,Seaweed Script,Style Script,Tangerine,Yesteryear,Mr Dafoe,Mr De Haviland,Mrs Saint Delafield,Qwigley,Pinyon Script,Petit Formal Script,Playball,Engagement,Euphoria Script,Felipa,Fondamento,Freehand,Grand Hotel,Herr Von Muellerhoff,Homemade Apple,Imperial Script,Inspiration,Island Moments,Kristi,La Belle Aurore,Lavishly Yours,League Script,Licorice,Love Light,Mea Culpa,Meow Script,Monsieur La Doulaise,MonteCarlo,Moon Dance,Ms Madi,Oooh Baby,Passions Conflict,Praise,Princess Sofia,Qwitcher Grypen,Reenie Beanie,Send Flowers,Shadows Into Light,Shadows Into Light Two,Square Peg,The Nautigal,Water Brush,WindSong,Zeyada,Brush Script,Segoe Script,Segoe Print,Lucida Handwriting,Lucida Calligraphy,Monotype Corsiva,Apple Chancery,URW Chancery,URW Chancery Italic,URW Chancery Bold Italic,Arial Bold,Arial,Arial Italic,Arial Bold Italic,Comic Sans,Comic Sans Bold,Helvetica,Helvetica Bold,Helvetica Oblique,Times New Roman,Times New Roman Bold,Times New Roman Italic,Georgia,Georgia Bold,Georgia Italic,Verdana,Verdana Bold,Verdana Italic,Tahoma,Tahoma Bold,Trebuchet,Trebuchet Bold,Trebuchet Italic,DejaVu Sans,DejaVu Sans Bold,DejaVu Sans Condensed,DejaVu Sans Condensed Bold,DejaVu Serif,DejaVu Serif Bold,Liberation Sans,Liberation Sans Bold,Liberation Sans Italic,Liberation Serif,Liberation Serif Bold,Liberation Mono,Liberation Mono Bold,Noto Sans,Noto Sans Bold,Noto Serif,Noto Serif Bold,FreeSans,FreeSans Bold,FreeSerif,FreeSerif Bold,Custom Font] // Custom font preset custom_font_choice = "Arial Bold"; // [Arial Bold,Arial,Arial Italic,Brush Script,Segoe Script,Segoe Print,Lucida Handwriting,Lucida Calligraphy,Monotype Corsiva,Apple Chancery,URW Chancery,URW Chancery Italic,Comic Sans,Helvetica,Helvetica Bold,Times New Roman,Times New Roman Bold,Georgia,Georgia Bold,Verdana,Verdana Bold,Tahoma,Trebuchet,DejaVu Sans,DejaVu Sans Bold,DejaVu Serif,Liberation Sans,Liberation Sans Bold,Liberation Serif,Liberation Serif Bold,Liberation Mono,Noto Sans,Noto Sans Bold,Noto Serif,FreeSans,FreeSerif,Manual] // Manual font // Examples: Pacifico:style=Regular, Lobster:style=Regular, Dancing Script:style=Regular manual_font_name = "Pacifico:style=Regular"; // Text size text_size = 23; // [8:0.5:42] // Letter spacing letter_spacing = 0.92; // [0.55:0.01:1.40] // Automatically shrink long names auto_fit_text = true; // Maximum name width name_max_width = 76; // [30:1:145] // Text extrusion text_depth = 1.6; // [0.5:0.1:4] // Name X position text_x = -9; // [-60:1:60] // Name Z position text_z = 17; // [5:1:48] // Name rotation text_rotation = 0; // [-25:1:25] /* [02 - SCRIPT TUNING] */ // Width of script fonts script_width_scale = 1.08; // [0.60:0.02:1.60] // Height of script fonts script_height_scale = 1.03; // [0.60:0.02:1.60] // Additional italic effect script_shear = 0; // [-20:1:20] /* [03 - SYMBOL] */ // Symbol symbol_type = "Heart"; // [None,Heart,Double Heart,Heart Outline,Star,Star Outline,Flower,Daisy,Rose,Paw,Cat Paw,Crown,Princess Crown,Diamond,Moon,Crescent Moon,Sun,Lightning,Butterfly,Clover,Sparkle,Sparkles,Cloud,Rainbow,Leaf,Monstera,Tree,Cat,Cat Face,Dog Bone,Dog Face,Fish,Dolphin,Music Note,Music Notes,House,Gift,Balloon,Apple,Cherry,Strawberry,Coffee Cup,Cupcake,Ice Cream,Camera,Gamepad,Football,Basketball,Soccer Ball,Car,Truck,Tractor,Rocket,Planet,Unicorn,Ghost,Skull,Smile,Peace,Infinity,Key,Anchor,Bell,Book,Pencil,Scissors,Gear,Wrench,Hammer,Plus,Cross,Hexagon,Circle,Triangle] // Symbol size symbol_size = 19; // [6:1:38] // Symbol X symbol_x = 43; // [-60:1:60] // Symbol Z symbol_z = 17; // [5:1:48] // Symbol extrusion symbol_depth = 1.6; // [0.5:0.1:4] // Rotation symbol_rotation = 0; // [-180:5:180] /* [04 - COLORS] */ box_color = "White"; // [White,Black,Dark Gray,Gray,Light Gray,Red,Dark Red,Pink,Hot Pink,Light Pink,Peach,Orange,Yellow,Gold,Green,Dark Green,Lime,Mint,Turquoise,Cyan,Sky Blue,Blue,Dark Blue,Purple,Dark Purple,Lavender,Brown,Beige,Cream] name_color = "Hot Pink"; // [White,Black,Dark Gray,Gray,Light Gray,Red,Dark Red,Pink,Hot Pink,Light Pink,Peach,Orange,Yellow,Gold,Green,Dark Green,Lime,Mint,Turquoise,Cyan,Sky Blue,Blue,Dark Blue,Purple,Dark Purple,Lavender,Brown,Beige,Cream] symbol_color = "Hot Pink"; // [White,Black,Dark Gray,Gray,Light Gray,Red,Dark Red,Pink,Hot Pink,Light Pink,Peach,Orange,Yellow,Gold,Green,Dark Green,Lime,Mint,Turquoise,Cyan,Sky Blue,Blue,Dark Blue,Purple,Dark Purple,Lavender,Brown,Beige,Cream] divider_color = "White"; // [White,Black,Dark Gray,Gray,Light Gray,Red,Dark Red,Pink,Hot Pink,Light Pink,Peach,Orange,Yellow,Gold,Green,Dark Green,Lime,Mint,Turquoise,Cyan,Sky Blue,Blue,Dark Blue,Purple,Dark Purple,Lavender,Brown,Beige,Cream] texture_color = "Light Gray"; // [White,Black,Dark Gray,Gray,Light Gray,Red,Dark Red,Pink,Hot Pink,Light Pink,Peach,Orange,Yellow,Gold,Green,Dark Green,Lime,Mint,Turquoise,Cyan,Sky Blue,Blue,Dark Blue,Purple,Dark Purple,Lavender,Brown,Beige,Cream] /* [05 - ORGANIZER SIZE] */ // Width width = 115; // [75:1:190] // Depth depth = 70; // [45:1:130] // Height height = 45; // [25:1:85] // Individual wall thicknesses // Front wall (name / decoration side) front_wall = 3; // [1.5:0.5:10] // Back wall back_wall = 3; // [1.5:0.5:10] // Left wall left_wall = 3; // [1.5:0.5:10] // Right wall right_wall = 3; // [1.5:0.5:10] // Bottom bottom = 3; // [1.5:0.5:7] // Outer corner radius (auto-limited to fit the box) corner_radius = 10; // [3:1:22] // Bottom outer edge fillet radius (0 = sharp edge) base_fillet = 1.5; // [0:0.5:8] /* [06 - COMPARTMENTS] */ compartment_layout = "2x3"; // [None,2 Vertical,3 Vertical,4 Vertical,5 Vertical,6 Vertical,7 Vertical,8 Vertical,2 Horizontal,3 Horizontal,4 Horizontal,5 Horizontal,6 Horizontal,2x2,2x3,2x4,2x5,3x2,3x3,3x4,3x5,4x2,4x3,4x4,5x2,5x3,Large Left + 2,Large Left + 3,Large Left + 4,Large Right + 2,Large Right + 3,Large Right + 4,Front 2 + Back Large,Front 3 + Back Large,Front 4 + Back Large,Back 2 + Front Large,Back 3 + Front Large,Back 4 + Front Large,Custom Grid] // Custom columns custom_columns = 3; // [1:1:8] // Custom rows custom_rows = 2; // [1:1:8] // Divider thickness divider_thickness = 2.2; // [1:0.2:6] // Divider height divider_height = 32; // [8:1:70] // Divider connection to wall divider_overlap = 0.35; // [0:0.05:1] // Ratio for large compartment large_ratio = 0.58; // [0.35:0.01:0.75] /* [07 - TEXTURE] */ // Texture texture_style = "Honeycomb"; // [None,Honeycomb,Hexagons,Dots,Vertical Lines,Horizontal Lines,Diagonal Lines,Grid,Diamonds,Stars,Plus,Triangles,Brick] // Texture type // Flush = texture is embedded into the wall and ends exactly level with the outer surface. // Raised = texture sits above the wall. // Engraved = texture is cut into the wall. texture_type = "Flush"; // [None,Flush,Raised,Engraved] // Flush insert depth inside the wall texture_flush_depth = 0.7; // [0.2:0.05:2.5] // Optional relief above the wall in Flush mode // 0 = perfectly flush, higher value = slightly raised texture_flush_relief = 0.0; // [0:0.05:2] // Texture location texture_location = "Back + Sides"; // [None,Front,Back,Left,Right,Front + Back,Left + Right,Back + Sides,All Sides] // Pattern size texture_size = 10; // [5:0.5:24] // Pattern line thickness texture_line = 1.0; // [0.4:0.1:3] // Depth texture_depth = 0.5; // [0.15:0.05:1.5] // Clear space near rounded corners texture_corner_margin = 5; // [2:1:16] // Extra side margin texture_side_margin = 3; // [0:1:15] // Bottom margin texture_bottom_margin = 6; // [2:1:20] // Top margin texture_top_margin = 6; // [2:1:20] // Keep front clean behind name keep_front_clean = true; /* [08 - FRONT PLATE] */ front_plate_style = "None"; // [None,Simple] // Plate depth front_plate_depth = 0.8; // [0.3:0.1:3] // Plate margin front_plate_margin = 5; // [2:1:16] /* [09 - EXPORT PART] */ // Body Only = shell walls + floor, no dividers. // Organizer Only = shell + dividers (the full container, undecorated). part = "All"; // [All,Body Only,Organizer Only,Dividers Only,Name Only,Symbol Only,Decoration Only,Texture Only] /* [10 - QUALITY] */ quality = "High"; // [Draft,Normal,High,Ultra] // ======================================================================= // QUALITY // ======================================================================= $fn = quality == "Draft" ? 24 : quality == "Normal" ? 40 : quality == "High" ? 64 : 96; // (11) Ultra is $fn=96 applied to every circle in every texture tile plus // the pocket / engrave CSG - it can hit MakerWorld's render time limit. if(quality == "Ultra") echo("NOTE: quality=Ultra is slow, especially with textures. Use High unless you need it."); // ======================================================================= // COLOR DATABASE // ======================================================================= function get_color(n) = n=="White" ? [1,1,1] : n=="Black" ? [0.03,0.03,0.03] : n=="Dark Gray" ? [0.16,0.16,0.16] : n=="Gray" ? [0.45,0.45,0.45] : n=="Light Gray" ? [0.76,0.76,0.76] : n=="Red" ? [0.92,0.04,0.06] : n=="Dark Red" ? [0.48,0.02,0.04] : n=="Pink" ? [1,0.35,0.58] : n=="Hot Pink" ? [1,0.08,0.43] : n=="Light Pink" ? [1,0.68,0.78] : n=="Peach" ? [1,0.58,0.45] : n=="Orange" ? [1,0.35,0.02] : n=="Yellow" ? [1,0.82,0.02] : n=="Gold" ? [1,0.58,0.02] : n=="Green" ? [0.05,0.62,0.22] : n=="Dark Green" ? [0.02,0.28,0.10] : n=="Lime" ? [0.45,0.95,0.05] : n=="Mint" ? [0.25,0.90,0.65] : n=="Turquoise" ? [0.05,0.75,0.70] : n=="Cyan" ? [0.05,0.90,1] : n=="Sky Blue" ? [0.20,0.65,1] : n=="Blue" ? [0.05,0.25,0.90] : n=="Dark Blue" ? [0.02,0.08,0.40] : n=="Purple" ? [0.50,0.10,0.85] : n=="Dark Purple" ? [0.25,0.03,0.45] : n=="Lavender" ? [0.72,0.55,1] : n=="Brown" ? [0.38,0.18,0.07] : n=="Beige" ? [0.84,0.72,0.55] : n=="Cream" ? [1,0.93,0.75] : [1,1,1]; // ======================================================================= // FONT DATABASE (data-driven - replaces the old triple-maintained lists) // // Each row: [ preset name , OpenSCAD font string , is_script ] // NOTE: many of these names are Windows/macOS system fonts or unbundled // Google fonts. On MakerWorld's Linux renderer an unavailable name // silently falls back to the default face - test-render before you // rely on a preset. Liberation / DejaVu / Noto are the safe set. // ======================================================================= FONT_TABLE = [ // ---- script / handwritten ------------------------------------------- ["Lobster", "Lobster", true], ["Pacifico", "Pacifico", true], ["Pacifico Regular", "Pacifico:style=Regular", true], ["Dancing Script", "Dancing Script", true], ["Great Vibes", "Great Vibes", true], ["Satisfy", "Satisfy", true], ["Kaushan Script", "Kaushan Script", true], ["Courgette", "Courgette", true], ["Allura", "Allura", true], ["Alex Brush", "Alex Brush", true], ["Sacramento", "Sacramento", true], ["Yellowtail", "Yellowtail", true], ["Cookie", "Cookie", true], ["Parisienne", "Parisienne", true], ["Marck Script", "Marck Script", true], ["Caveat", "Caveat", true], ["Caveat Brush", "Caveat Brush", true], ["Leckerli One", "Leckerli One", true], ["Lobster Two", "Lobster Two", true], ["Oleo Script", "Oleo Script", true], ["Oleo Script Swash Caps", "Oleo Script Swash Caps", true], ["Berkshire Swash", "Berkshire Swash", true], ["Damion", "Damion", true], ["Italianno", "Italianno", true], ["Merienda", "Merienda", true], ["Merienda One", "Merienda One", true], ["Norican", "Norican", true], ["Pattaya", "Pattaya", true], ["Rancho", "Rancho", true], ["Rochester", "Rochester", true], ["Rouge Script", "Rouge Script", true], ["Seaweed Script", "Seaweed Script", true], ["Style Script", "Style Script", true], ["Tangerine", "Tangerine", true], ["Yesteryear", "Yesteryear", true], ["Mr Dafoe", "Mr Dafoe", true], ["Mr De Haviland", "Mr De Haviland", true], ["Mrs Saint Delafield", "Mrs Saint Delafield", true], ["Qwigley", "Qwigley", true], ["Pinyon Script", "Pinyon Script", true], ["Petit Formal Script", "Petit Formal Script", true], ["Playball", "Playball", true], ["Engagement", "Engagement", true], ["Euphoria Script", "Euphoria Script", true], ["Felipa", "Felipa", true], ["Fondamento", "Fondamento", true], ["Freehand", "Freehand", true], ["Grand Hotel", "Grand Hotel", true], ["Herr Von Muellerhoff", "Herr Von Muellerhoff", true], ["Homemade Apple", "Homemade Apple", true], ["Imperial Script", "Imperial Script", true], ["Inspiration", "Inspiration", true], ["Island Moments", "Island Moments", true], ["Kristi", "Kristi", true], ["La Belle Aurore", "La Belle Aurore", true], ["Lavishly Yours", "Lavishly Yours", true], ["League Script", "League Script", true], ["Licorice", "Licorice", true], ["Love Light", "Love Light", true], ["Mea Culpa", "Mea Culpa", true], ["Meow Script", "Meow Script", true], ["Monsieur La Doulaise", "Monsieur La Doulaise", true], ["MonteCarlo", "MonteCarlo", true], ["Moon Dance", "Moon Dance", true], ["Ms Madi", "Ms Madi", true], ["Oooh Baby", "Oooh Baby", true], ["Passions Conflict", "Passions Conflict", true], ["Praise", "Praise", true], ["Princess Sofia", "Princess Sofia", true], ["Qwitcher Grypen", "Qwitcher Grypen", true], ["Reenie Beanie", "Reenie Beanie", true], ["Send Flowers", "Send Flowers", true], ["Shadows Into Light", "Shadows Into Light", true], ["Shadows Into Light Two", "Shadows Into Light Two", true], ["Square Peg", "Square Peg", true], ["The Nautigal", "The Nautigal", true], ["Water Brush", "Water Brush", true], ["WindSong", "WindSong", true], ["Zeyada", "Zeyada", true], ["Brush Script", "Brush Script MT", true], ["Segoe Script", "Segoe Script", true], ["Segoe Print", "Segoe Print", true], ["Lucida Handwriting", "Lucida Handwriting", true], ["Lucida Calligraphy", "Lucida Calligraphy", true], ["Monotype Corsiva", "Monotype Corsiva", true], ["Apple Chancery", "Apple Chancery", true], ["URW Chancery", "URW Chancery L", true], ["URW Chancery Italic", "URW Chancery L:style=Italic", true], ["URW Chancery Bold Italic","URW Chancery L:style=Bold Italic", true], // ---- upright / display -------------------------------------------- ["Arial Bold", "Arial:style=Bold", false], ["Arial", "Arial", false], ["Arial Italic", "Arial:style=Italic", false], ["Arial Bold Italic", "Arial:style=Bold Italic", false], ["Comic Sans", "Comic Sans MS", false], ["Comic Sans Bold", "Comic Sans MS:style=Bold", false], ["Helvetica", "Helvetica", false], ["Helvetica Bold", "Helvetica:style=Bold", false], ["Helvetica Oblique", "Helvetica:style=Oblique", false], ["Times New Roman", "Times New Roman", false], ["Times New Roman Bold", "Times New Roman:style=Bold", false], ["Times New Roman Italic", "Times New Roman:style=Italic", false], ["Georgia", "Georgia", false], ["Georgia Bold", "Georgia:style=Bold", false], ["Georgia Italic", "Georgia:style=Italic", false], ["Verdana", "Verdana", false], ["Verdana Bold", "Verdana:style=Bold", false], ["Verdana Italic", "Verdana:style=Italic", false], ["Tahoma", "Tahoma", false], ["Tahoma Bold", "Tahoma:style=Bold", false], ["Trebuchet", "Trebuchet MS", false], ["Trebuchet Bold", "Trebuchet MS:style=Bold", false], ["Trebuchet Italic", "Trebuchet MS:style=Italic", false], ["DejaVu Sans", "DejaVu Sans", false], ["DejaVu Sans Bold", "DejaVu Sans:style=Bold", false], ["DejaVu Sans Condensed", "DejaVu Sans:style=Condensed", false], ["DejaVu Sans Condensed Bold", "DejaVu Sans:style=Condensed Bold", false], ["DejaVu Serif", "DejaVu Serif", false], ["DejaVu Serif Bold", "DejaVu Serif:style=Bold", false], ["Liberation Sans", "Liberation Sans", false], ["Liberation Sans Bold", "Liberation Sans:style=Bold", false], ["Liberation Sans Italic", "Liberation Sans:style=Italic", false], ["Liberation Serif", "Liberation Serif", false], ["Liberation Serif Bold", "Liberation Serif:style=Bold", false], ["Liberation Mono", "Liberation Mono", false], ["Liberation Mono Bold", "Liberation Mono:style=Bold", false], ["Noto Sans", "Noto Sans", false], ["Noto Sans Bold", "Noto Sans:style=Bold", false], ["Noto Serif", "Noto Serif", false], ["Noto Serif Bold", "Noto Serif:style=Bold", false], ["FreeSans", "FreeSans", false], ["FreeSans Bold", "FreeSans:style=Bold", false], ["FreeSerif", "FreeSerif", false], ["FreeSerif Bold", "FreeSerif:style=Bold", false] ]; CUSTOM_FONT_TABLE = [ ["Arial Bold", "Arial:style=Bold"], ["Arial", "Arial"], ["Arial Italic", "Arial:style=Italic"], ["Brush Script", "Brush Script MT"], ["Segoe Script", "Segoe Script"], ["Segoe Print", "Segoe Print"], ["Lucida Handwriting", "Lucida Handwriting"], ["Lucida Calligraphy", "Lucida Calligraphy"], ["Monotype Corsiva", "Monotype Corsiva"], ["Apple Chancery", "Apple Chancery"], ["URW Chancery", "URW Chancery L"], ["URW Chancery Italic", "URW Chancery L:style=Italic"], ["Comic Sans", "Comic Sans MS"], ["Helvetica", "Helvetica"], ["Helvetica Bold", "Helvetica:style=Bold"], ["Times New Roman", "Times New Roman"], ["Times New Roman Bold", "Times New Roman:style=Bold"], ["Georgia", "Georgia"], ["Georgia Bold", "Georgia:style=Bold"], ["Verdana", "Verdana"], ["Verdana Bold", "Verdana:style=Bold"], ["Tahoma", "Tahoma"], ["Trebuchet", "Trebuchet MS"], ["DejaVu Sans", "DejaVu Sans"], ["DejaVu Sans Bold", "DejaVu Sans:style=Bold"], ["DejaVu Serif", "DejaVu Serif"], ["Liberation Sans", "Liberation Sans"], ["Liberation Sans Bold", "Liberation Sans:style=Bold"], ["Liberation Serif", "Liberation Serif"], ["Liberation Serif Bold", "Liberation Serif:style=Bold"], ["Liberation Mono", "Liberation Mono"], ["Noto Sans", "Noto Sans"], ["Noto Sans Bold", "Noto Sans:style=Bold"], ["Noto Serif", "Noto Serif"], ["FreeSans", "FreeSans"], ["FreeSerif", "FreeSerif"] // "Manual" is handled directly -> manual_font_name ]; FONT_FALLBACK = "Liberation Sans:style=Bold"; function table_row(tbl, key, i=0) = i >= len(tbl) ? undef : tbl[i][0] == key ? tbl[i] : table_row(tbl, key, i+1); function custom_font_string() = custom_font_choice == "Manual" ? manual_font_name : let (row = table_row(CUSTOM_FONT_TABLE, custom_font_choice)) row == undef ? FONT_FALLBACK : row[1]; function selected_font() = font_style == "Custom Font" ? custom_font_string() : let (row = table_row(FONT_TABLE, font_style)) row == undef ? FONT_FALLBACK : row[1]; function is_script_font() = let (row = table_row(FONT_TABLE, font_style)) row == undef ? false : row[2]; // ======================================================================= // DECORATION PLACEMENT HELPERS (patch 2 + 3) // ======================================================================= function clampv(v, lo, hi) = min(max(v, lo), hi); // How far the name / symbol must move out so they sit ON the front plate // instead of inside it. function front_face_offset() = front_plate_style == "Simple" ? front_plate_depth : 0; // Usable half-width of the flat part of the front face. function deco_x_limit() = max(width/2 - corner_radius_eff() - 4, 4); function deco_z_lo() = 4; function deco_z_hi() = max(height - 4, deco_z_lo() + 2); function name_pos_x() = clampv(text_x, -deco_x_limit(), deco_x_limit()); function name_pos_z() = clampv(text_z, deco_z_lo(), deco_z_hi()); function symbol_pos_x() = clampv(symbol_x, -deco_x_limit(), deco_x_limit()); function symbol_pos_z() = clampv(symbol_z, deco_z_lo(), deco_z_hi()); // ======================================================================= // NAME AUTO FIT // ======================================================================= function estimated_name_width() = // (5) glyph run width ... max(len(name_text),1) * text_size * ( is_script_font() ? 0.50 : 0.60 ) * letter_spacing * // ... times the extra width script mode adds ... ( is_script_font() ? script_width_scale : 1 ) + // ... plus the horizontal lean the shear adds (roughly one glyph height). text_size * abs(tan(script_shear)); function name_fit_scale() = auto_fit_text ? min( 1, name_max_width / max( estimated_name_width(), 1 ) ) : 1; // ======================================================================= // TEXTURE HELPERS // ======================================================================= function texture_active() = texture_style!="None" && texture_type!="None" && texture_location!="None"; function front_tex() = texture_location=="Front" || texture_location=="Front + Back" || texture_location=="All Sides"; function back_tex() = texture_location=="Back" || texture_location=="Front + Back" || texture_location=="Back + Sides" || texture_location=="All Sides"; function left_tex() = texture_location=="Left" || texture_location=="Left + Right" || texture_location=="Back + Sides" || texture_location=="All Sides"; function right_tex() = texture_location=="Right" || texture_location=="Left + Right" || texture_location=="Back + Sides" || texture_location=="All Sides"; // ======================================================================= // MAIN OUTPUT // ======================================================================= if(part=="All") { color(get_color(box_color)) final_body(); color(get_color(divider_color)) dividers(); if(font_style!="None") color(get_color(name_color)) name_object(); if(symbol_type!="None") color(get_color(symbol_color)) symbol_object(); if( texture_active() && texture_type=="Raised" ) color(get_color(texture_color)) raised_texture(); if( texture_active() && texture_type=="Flush" ) color(get_color(texture_color)) flush_texture_fill(); } else if(part=="Body Only") { color(get_color(box_color)) final_body(); } else if(part=="Organizer Only") { color(get_color(box_color)) final_body(); color(get_color(divider_color)) dividers(); } else if(part=="Dividers Only") { color(get_color(divider_color)) dividers(); } else if(part=="Name Only") { if(font_style!="None") color(get_color(name_color)) name_object(); } else if(part=="Symbol Only") { if(symbol_type!="None") color(get_color(symbol_color)) symbol_object(); } else if(part=="Decoration Only") { if(font_style!="None") color(get_color(name_color)) name_object(); if(symbol_type!="None") color(get_color(symbol_color)) symbol_object(); } else if(part=="Texture Only") { if(texture_active()) color(get_color(texture_color)) texture_export_object(); } // ======================================================================= // FINAL BODY // ======================================================================= module final_body() { difference() { union() { organizer_shell(); if(front_plate_style=="Simple") front_plate(); } if( texture_active() && texture_type=="Engraved" ) engraved_texture(); // In Flush mode a shallow pocket is cut into the organizer wall. // The colored texture insert is then placed into this pocket so its // outer surface is exactly level with the organizer. if( texture_active() && texture_type=="Flush" ) flush_texture_pocket(); } } // ======================================================================= // ORGANIZER SHELL // ======================================================================= // Inner cavity helpers for independently adjustable walls. function inner_width_value() = max(width-left_wall-right_wall, 0.5); function inner_depth_value() = max(depth-front_wall-back_wall, 0.5); function inner_center_x() = (left_wall-right_wall)/2; function inner_center_y() = (front_wall-back_wall)/2; function minimum_side_wall() = min(front_wall, back_wall, left_wall, right_wall); // Corner radius clamped so it can never exceed half the smallest footprint // dimension (raw slider allows values that turn the box into a stadium). function corner_radius_eff() = max(min(corner_radius, width/2 - 1, depth/2 - 1), 0.5); function inner_corner_radius() = max( min( corner_radius_eff()-minimum_side_wall(), inner_width_value()/2-0.25, inner_depth_value()/2-0.25 ), 0.25 ); module organizer_shell() { difference() { outer_shell_solid(); // -0.02 so the cut face is not coplanar with the floor top // (coplanar difference -> preview z-fighting / non-manifold sliver). translate([ inner_center_x(), inner_center_y(), bottom - 0.02 ]) rounded_box( inner_width_value(), inner_depth_value(), height+5, inner_corner_radius() ); } } // 2D footprint of the outer shell (rounded vertical corners). module base_footprint(cr) { offset(r=cr) square([max(width - 2*cr, 0.5), max(depth - 2*cr, 0.5)], center=true); } // Outer solid: rounded vertical corners (corner_radius_eff) plus a true // quarter-round FILLET on the bottom outer edge (radius base_fillet). // The fillet band z in [0, f] is a stack of thin hull()ed slabs whose // footprint is inset by f - sqrt(f^2 - (f-z)^2) -> a real circular arc. module outer_shell_solid() { cr = corner_radius_eff(); f = max(0, min(base_fillet, cr - 0.5, height/2 - 0.5, min(width, depth)/2 - cr - 0.5)); if(f <= 0.05) { rounded_box(width, depth, height, cr); } else { // straight body above the fillet translate([0, 0, f]) linear_extrude(height = height - f) base_footprint(cr); // quarter-round fillet band steps = max(ceil(f * 4), 6); for(i = [0 : steps - 1]) { z0 = f * i / steps; z1 = f * (i + 1) / steps; in0 = f - sqrt(max(f*f - (f - z0)*(f - z0), 0)); in1 = f - sqrt(max(f*f - (f - z1)*(f - z1), 0)); hull() { translate([0, 0, z0]) linear_extrude(0.02) offset(r = -in0) base_footprint(cr); translate([0, 0, z1]) linear_extrude(0.02) offset(r = -in1) base_footprint(cr); } } } } // ======================================================================= // ROUNDED BOX // ======================================================================= module rounded_box(w,d,h,r) { linear_extrude(height=h) offset(r=r) square( [ max(w-2*r,0.5), max(d-2*r,0.5) ], center=true ); } // ======================================================================= // FRONT PLATE // ======================================================================= module front_plate() { pw = width -2*front_plate_margin; ph = height -2*front_plate_margin; translate([ 0, -depth/2+0.03, height/2 ]) rotate([90,0,0]) linear_extrude( height=front_plate_depth ) offset(r=3) square( [ max(pw-6,1), max(ph-6,1) ], center=true ); } // ======================================================================= // DIVIDERS // ======================================================================= module dividers() { iw = inner_width_value() +2*divider_overlap; id = inner_depth_value() +2*divider_overlap; dh = min( divider_height, height-bottom-minimum_side_wall() ); // All divider layouts are centered inside the asymmetric inner cavity. translate([ inner_center_x(), inner_center_y(), 0 ]) { if(compartment_layout=="None") {} else if(compartment_layout=="2 Vertical") grid_layout(2,1,iw,id,dh); else if(compartment_layout=="3 Vertical") grid_layout(3,1,iw,id,dh); else if(compartment_layout=="4 Vertical") grid_layout(4,1,iw,id,dh); else if(compartment_layout=="5 Vertical") grid_layout(5,1,iw,id,dh); else if(compartment_layout=="6 Vertical") grid_layout(6,1,iw,id,dh); else if(compartment_layout=="7 Vertical") grid_layout(7,1,iw,id,dh); else if(compartment_layout=="8 Vertical") grid_layout(8,1,iw,id,dh); else if(compartment_layout=="2 Horizontal") grid_layout(1,2,iw,id,dh); else if(compartment_layout=="3 Horizontal") grid_layout(1,3,iw,id,dh); else if(compartment_layout=="4 Horizontal") grid_layout(1,4,iw,id,dh); else if(compartment_layout=="5 Horizontal") grid_layout(1,5,iw,id,dh); else if(compartment_layout=="6 Horizontal") grid_layout(1,6,iw,id,dh); else if(compartment_layout=="2x2") grid_layout(2,2,iw,id,dh); else if(compartment_layout=="2x3") grid_layout(2,3,iw,id,dh); else if(compartment_layout=="2x4") grid_layout(2,4,iw,id,dh); else if(compartment_layout=="2x5") grid_layout(2,5,iw,id,dh); else if(compartment_layout=="3x2") grid_layout(3,2,iw,id,dh); else if(compartment_layout=="3x3") grid_layout(3,3,iw,id,dh); else if(compartment_layout=="3x4") grid_layout(3,4,iw,id,dh); else if(compartment_layout=="3x5") grid_layout(3,5,iw,id,dh); else if(compartment_layout=="4x2") grid_layout(4,2,iw,id,dh); else if(compartment_layout=="4x3") grid_layout(4,3,iw,id,dh); else if(compartment_layout=="4x4") grid_layout(4,4,iw,id,dh); else if(compartment_layout=="5x2") grid_layout(5,2,iw,id,dh); else if(compartment_layout=="5x3") grid_layout(5,3,iw,id,dh); else if(compartment_layout=="Large Left + 2") large_side_layout(true,2,iw,id,dh); else if(compartment_layout=="Large Left + 3") large_side_layout(true,3,iw,id,dh); else if(compartment_layout=="Large Left + 4") large_side_layout(true,4,iw,id,dh); else if(compartment_layout=="Large Right + 2") large_side_layout(false,2,iw,id,dh); else if(compartment_layout=="Large Right + 3") large_side_layout(false,3,iw,id,dh); else if(compartment_layout=="Large Right + 4") large_side_layout(false,4,iw,id,dh); else if(compartment_layout=="Front 2 + Back Large") front_small_layout(2,iw,id,dh); else if(compartment_layout=="Front 3 + Back Large") front_small_layout(3,iw,id,dh); else if(compartment_layout=="Front 4 + Back Large") front_small_layout(4,iw,id,dh); else if(compartment_layout=="Back 2 + Front Large") back_small_layout(2,iw,id,dh); else if(compartment_layout=="Back 3 + Front Large") back_small_layout(3,iw,id,dh); else if(compartment_layout=="Back 4 + Front Large") back_small_layout(4,iw,id,dh); else if(compartment_layout=="Custom Grid") grid_layout( custom_columns, custom_rows, iw, id, dh ); } } // ======================================================================= // REGULAR GRID // ======================================================================= module grid_layout( cols, rows, iw, id, h ) { if(cols>1) for(i=[1:cols-1]) { x = -iw/2 +iw*i/cols; translate([ x-divider_thickness/2, -id/2, bottom ]) cube([ divider_thickness, id, h ]); } if(rows>1) for(j=[1:rows-1]) { y = -id/2 +id*j/rows; translate([ -iw/2, y-divider_thickness/2, bottom ]) cube([ iw, divider_thickness, h ]); } } // ======================================================================= // LARGE SIDE // ======================================================================= module large_side_layout( large_left, count, iw, id, h ) { lw = iw*large_ratio; sw = iw-lw; split = large_left ? -iw/2+lw : iw/2-lw; translate([ split-divider_thickness/2, -id/2, bottom ]) cube([ divider_thickness, id, h ]); for(j=[1:count-1]) { y = -id/2 +id*j/count; if(large_left) translate([ split, y-divider_thickness/2, bottom ]) cube([ sw, divider_thickness, h ]); else translate([ -iw/2, y-divider_thickness/2, bottom ]) cube([ sw, divider_thickness, h ]); } } // ======================================================================= // FRONT SMALL // ======================================================================= module front_small_layout( count, iw, id, h ) { fd = id*(1-large_ratio); sy = -id/2+fd; translate([ -iw/2, sy-divider_thickness/2, bottom ]) cube([ iw, divider_thickness, h ]); for(i=[1:count-1]) { x = -iw/2 +iw*i/count; translate([ x-divider_thickness/2, -id/2, bottom ]) cube([ divider_thickness, fd, h ]); } } // ======================================================================= // BACK SMALL // ======================================================================= module back_small_layout( count, iw, id, h ) { bd = id*(1-large_ratio); sy = id/2-bd; translate([ -iw/2, sy-divider_thickness/2, bottom ]) cube([ iw, divider_thickness, h ]); for(i=[1:count-1]) { x = -iw/2 +iw*i/count; translate([ x-divider_thickness/2, sy, bottom ]) cube([ divider_thickness, bd, h ]); } } // ======================================================================= // NAME (1: roll in plane -- 2/3: plate offset + clamp -- 7: 0.6 mm bite) // ======================================================================= module name_object() { // Inner None-guard removed (10): every caller already checks font_style. translate([ name_pos_x(), -depth/2+0.6-front_face_offset(), name_pos_z() ]) rotate([90,0,0]) linear_extrude( height=text_depth ) rotate(text_rotation) multmatrix([ [1,tan(script_shear),0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ]) scale([ name_fit_scale() * ( is_script_font() ? script_width_scale : 1 ), is_script_font() ? script_height_scale : 1 ]) text( name_text, size=text_size, font=selected_font(), spacing=letter_spacing, halign="center", valign="center" ); } // ======================================================================= // SYMBOL (1: roll in plane -- 2/3: plate offset + clamp -- 7: 0.6 mm bite // -- 8: every symbol normalised to symbol_size mm tall) // ======================================================================= module symbol_object() { // Inner None-guard removed (10): every caller already checks symbol_type. translate([ symbol_pos_x(), -depth/2+0.6-front_face_offset(), symbol_pos_z() ]) rotate([90,0,0]) linear_extrude( height=symbol_depth ) rotate(symbol_rotation) // (8) resize() forces a consistent height (aspect kept) // instead of scale(symbol_size/20), which gave wildly // different real sizes because the source symbols are // drawn at anything from ~11 to ~24 units tall. resize([0, symbol_size, 0], auto = true) selected_symbol(); } // ======================================================================= // SYMBOL SELECTOR // ======================================================================= module selected_symbol() { if(symbol_type=="Heart") heart_symbol(); else if(symbol_type=="Double Heart") double_heart_symbol(); else if(symbol_type=="Heart Outline") heart_outline_symbol(); else if(symbol_type=="Star") star_symbol(); else if(symbol_type=="Star Outline") star_outline_symbol(); else if(symbol_type=="Flower") flower_symbol(); else if(symbol_type=="Daisy") daisy_symbol(); else if(symbol_type=="Rose") rose_symbol(); else if(symbol_type=="Paw") paw_symbol(); else if(symbol_type=="Cat Paw") cat_paw_symbol(); else if(symbol_type=="Crown") crown_symbol(); else if(symbol_type=="Princess Crown") princess_crown_symbol(); else if(symbol_type=="Diamond") diamond_symbol(); else if(symbol_type=="Moon") moon_symbol(); else if(symbol_type=="Crescent Moon") crescent_moon_symbol(); // (9) distinct now else if(symbol_type=="Sun") sun_symbol(); else if(symbol_type=="Lightning") lightning_symbol(); else if(symbol_type=="Butterfly") butterfly_symbol(); else if(symbol_type=="Clover") clover_symbol(); else if(symbol_type=="Sparkle") sparkle_symbol(); else if(symbol_type=="Sparkles") sparkles_symbol(); else if(symbol_type=="Cloud") cloud_symbol(); else if(symbol_type=="Rainbow") rainbow_symbol(); else if(symbol_type=="Leaf") leaf_symbol(); else if(symbol_type=="Monstera") monstera_symbol(); else if(symbol_type=="Tree") tree_symbol(); else if(symbol_type=="Cat") cat_symbol(); else if(symbol_type=="Cat Face") cat_face_symbol(); else if(symbol_type=="Dog Bone") bone_symbol(); else if(symbol_type=="Dog Face") dog_face_symbol(); else if(symbol_type=="Fish") fish_symbol(); else if(symbol_type=="Dolphin") dolphin_symbol(); else if(symbol_type=="Music Note") music_symbol(); else if(symbol_type=="Music Notes") music_notes_symbol(); else if(symbol_type=="House") house_symbol(); else if(symbol_type=="Gift") gift_symbol(); else if(symbol_type=="Balloon") balloon_symbol(); else if(symbol_type=="Apple") apple_symbol(); else if(symbol_type=="Cherry") cherry_symbol(); else if(symbol_type=="Strawberry") strawberry_symbol(); else if(symbol_type=="Coffee Cup") coffee_symbol(); else if(symbol_type=="Cupcake") cupcake_symbol(); else if(symbol_type=="Ice Cream") icecream_symbol(); else if(symbol_type=="Camera") camera_symbol(); else if(symbol_type=="Gamepad") gamepad_symbol(); else if(symbol_type=="Football") football_symbol(); else if(symbol_type=="Basketball") basketball_symbol(); else if(symbol_type=="Soccer Ball") soccer_symbol(); else if(symbol_type=="Car") car_symbol(); else if(symbol_type=="Truck") truck_symbol(); else if(symbol_type=="Tractor") tractor_symbol(); else if(symbol_type=="Rocket") rocket_symbol(); else if(symbol_type=="Planet") planet_symbol(); else if(symbol_type=="Unicorn") unicorn_symbol(); else if(symbol_type=="Ghost") ghost_symbol(); else if(symbol_type=="Skull") skull_symbol(); else if(symbol_type=="Smile") smile_symbol(); else if(symbol_type=="Peace") peace_symbol(); else if(symbol_type=="Infinity") infinity_symbol(); else if(symbol_type=="Key") key_symbol(); else if(symbol_type=="Anchor") anchor_symbol(); else if(symbol_type=="Bell") bell_symbol(); else if(symbol_type=="Book") book_symbol(); else if(symbol_type=="Pencil") pencil_symbol(); else if(symbol_type=="Scissors") scissors_symbol(); else if(symbol_type=="Gear") gear_symbol(); else if(symbol_type=="Wrench") wrench_symbol(); else if(symbol_type=="Hammer") hammer_symbol(); else if(symbol_type=="Plus") plus_symbol(); else if(symbol_type=="Cross") cross_symbol(); else if(symbol_type=="Hexagon") circle(r=9,$fn=6); else if(symbol_type=="Circle") circle(r=9); else if(symbol_type=="Triangle") polygon([[-9,-7],[9,-7],[0,10]]); } // ======================================================================= // EXTRA SYMBOLS // ======================================================================= module double_heart_symbol() { scale([0.78,0.78]) translate([-4,1]) heart_symbol(); scale([0.62,0.62]) translate([7,-4]) heart_symbol(); } module heart_outline_symbol() { difference() { heart_symbol(); scale([0.72,0.72]) heart_symbol(); } } module star_outline_symbol() { difference() { star_symbol(); scale([0.66,0.66]) star_symbol(); } } module daisy_symbol() { circle(r=2.6); for(a=[0:45:315]) rotate(a) translate([0,6.2]) scale([0.7,1.35]) circle(r=3.2); } module rose_symbol() { union() { for(a=[0:60:300]) rotate(a) translate([0,3.2]) scale([0.6,1.15]) circle(r=4.2); circle(r=3.3); } } // (9) Cat paw: a scalloped THREE-LOBE main pad + four teardrop toes. // The dog paw_symbol() is a plain ellipse pad + four round dots - this // trilobed, notched pad reads unmistakably as a different icon. module cat_paw_symbol() { union() { // three-lobe pad (bumps along the bottom edge) hull() { translate([-3.6,-3.2]) circle(r=3.3); translate([ 3.6,-3.2]) circle(r=3.3); translate([0,-0.6]) circle(r=4.3); } translate([-4.0,-6.4]) circle(r=2.7); translate([ 0.0,-7.3]) circle(r=2.9); translate([ 4.0,-6.4]) circle(r=2.7); // four teardrop toes fanned in an arc, pointing outward for(a=[-45:30:45]) rotate(a) translate([0,8.6]) scale([0.8,1.45]) circle(r=2.5); } } // (9) Crescent + star - the classic icon, and nothing like the plain // fat crescent of moon_symbol(). module crescent_moon_symbol() { union() { difference() { circle(r=10); translate([5.4,1.1]) circle(r=9.2); // thin sickle } translate([7.4,-1.4]) scale(0.42) star_symbol(); // star in the mouth } } module princess_crown_symbol() { union() { polygon([[-10,-6],[-9,5],[-5,1],[-2,8],[0,2],[3,9],[6,1],[10,5],[9,-6]]); translate([0,-6]) square([19,3],center=true); } } module sparkles_symbol() { sparkle_symbol(); translate([8,6]) scale(0.45) sparkle_symbol(); translate([-8,-6]) scale(0.35) sparkle_symbol(); } module rainbow_symbol() { difference() { intersection() { circle(r=10); translate([0,0]) square([22,11],center=true); } intersection() { circle(r=6.5); translate([0,0]) square([16,11],center=true); } } } module monstera_symbol() { difference() { scale([0.78,1.05]) circle(r=9); translate([-5,4]) rotate(-25) square([8,1.5],center=true); translate([5,4]) rotate(25) square([8,1.5],center=true); translate([-6,-1]) rotate(-15) square([7,1.5],center=true); translate([6,-1]) rotate(15) square([7,1.5],center=true); } translate([-0.9,-12]) square([1.8,10]); } module cat_face_symbol() { union() { circle(r=7.5); polygon([[-7,4],[-7,10],[-2,7]]); polygon([[7,4],[7,10],[2,7]]); } } module dog_face_symbol() { union() { circle(r=7); translate([-7,2]) rotate(20) scale([0.7,1.3]) circle(r=4); translate([7,2]) rotate(-20) scale([0.7,1.3]) circle(r=4); } } module dolphin_symbol() { union() { rotate(-12) scale([1.35,0.55]) circle(r=6); polygon([[-7,0],[-12,5],[-10,0],[-12,-5]]); translate([4,3]) rotate(25) polygon([[0,0],[5,4],[3,-1]]); } } module music_notes_symbol() { music_symbol(); translate([-7,3]) scale(0.7) music_symbol(); } module strawberry_symbol() { union() { scale([0.85,1.15]) circle(r=7); translate([0,7]) polygon([[-5,0],[0,5],[5,0],[2,-1],[0,2],[-2,-1]]); } } module coffee_symbol() { difference() { union() { translate([-2,-1]) square([13,11],center=true); translate([6,0]) circle(r=5); } translate([6,0]) circle(r=2.7); } translate([-2,-7]) square([16,2],center=true); } module cupcake_symbol() { union() { polygon([[-7,-7],[7,-7],[5,2],[-5,2]]); translate([0,3]) circle(r=6); translate([-4,4]) circle(r=4); translate([4,4]) circle(r=4); } } module icecream_symbol() { union() { translate([0,4]) circle(r=6); polygon([[-5,0],[5,0],[0,-11]]); } } module camera_symbol() { union() { square([18,12],center=true); translate([-5,7]) square([6,3],center=true); } difference() { translate([2,0]) circle(r=4); translate([2,0]) circle(r=2); } } module gamepad_symbol() { hull() { translate([-6,0]) circle(r=5); translate([6,0]) circle(r=5); } translate([-5,0]) { square([2,7],center=true); square([7,2],center=true); } translate([5,2]) circle(r=1.4); translate([7,-1]) circle(r=1.4); } module football_symbol() { difference() { scale([1.5,0.75]) circle(r=7); translate([0,0]) square([1.2,8],center=true); } } module basketball_symbol() { difference() { circle(r=9); square([1,19],center=true); square([19,1],center=true); rotate(45) scale([1,0.45]) circle(r=9); rotate(-45) scale([1,0.45]) circle(r=9); } } module soccer_symbol() { union() { circle(r=9); circle(r=3,$fn=5); } } module car_symbol() { union() { translate([0,-1]) square([18,7],center=true); polygon([[-6,2],[-3,7],[5,7],[8,2]]); translate([-6,-5]) circle(r=2.5); translate([6,-5]) circle(r=2.5); } } module truck_symbol() { union() { translate([-3,0]) square([12,8],center=true); translate([6,-1]) square([7,6],center=true); translate([-5,-5]) circle(r=2.3); translate([5,-5]) circle(r=2.3); } } module tractor_symbol() { union() { square([11,6],center=true); translate([3,5]) square([5,5],center=true); translate([-5,-5]) circle(r=4); translate([6,-5]) circle(r=2.5); } } module rocket_symbol() { union() { scale([0.6,1.2]) circle(r=6); translate([0,8]) polygon([[-4,0],[4,0],[0,7]]); translate([-4,-5]) polygon([[0,0],[-4,-5],[1,-4]]); translate([4,-5]) polygon([[0,0],[4,-5],[-1,-4]]); } } module planet_symbol() { union() { circle(r=6); difference() { scale([1.7,0.45]) circle(r=8); circle(r=6.5); } } } module unicorn_symbol() { union() { circle(r=6.5); translate([0,9]) polygon([[-2,0],[2,0],[0,8]]); translate([-5,5]) polygon([[-3,0],[0,5],[2,0]]); translate([5,5]) polygon([[3,0],[0,5],[-2,0]]); } } module ghost_symbol() { difference() { union() { translate([0,2]) circle(r=7); translate([0,-4]) square([14,10],center=true); translate([-5,-9]) circle(r=2.5); translate([0,-9]) circle(r=2.5); translate([5,-9]) circle(r=2.5); } translate([-2.5,3]) circle(r=1.2); translate([2.5,3]) circle(r=1.2); } } module skull_symbol() { difference() { union() { translate([0,2]) circle(r=7); translate([0,-5]) square([9,7],center=true); } translate([-2.5,3]) circle(r=2); translate([2.5,3]) circle(r=2); translate([0,-1]) polygon([[-1.5,0],[1.5,0],[0,-3]]); } } module smile_symbol() { difference() { circle(r=9); translate([-3,3]) circle(r=1.3); translate([3,3]) circle(r=1.3); translate([0,-1]) difference() { circle(r=5); translate([0,2]) square([12,8],center=true); } } } module peace_symbol() { difference() { circle(r=9); circle(r=7); } square([1.7,16],center=true); translate([0,-3]) rotate(45) square([1.7,10],center=true); translate([0,-3]) rotate(-45) square([1.7,10],center=true); } module infinity_symbol() { difference() { union() { translate([-5,0]) scale([1.2,0.75]) circle(r=5); translate([5,0]) scale([1.2,0.75]) circle(r=5); } union() { translate([-5,0]) scale([1.2,0.75]) circle(r=2.6); translate([5,0]) scale([1.2,0.75]) circle(r=2.6); } } } module key_symbol() { difference() { translate([-5,4]) circle(r=5); translate([-5,4]) circle(r=2.5); } translate([2,0]) rotate(-35) square([13,3],center=true); translate([7,-4]) square([3,5],center=true); } module anchor_symbol() { union() { difference() { translate([0,7]) circle(r=4); translate([0,7]) circle(r=2); } square([2.5,17],center=true); translate([0,-6]) rotate(45) square([2.5,10],center=true); translate([0,-6]) rotate(-45) square([2.5,10],center=true); translate([0,-10]) square([15,2.5],center=true); } } module bell_symbol() { union() { translate([0,1]) scale([0.9,1.1]) circle(r=6); translate([0,-5]) square([14,3],center=true); translate([0,-8]) circle(r=2); } } module book_symbol() { union() { translate([-4,0]) polygon([[-5,-7],[0,-5],[0,7],[-5,6]]); translate([4,0]) polygon([[5,-7],[0,-5],[0,7],[5,6]]); } } module pencil_symbol() { rotate(-35) union() { square([4,16],center=true); translate([0,9]) polygon([[-2,0],[2,0],[0,5]]); } } module scissors_symbol() { difference() { union() { translate([-4,-5]) circle(r=4); translate([4,-5]) circle(r=4); } translate([-4,-5]) circle(r=2); translate([4,-5]) circle(r=2); } translate([-1,2]) rotate(25) square([2,13],center=true); translate([1,2]) rotate(-25) square([2,13],center=true); } module gear_symbol() { difference() { union() { circle(r=7); for(a=[0:45:315]) rotate(a) translate([0,8]) square([3,4],center=true); } circle(r=3); } } module wrench_symbol() { union() { rotate(-30) square([4,15],center=true); translate([-4,6]) difference() { circle(r=5); translate([2,2]) square([7,7],center=true); } } } module hammer_symbol() { rotate(-30) { square([3,17],center=true); translate([0,8]) square([12,5],center=true); } } // ======================================================================= // HEART (patch 4: no duplicate seam vertex at a=360) // ======================================================================= module heart_symbol() { polygon( points=[ for(a=[0:3:357]) [ 0.70 *16 *pow(sin(a),3), 0.70*( 13*cos(a) -5*cos(2*a) -2*cos(3*a) -cos(4*a) ) ] ] ); } // ======================================================================= // STAR // ======================================================================= module star_symbol() { polygon([ [0,10], [2.35,3.25], [9.5,3.1], [3.8,-1.2], [5.9,-8], [0,-4], [-5.9,-8], [-3.8,-1.2], [-9.5,3.1], [-2.35,3.25] ]); } // ======================================================================= // FLOWER // ======================================================================= module flower_symbol() { union() { circle(r=3); for(a=[0:45:315]) rotate(a) translate([0,6.2]) scale([0.85,1.15]) circle(r=3.5); } } // ======================================================================= // PAW // ======================================================================= module paw_symbol() { union() { translate([0,-3]) scale([1.3,1]) circle(r=5); translate([-6.2,4]) circle(r=2.6); translate([-2.1,7]) circle(r=2.6); translate([2.1,7]) circle(r=2.6); translate([6.2,4]) circle(r=2.6); } } // ======================================================================= // CROWN // ======================================================================= module crown_symbol() { polygon([ [-10,-7], [-9,6], [-4,1], [0,9], [4,1], [9,6], [10,-7] ]); } // ======================================================================= // DIAMOND // ======================================================================= module diamond_symbol() { polygon([ [0,10], [9,0], [0,-10], [-9,0] ]); } // ======================================================================= // MOON // ======================================================================= module moon_symbol() { difference() { circle(r=10); translate([4,3]) circle(r=9); } } // ======================================================================= // SUN // ======================================================================= module sun_symbol() { union() { circle(r=5); for(a=[0:30:330]) rotate(a) translate([0,8]) square( [2,5], center=true ); } } // ======================================================================= // LIGHTNING // ======================================================================= module lightning_symbol() { polygon([ [2,11], [-7,0], [-1,0], [-5,-11], [8,2], [2,2] ]); } // ======================================================================= // BUTTERFLY // ======================================================================= module butterfly_symbol() { union() { translate([-5,4]) scale([1.15,1.4]) circle(r=4); translate([5,4]) scale([1.15,1.4]) circle(r=4); translate([-4,-4]) circle(r=3); translate([4,-4]) circle(r=3); scale([0.35,1.4]) circle(r=3.5); } } // ======================================================================= // CLOVER // ======================================================================= module clover_symbol() { union() { translate([-4,4]) circle(r=4.5); translate([4,4]) circle(r=4.5); translate([-4,-4]) circle(r=4.5); translate([4,-4]) circle(r=4.5); } } // ======================================================================= // SPARKLE // ======================================================================= module sparkle_symbol() { polygon([ [0,11], [2,3], [9,0], [2,-3], [0,-11], [-2,-3], [-9,0], [-2,3] ]); } // ======================================================================= // CLOUD // ======================================================================= module cloud_symbol() { union() { translate([-5,0]) circle(r=5); translate([0,4]) circle(r=6); translate([6,0]) circle(r=5); translate([0,-2]) square( [14,6], center=true ); } } // ======================================================================= // LEAF // ======================================================================= module leaf_symbol() { intersection() { translate([-4,0]) circle(r=9); translate([4,0]) circle(r=9); } } // ======================================================================= // TREE // ======================================================================= module tree_symbol() { union() { translate([0,4]) circle(r=6); translate([-4,1]) circle(r=5); translate([4,1]) circle(r=5); translate([-1.5,-10]) square([3,10]); } } // ======================================================================= // CAT // ======================================================================= module cat_symbol() { union() { circle(r=8); polygon([ [-8,4], [-8,11], [-2,7] ]); polygon([ [8,4], [8,11], [2,7] ]); } } // ======================================================================= // DOG BONE // ======================================================================= module bone_symbol() { union() { rotate(45) square( [15,5], center=true ); translate([-6,6]) circle(r=4); translate([-9,3]) circle(r=4); translate([6,-6]) circle(r=4); translate([9,-3]) circle(r=4); } } // ======================================================================= // FISH // ======================================================================= module fish_symbol() { union() { scale([1.3,0.8]) circle(r=6); polygon([ [-7,0], [-13,6], [-13,-6] ]); } } // ======================================================================= // MUSIC NOTE // ======================================================================= module music_symbol() { union() { translate([-3,-6]) circle(r=3.5); translate([4,-4]) circle(r=3.5); translate([-0.5,0]) square([2.5,12]); translate([6.5,2]) square([2.5,12]); } } // ======================================================================= // HOUSE // ======================================================================= module house_symbol() { union() { square( [14,11], center=true ); translate([0,5]) polygon([ [-9,0], [9,0], [0,9] ]); } } // ======================================================================= // GIFT // ======================================================================= module gift_symbol() { union() { square( [16,13], center=true ); translate([0,7]) square( [18,3], center=true ); square( [3,14], center=true ); } } // ======================================================================= // BALLOON // ======================================================================= module balloon_symbol() { union() { translate([0,3]) scale([0.85,1.15]) circle(r=7); polygon([ [-2,-4], [2,-4], [0,-8] ]); } } // ======================================================================= // APPLE // ======================================================================= module apple_symbol() { union() { translate([-3,0]) circle(r=6); translate([3,0]) circle(r=6); translate([0,5]) rotate(-25) square([2,6]); } } // ======================================================================= // CHERRY // ======================================================================= module cherry_symbol() { union() { translate([-5,-4]) circle(r=4.5); translate([5,-4]) circle(r=4.5); hull() { translate([-5,0]) circle(r=0.7); translate([0,9]) circle(r=0.7); } hull() { translate([5,0]) circle(r=0.7); translate([0,9]) circle(r=0.7); } } } // ======================================================================= // PLUS // ======================================================================= module plus_symbol() { union() { square( [5,18], center=true ); square( [18,5], center=true ); } } // ======================================================================= // CROSS // ======================================================================= module cross_symbol() { union() { square( [5,20], center=true ); translate([0,3]) square( [15,5], center=true ); } } // ======================================================================= // TEXTURE DIMENSIONS // ======================================================================= function texture_front_width() = max( width -2*corner_radius_eff() -2*texture_corner_margin -2*texture_side_margin, 10 ); function texture_side_width() = max( depth -2*corner_radius_eff() -2*texture_corner_margin -2*texture_side_margin, 10 ); function texture_height_value() = max( height -texture_bottom_margin -texture_top_margin, 10 ); // ======================================================================= // FLUSH TEXTURE HELPERS // ======================================================================= // Return the thickness of the selected outer wall. function wall_for_face(face) = face=="front" ? front_wall : face=="back" ? back_wall : face=="left" ? left_wall : face=="right" ? right_wall : minimum_side_wall(); // Keep a flush insert safely inside the thickness of the selected wall. function texture_flush_depth_value(face) = min( texture_flush_depth, max(wall_for_face(face)-0.20,0.20) ); // Export the correct texture object for the selected mode. module texture_export_object() { if(texture_type=="Flush") flush_texture_fill(); else if(texture_type=="Raised") raised_texture(); else if(texture_type=="Engraved") engraved_texture(); } // Colored insert used by Flush mode. module flush_texture_fill() { if(front_tex() && !keep_front_clean) texture_face_flush("front",false); if(back_tex()) texture_face_flush("back",false); if(left_tex()) texture_face_flush("left",false); if(right_tex()) texture_face_flush("right",false); } // Pocket removed from the organizer body. // A tiny overshoot prevents coplanar CSG artifacts. module flush_texture_pocket() { if(front_tex() && !keep_front_clean) texture_face_flush("front",true); if(back_tex()) texture_face_flush("back",true); if(left_tex()) texture_face_flush("left",true); if(right_tex()) texture_face_flush("right",true); } // Places a texture so that its outside face follows the flat wall surface. // In normal use texture_flush_relief = 0 gives a perfectly flush result. module texture_face_flush( face, pocket=false ) { fw = ( face=="front" || face=="back" ) ? texture_front_width() : texture_side_width(); fh = texture_height_value(); zc = texture_bottom_margin +fh/2; fd = texture_flush_depth_value(face); eps = pocket ? 0.04 : 0; base_inset = fd+eps; total_depth = fd +texture_flush_relief +2*eps; if(face=="front") translate([ 0, -depth/2+base_inset, zc ]) rotate([90,0,0]) texture_panel( fw, fh, total_depth, false ); else if(face=="back") translate([ 0, depth/2-base_inset, zc ]) rotate([-90,0,0]) texture_panel( fw, fh, total_depth, false ); else if(face=="right") translate([ width/2-base_inset, 0, zc ]) rotate([0,90,0]) rotate([0,0,90]) texture_panel( fw, fh, total_depth, false ); else if(face=="left") translate([ -width/2+base_inset, 0, zc ]) rotate([0,-90,0]) rotate([0,0,-90]) texture_panel( fw, fh, total_depth, false ); } // ======================================================================= // RAISED TEXTURE // ======================================================================= module raised_texture() { if(front_tex() && !keep_front_clean) texture_face("front",false); if(back_tex()) texture_face("back",false); if(left_tex()) texture_face("left",false); if(right_tex()) texture_face("right",false); } // ======================================================================= // ENGRAVED TEXTURE // ======================================================================= module engraved_texture() { if(front_tex() && !keep_front_clean) texture_face("front",true); if(back_tex()) texture_face("back",true); if(left_tex()) texture_face("left",true); if(right_tex()) texture_face("right",true); } // ======================================================================= // TEXTURE FACE // ======================================================================= module texture_face( face, engrave=false ) { fw = ( face=="front" || face=="back" ) ? texture_front_width() : texture_side_width(); fh = texture_height_value(); zc = texture_bottom_margin +fh/2; d = engrave ? min( texture_depth+0.25, max(wall_for_face(face)-0.10,0.10) ) : texture_depth; if(face=="front") translate([ 0, -depth/2 +(engrave ? d/2 : 0.03), zc ]) rotate([90,0,0]) texture_panel( fw, fh, d, engrave ); else if(face=="back") translate([ 0, depth/2 -(engrave ? d/2 : 0.03), zc ]) rotate([-90,0,0]) texture_panel( fw, fh, d, engrave ); else if(face=="right") translate([ width/2 -(engrave ? d/2 : 0.03), 0, zc ]) rotate([0,90,0]) rotate([0,0,90]) texture_panel( fw, fh, d, engrave ); else if(face=="left") translate([ -width/2 +(engrave ? d/2 : 0.03), 0, zc ]) rotate([0,-90,0]) rotate([0,0,-90]) texture_panel( fw, fh, d, engrave ); } // ======================================================================= // TEXTURE PANEL // ======================================================================= module texture_panel( w, h, d, engrave ) { linear_extrude( height=d, center=engrave ) intersection() { texture_mask( w, h ); selected_texture( w, h ); } } // ======================================================================= // TEXTURE MASK // ======================================================================= module texture_mask(w,h) { r = min( 4, min(w,h)/4 ); offset(r=r) square( [ max(w-2*r,1), max(h-2*r,1) ], center=true ); } // ======================================================================= // TEXTURE SELECTOR // ======================================================================= module selected_texture(w,h) { if(texture_style=="Honeycomb") tex_honeycomb(w,h); else if(texture_style=="Hexagons") tex_hexagons(w,h); else if(texture_style=="Dots") tex_dots(w,h); else if(texture_style=="Vertical Lines") tex_vertical(w,h); else if(texture_style=="Horizontal Lines") tex_horizontal(w,h); else if(texture_style=="Diagonal Lines") tex_diagonal(w,h); else if(texture_style=="Grid") { tex_vertical(w,h); tex_horizontal(w,h); } else if(texture_style=="Diamonds") tex_diamonds(w,h); else if(texture_style=="Stars") tex_stars(w,h); else if(texture_style=="Plus") tex_plus(w,h); else if(texture_style=="Triangles") tex_triangles(w,h); else if(texture_style=="Brick") tex_brick(w,h); } // ======================================================================= // HONEYCOMB // ======================================================================= module tex_honeycomb(w,h) { r = max( texture_size/2, 2.5 ); sx = r*1.75; sy = r*1.52; for( row=[ -ceil(h/sy)-2: ceil(h/sy)+2 ] ) for( col=[ -ceil(w/sx)-2: ceil(w/sx)+2 ] ) { xx = col*sx + ( abs(row)%2==0 ? 0 : sx/2 ); yy = row*sy; translate([xx,yy]) difference() { circle( r=r, $fn=6 ); circle( r=max( r-texture_line, 0.5 ), $fn=6 ); } } } // ======================================================================= // HEXAGONS // ======================================================================= module tex_hexagons(w,h) { sx = texture_size*1.15; sy = texture_size; for( row=[ -ceil(h/sy)-2: ceil(h/sy)+2 ] ) for( col=[ -ceil(w/sx)-2: ceil(w/sx)+2 ] ) { xx = col*sx + ( abs(row)%2==0 ? 0 : sx/2 ); translate([ xx, row*sy ]) circle( r=texture_size*0.32, $fn=6 ); } } // ======================================================================= // DOTS // ======================================================================= module tex_dots(w,h) { for( x=[ -w/2: texture_size: w/2 ] ) for( y=[ -h/2: texture_size: h/2 ] ) translate([x,y]) circle( r=max( texture_line, 0.6 ) ); } // ======================================================================= // VERTICAL // ======================================================================= module tex_vertical(w,h) { for( x=[ -w/2: texture_size: w/2 ] ) translate([x,0]) square( [ texture_line, h+6 ], center=true ); } // ======================================================================= // HORIZONTAL // ======================================================================= module tex_horizontal(w,h) { for( y=[ -h/2: texture_size: h/2 ] ) translate([0,y]) square( [ w+6, texture_line ], center=true ); } // ======================================================================= // DIAGONAL // ======================================================================= module tex_diagonal(w,h) { span = w+h; for( x=[ -span: texture_size: span ] ) translate([x,0]) rotate(45) square( [ texture_line, span*2 ], center=true ); } // ======================================================================= // DIAMONDS // ======================================================================= module tex_diamonds(w,h) { span = w+h; spacing = texture_size*1.45; for( x=[ -span: spacing: span ] ) { translate([x,0]) rotate(45) square( [ texture_line, span*2 ], center=true ); translate([x,0]) rotate(-45) square( [ texture_line, span*2 ], center=true ); } } // ======================================================================= // STARS // ======================================================================= module tex_stars(w,h) { spacing = texture_size*1.8; s = texture_size/25; for( x=[ -w/2+spacing/2: spacing: w/2-spacing/2 ] ) for( y=[ -h/2+spacing/2: spacing: h/2-spacing/2 ] ) translate([x,y]) scale(s) star_symbol(); } // ======================================================================= // PLUS // ======================================================================= module tex_plus(w,h) { spacing = texture_size*1.3; for( x=[ -w/2: spacing: w/2 ] ) for( y=[ -h/2: spacing: h/2 ] ) translate([x,y]) union() { square( [ texture_line, texture_size*0.45 ], center=true ); square( [ texture_size*0.45, texture_line ], center=true ); } } // ======================================================================= // TRIANGLES // ======================================================================= module tex_triangles(w,h) { spacing = texture_size*1.35; for( x=[ -w/2: spacing: w/2 ] ) for( y=[ -h/2: spacing: h/2 ] ) translate([x,y]) circle( r=texture_size*0.25, $fn=3 ); } // ======================================================================= // BRICK // ======================================================================= module tex_brick(w,h) { bw = texture_size*1.8; bh = texture_size; for( y=[ -h/2: bh: h/2 ] ) translate([0,y]) square( [ w+8, texture_line ], center=true ); for( row=[ -ceil(h/bh)-1: ceil(h/bh)+1 ] ) { yy = row*bh +bh/2; shift = abs(row)%2==0 ? 0 : bw/2; for( x=[ -w/2-bw: bw: w/2+bw ] ) translate([ x+shift, yy ]) square( [ texture_line, bh ], center=true ); } }