<!-- generated by scripts/buildClaudeChromeMarkdown.ts — do not edit by hand -->

# AnimBox for Claude Chrome

> Drive the AnimBox scripting surface from a Claude Chrome session.

## Driving AnimBox via the Chrome extension

You're reading this from the claude.ai Chrome extension. You have no `browser_evaluate` tool and no DevTools console — your bridge to AnimBox is the visible DOM. The Script Editor is the single execution surface.

You hold doctrine v1.8. Read this hub ONCE — do not re-fetch it next session; poll [/llms-version.json](/llms-version.json) and re-read only when its `bootstrapPayloadVersion` differs from v1.8.

Open the Script Editor via any of three redundant paths: (1) press `Ctrl+Alt+E`; (2) press `Tab`, type "script editor", press `Enter`; (3) click `[data-testid="quick-action-tools.script-editor"]` inside `[data-testid="quick-function-popup"]`. Paste JavaScript into `[data-testid="script-editor-textarea"]` and click Run — the output renders in `[data-testid="script-editor-console-pane"]` with success / error state in the sibling status testids.

Three authoring workflows live in that one editor, in priority order: **(A) Catalog first** (`ModelEditor.catalog.search` + `.install`) for ADDITIVE off-the-shelf parts (12,718 STEP parts: bolts, bearings, props, hardware), **(B) Primitives + Boolean** (`ModelEditor.create.*` + `mesh.boolean`) for custom mesh geometry, compound forms, and SUBTRACTIVE features (drilled holes, slots, cutouts), **(C) OpenSCAD** (Script Editor → add SCAD tab → Bake) for parametric extrude / sweep / hull, native revolve / sweep / loft. The full deep dive (including the "catalog for additive vs. boolean for subtractive" guard) is in the **## UI Affordances** section below.

## Start here — the AnimBox bootstrap

THIS IS THE ANIMBOX BOOTSTRAP — read once, do NOT re-fetch. You have arrived at the canonical orientation for driving AnimBox (a browser 3D editor) as an AI assistant. Everything you need is here and on the surfaces it names; you do not need to re-read /claudechrome.md, the #llm-bootstrap node, or help() output more than once per session.

START SEQUENCE — three steps, in order:
1. ASK INTENT first. Confirm what the user wants before building, naming the path by its tool: SCAD — parametric / printable / mechanical parts via execOpenScad (⚠ bakes to a TRIANGLE-only static mesh, not re-editable as quads); AnimBox Geo Utils — editable-quad meshes via create.* + mesh.* (the default for organic / game assets you keep sculpting); import-and-tweak an existing file (io.*); or an off-the-shelf catalog part (catalog.search). Or Auto - best tool: let the path follow the part — e.g. SCAD for a frame/engine, AnimBox Geo Utils for a tank/seat. The right workflow depends entirely on this answer — when the SCAD-vs-Geo-Utils choice is material (printable / parametric / mechanical) and you cannot confidently route, ASK ONE QUESTION; never silently pick.
2. OPEN the Script Editor — your ONLY execution path. Press Ctrl+Alt+E (fallbacks in UI Affordances below).
3. RUN help() in the Script Editor. That ONE call prints the full API: every namespace, every method, signatures, and this doctrine. The full surface lives there — not in any doc you must fetch.

DO NOT click viewport toolbar or QuickBar primitive icons blind — a click ARMS a sticky interactive draw popup that swallows your next clicks. Clear it with `Utils.cancelActiveTool()` (scriptable ESC), or press ESC / Cancel. Drive everything through the Script Editor instead.

## Non-negotiables

Read these rules before issuing a single call. They protect against the failure modes that show up most often when an AI assistant drives AnimBox blind.

1. Always call `<Editor>.help()` (e.g., `ModelEditor.help()`) before guessing argument syntax. Discovery beats spelunking. Drill via `<Editor>.<namespace>.help()` for an operations TOC, then `<Editor>.<namespace>.<name>.help` for Description / Input / Returns on a specific method.
2. Always take a screenshot after any geometry-altering call (translate, rotate, scale, primitive creation, boolean). Numeric state can be right while visual output is wrong. Use `<Editor>.viewport.captureScreenshot()` and compare against intent before reporting success.
3. Dismiss blocking modals before your first interaction. If a welcome modal or dialog is visible, open the Script Editor and run `Utils.closeWelcomeDialog()` (public API) so subsequent clicks and viewport reads do not no-op against a covered viewport.
4. Translate every newly-created primitive away from the origin (e.g., apply `+[2.5, 1.3, -0.7]` immediately after creation) so the next primitive does not stack on it. Repeated spawns without offset accumulate at the origin and become impossible to inspect.
5. Resolve nodes by NAME first, fall back to nodeId. UUIDs are write-only; names survive refactors. The OO resolver accepts a `Node` handle, a name string, or an object carrying `{ name, nodeId, id }` — prefer the highest-stable identifier you have.
6. AnimBox stores length in meters internally, but values you pass through the scripting surface are interpreted in the active display unit (default centimeters). Use `<Editor>.viewport.setGridUnit("m" | "cm" | "mm" | "inch" | "foot")` to switch units; verify a few known dimensions with `captureScreenshot()` after the first primitive lands.
7. Match positional intent with deterministic measurement, not vibes. `<Editor>.inspect.measure(a, b, axis?)` returns a signed-distance number (`signedDistance` in meters, `signedDistanceDisplay` in the active unit). `<Editor>.inspect.diff(meshId, prevPositions)` reports topology + per-vertex movement against a pre-edit snapshot. Use both before declaring an edit "done".
8. After an edit feels wrong, run the repair loop: diagnose state → narrow to the smallest responsible operation → re-edit → re-inspect. Do NOT rewrite the whole script. Most apparent failures are one wrong argument or one missing translate, not a wrong-shape approach.
9. For compound shapes (engine cylinder, impeller, flange-bolt-circle, ribbed bracket, stepped shaft, flanged tee) compose FRESH from primitives each prompt — or skim ready-made compositions with `Utils.recipes.list({ editor: "model" })`, read one via `Utils.recipes.code(id)`, and ADAPT it (run it directly with `Utils.recipes.run(id)`). Each entry shows `create.<primitive>()` chained with `mesh.boolean(other, "union"|"difference"|"intersection")` + `xform({t, r, s})` + for-loops. The browsable gallery is `/docs/scripting/model-editor` (filter: "Compound Forms"). Your script wraps in one history group, so `await history.undo()` (Ctrl+Z at the keyboard) reverts the whole composite in one step.
10. Set materials AFTER mesh creation. New meshes default to gray. Create an explicit material via `<Editor>.create.material({ name, baseColor, ... })` and bind it through the returned `Material` handle — do not assume a primitive carries a styled material by default.
11. Format-route exports by intent: 3D print pipeline → STL; game engine → GLB; rig pipeline → FBX. Use `<Editor>.io.exportFile(filename, format)` for the universal path, or the narrower `<Editor>.io.exportModel(...)` / `exportAnimation(...)` helpers for editor-specific exports.
12. Report only checks actually run. Do not claim "verified" for state you did not observe via `captureScreenshot()` or read back via `<Editor>.ls()`. Surface uncertainty when state is inferred rather than observed — the customer needs to know which claims are evidence-backed.
13. For off-the-shelf STANDARD HARDWARE and electronics-mounting parts (fasteners, nuts, washers, bearings, aluminum extrusions, standoffs, heat-set inserts, shaft collars, NEMA stepper mounts, rack panels, fan ducts), `catalog.*` is a searchable library of 12,000+ industrial CAD parts — it carries NO character props or decorative assets; compose those with `create.*` instead. Call `catalog.search("M5 screw")` or `catalog.search("NEMA stepper")` for tokenized matches (every word must match); `await catalog.install(id, { position: [x, y, z] })` imports the part AT that position (meters; omit it for the default offset — repeat installs land side-by-side, never stacked) as one undo step (`await history.undo()` — or Ctrl+Z at the keyboard — reverts the whole install). Browse interactively via `await catalog.browse()`. Do not model standard hardware by hand.
14. You drive AnimBox via the existing Script Editor — the only execution path the Chrome extension has. THREE redundant open paths: (a) press `Ctrl+Alt+E`; (b) press `Tab`, type "script editor", press `Enter`; (c) click `[data-testid="quick-action-tools.script-editor"]` inside `[data-testid="quick-function-popup"]`. Any one path failing leaves the other two. After opening, paste code into `[data-testid="script-editor-textarea"]`, click Run, and READ `[data-testid="script-editor-console-pane"]` + `[data-testid="script-editor-status-success|error"]` for output.
15. Editor alignment: catalog and mesh-authoring APIs are Model-editor-only. Before calling any `ModelEditor.catalog.*` / `ModelEditor.create.*` method, call `Utils.editor.current()` — if it returns anything other than `"model"`, call `await Utils.editor.switchTo("model")` first. To see all publicly available editors, call `Utils.editor.list()` — it filters out alpha-gated modes.
16. Only Model and Previewer editors are publicly available. `Utils.editor.switchTo("controlrig")` and `Utils.editor.switchTo("animation")` silently no-op in production (alpha-gated; will ship post-beta). Do NOT assume those modes are reachable; use `Utils.editor.list()` to enumerate the public set. If your script needs a non-Model editor, switch via `Utils.editor.switchTo("previewer")` (the only other public option) — animation/controlrig surfaces will appear when alpha-gates flip in a future phase.
17. Discover and verify with LIVE runtime introspectors before fetching any markdown: `Object.keys(ModelEditor.create)` enumerates the primitives, `ModelEditor.help()` prints the doctrine + namespace grid, `ModelEditor.<namespace>.<name>.help` reads one method, and `Utils.examples.list()` / `Utils.recipes.list()` list runnable worked examples (`Utils.recipes.run(id)` executes one; `Utils.recipes.code(id)` returns its source to adapt). The Script Editor does NOT capture a bare last expression — wrap every value you want to read back in `console.log(JSON.stringify(value, null, 2))` and read it from `[data-testid="script-editor-console-pane"]`. Fetch `/claudechrome.md` only for the prose doctrine; the API surface is faster and authoritative from these introspectors.
18. Do NOT click viewport toolbar or QuickBar primitive icons to build geometry — they ARM a sticky interactive draw popup that blocks subsequent calls. The scriptable disarm is `Utils.cancelActiveTool()` (the equivalent of pressing ESC or clicking Cancel). Build EVERYTHING through the Script Editor: `await create.<primitive>(opts)` then `xform`, `mesh.boolean`, and `setMaterial`. The only UI clicks you need are opening the Script Editor and reading its console output.
19. Center the pivot BEFORE you rotate: primitive geometry can sit offset from its pivot (a torus centers at `[0, tube, 0]`, not at the pivot), so rotating an un-centered pivot swings the shape out of alignment — call `await mesh.centerPivot()` first. And verify POSITION from the GEOMETRY, never the transform: read `mesh.geometry.bbox({ space: "world" }).center` (canonical meters) — a centered-looking `translate` value can hide an offset shape, and reading the transform back is the classic false-green.

## Reading errors

When a script run fails, the Script Editor renders a structured error card at the top of the console pane. Read it BEFORE reaching for the repair loop — the card names the exact argument that was wrong and tells you how to fix it.

DOM contract — the card is a child of `[data-testid="script-editor-console-pane"]`:

| testid | Content | Present when |
|--------|---------|--------------|
| `script-editor-error-header` | `ErrorClass: message` | always (any run error) |
| `script-editor-error-argname` | `[argName]` pill — the name of the bad argument | `InvalidArgumentError` with a named arg |
| `script-editor-error-code` | `[code]` badge — machine error code (`INVALID_ARG`, `OP_NOT_APPLICABLE`, etc.) | structured AnimBox errors |
| `script-editor-error-expected` | `Expected: <shape>` — what the argument should have been | error carries an `expected` field |
| `script-editor-error-remediation` | `Fix: <instruction>` — a human + LLM-readable correction hint | error carries a `remediation` field |
| `script-editor-error-stack` | filtered stack frames (user code first; engine frames hidden) | always (any run error) |
| `script-editor-error-copy` | copy button — copies the FULL unfiltered stack to the clipboard | always (any run error) |

Error classes — the `[code]` badge tells you which self-correction applies:

- `INVALID_ARG` (`InvalidArgumentError`) — you passed a bad value. Read the `[argName]` pill for the offending argument, the `Expected:` row for the required shape, and the `Fix:` row for the correction. Example: `mesh.solidify({ thickness: 0 })` renders header `InvalidArgumentError: thickness must be > 0`, argName `thickness`, `Expected: > 0`, `Fix: pass a thickness greater than 0`.
- `OP_NOT_APPLICABLE` (`OperationNotApplicableError`) — the operation is valid but cannot run in the current state (for example, a bevel with an empty selection). Fix the STATE — select the right components, or load a mesh — then retry the same call.
- `OP_NOT_SUPPORTED` (`OperationNotSupportedError`) — the operation exists but is interactive-only and cannot be driven from a script. Use the keyboard or menu path, or call `<namespace>.help()` for the scriptable equivalent.
- `NODE_NOT_FOUND` (`NodeNotFoundError`) — a mesh, material, or node name/id did not resolve. Re-list the scene with `ls()` and re-resolve against a current name.
- `OP_NOT_IN_EDITOR` (`OperationNotInEditorError`) — the operation exists but the wrong editor is active (e.g. a `ModelEditor.create.*` / `ModelEditor.catalog.*` call while the Previewer is active). Call `Utils.editor.current()`; if it is not `"model"`, `await Utils.editor.switchTo("model")` then retry the same call.

Silent-no-op self-check (CRITICAL). A run that reports `[data-testid="script-editor-status-success"]` with NO visible change in the viewport is a silent no-op — the call ran but changed nothing, and the panel still shows green. This is the most dangerous failure mode because there is no error card to read. Always confirm geometry actually changed:

```js
// `mesh` is the handle you just operated on.
const before = mesh.verts.count;
// ...run your operation on mesh...
const after = mesh.verts.count;
console.log('topology changed:', before !== after, { before, after });
```

If the count is unchanged the vertices may still have MOVED — snapshot positions first with `const snap = mesh.verts.getPositions({ space: 'object' });`, run the op, then call `inspect.diff(meshId, snap)`. Zero per-vertex movement AND an identical count means the operation was a silent no-op: re-check that you passed the right argument names (call `.help` on the method) and that a mesh is actually selected.

## Repair loop

When an edit feels wrong, do NOT rewrite the script from scratch. Run the three-phase repair loop.

DIAGNOSE — read state directly with the precision-validation cluster. Call `<Editor>.ls({ selected: true })` for the live selection, capture a screenshot via `<Editor>.viewport.captureScreenshot()`, call `<Editor>.inspect.measure(a, b, axis?)` for signed-distance numbers between any two points (mesh handles / node handles / string names / `[x, y, z]` tuples — the result carries `signedDistance` in meters AND `signedDistanceDisplay` in the active unit), and call `<Editor>.inspect.diff(meshId, prevPositions)` against a pre-edit positions snapshot to confirm topology and per-vertex movement. Numbers plus visuals together. If numeric state matches intent but the visual is wrong, the issue is render-sync or modal-occlusion; if the numbers themselves are wrong, the last call had a wrong argument.

MINIMAL-EDIT — change the smallest responsible thing. Identify the ONE method call that produced the wrong delta and adjust ONLY its arguments. One wrong argument, one missing translate, one stale selection. Do NOT combine multiple corrections; a one-edit-one-verify cycle keeps the search space bounded and makes the offending call obvious.

RE-INSPECT — rerun the diagnose tools. Confirm the delta is now zero (`inspect.measure` returns the expected offset; `inspect.diff` shows only the change you intended; the screenshot matches intent). If still wrong, narrow further — never broaden the edit. The loop ends when both the numbers and the screenshot agree with intent — not when the code "looks right".

## Recipes

Compose compound shapes FRESH from `create.*` + `mesh.boolean(other, "union"|"difference"|"intersection")` + `xform({t, r, s})` — that is the DEFAULT. Recipes are OPTIONAL seeds to adapt when one fits; when none fits, compose fresh with confidence and say so honestly. Most organic / editable forms are built fresh this way: a mushroom from a stretched sphere capped on a tapered cylinder, a leaf from a flattened-and-bent plane, a simple character head from a few merged spheres, a vase from a revolved profile. Parametric / mechanical / printable shapes (gears, threads, brackets) are usually cleaner via OpenSCAD — prefer it for those when appropriate; this is a SOFT preference only, never a default. To ADAPT a curated seed: `Utils.recipes.list({ editor: "model" })` lists ids, `Utils.recipes.code(id)` reads one to ADAPT, `Utils.recipes.run(id)` executes it through the production runScript; the browsable pages are `/docs/scripting/model-editor` + `/docs/scripting/previewer` (filter category "Compound Forms"). For a worked example of any specific symbol, use `Utils.examples.list()` / `Utils.examples.code(symbol)` / `Utils.examples.run(symbol)` / `Utils.examples.forSurface(apiPath)` (one example per public symbol). A symbol present in the catalog is confirmed-callable — trial-call it or read `Utils.examples.code(symbol)`; NEVER report a catalogued symbol as missing. Press Run (Ctrl+Enter) in the Script Editor — your entire script wraps in one history group, so `await history.undo()` (or Ctrl+Z at the keyboard) reverts the whole composite in one step. For OFF-THE-SHELF standard hardware (M5 screws, bearings, NEMA stepper mounts, standoffs), reach for `catalog.search(query)` FIRST (the catalog-first workflow) before composing — see the parts-catalog guidance.

## Catalog

Off-the-shelf parts ship as a searchable catalog on `catalog.*` (Model editor only — mesh authoring is Model-scoped). Call `catalog.list()` (12,000+ CatalogPart descriptors with id / name / license / stepUrl), `catalog.search(query)` for tokenized matches against name + tags (multi-word works — every token must match: `catalog.search('M5 screw')`, `catalog.search('shaft collar')`), `catalog.get(id)` for one part's metadata, and `await catalog.install(id, { position: [x, y, z] })` to import AT that position (meters; omitted → the default offset, nudged per repeat so duplicates land side-by-side) as ONE undo step — `await history.undo()` (or Ctrl+Z) reverts the install. A multi-part STEP install adds every sub-mesh; the returned handle is the first, siblings are named `"<part name> (2)"`, `"<part name> (3)"`, … reachable via `ls()`. Browse interactively via `await catalog.browse()`. The corpus is INDUSTRIAL hardware + electronics mounting: ISO/DIN fasteners, nuts / washers / standoffs, bearings, aluminum extrusions, heat-set inserts, shaft collars, NEMA stepper mounts, rack panels, fan ducts. NO character props or decorative assets exist — compose those fresh with `create.*`. Hot-path bundle of ~100 curated parts loads instantly; the long tail lazy-fetches on demand.

## UI Affordances

You drive AnimBox via the visible DOM — no DevTools, no `browser_evaluate`. Your bridge is the existing Script Editor.

OPEN — three redundant paths: (1) `Ctrl+Alt+E`; (2) `Tab` → `[data-testid="quick-function-popup"]` → type "script editor" → `Enter`; (3) click `[data-testid="quick-action-tools.script-editor"]`. Any one failing leaves two.

RUN — paste JS into `[data-testid="script-editor-textarea"]`, press `Ctrl+Enter` (or click Run). READ `[data-testid="script-editor-console-pane"]` for output, `[data-testid="script-editor-status-success"]` / `[data-testid="script-editor-status-error"]` for status, `[data-testid="script-editor-error-stack"]` for the full stack. The panel root is `[data-testid="script-editor-panel"]`.

EDITOR FIRST — catalog and mesh-authoring APIs are Model-editor-only. Call `Utils.editor.current()` to read the active mode; if not `'model'`, `await Utils.editor.switchTo("model")` first. `Utils.editor.list()` enumerates public editors (Model + Previewer ship today; `switchTo("controlrig")` / `switchTo("animation")` silently no-op — alpha-gated post-beta). If a click ever armed a sticky interactive draw popup, call `Utils.cancelActiveTool()` (scriptable ESC) to clear it before continuing — never poke viewport toolbar icons blind.

THREE AUTHORING WORKFLOWS — pick the right one, IN THIS ORDER:

(A) CATALOG FIRST — `catalog.search(query)` for ADDITIVE off-the-shelf parts. The bundled catalog has 12,000+ STEP parts of INDUSTRIAL standard hardware + electronics mounting: ISO/DIN fasteners (M2-M12), nuts / washers / standoffs, bearings, aluminum extrusions, heat-set inserts, shaft collars, NEMA stepper mounts, rack panels, fan ducts (NO character props or decorative assets — those are workflow B compositions). Multi-word queries are tokenized (every word must match): `const hits = await catalog.search('M5 screw'); const mesh = await catalog.install(hits[0].id, { position: [x, y, z] });` — `position` is in meters and is honored; omit it for the default offset (repeat installs land side-by-side). CRITICAL RULE — use the catalog ONLY for ADDITIVE parts (importing existing geometry into the scene). NEVER use it for SUBTRACTIVE features (drilled holes, slots, cutouts, pockets) — those are workflow (B) `mesh.boolean(other, 'difference')`. Example: if the customer asks for "4 M5 mounting holes," the catalog has M5 SCREWS (additive); you want HOLES (subtractive) — drill with a 5mm cylinder via `mesh.boolean(hole, 'difference')`. Browse interactively: `await catalog.browse()`.

(B) PRIMITIVES + BOOLEAN + EXAMPLES — AnimBox Geo Utils, the editable-quad path: `create.*` + `mesh.boolean(...)` for custom mesh geometry, compound forms, and SUBTRACTIVE features. 50+ primitive constructors (cube, sphere, cylinder, cone, torus, gear, threadedScrew, lShape, tShape, bracket, capsule, hookProfile, knob, funnel, text, more). Compose multi-step shapes by chaining confirmed-callable ops — `create.* + mesh.boolean + mesh.mirror + Node.radialDuplicate`: `await create.cylinder({...})` then `mesh.boolean(other, 'union'|'difference'|'intersection')` + `mesh.mirror({axis})` + `xform({t:[x,y,z], r:[0,90,0]})` + `Node.radialDuplicate({count, axis, angle})` + for-loops. Output stays editable as quads. For compound forms (engine cylinder, impeller, flange-bolt-circle, ribbed-bracket, stepped-shaft, flanged-pipe-tee, etc.), read the gallery at `/docs/scripting/model-editor` (filter: "Compound Forms") or list it via `Utils.recipes.list()` for head-start patterns to ADAPT.

(C) OPENSCAD — SCAD, the parametric path: for native parametric extrude / sweep / hull / minkowski source click `[data-testid="script-editor-tab-add-menu-openscad"]` → write SCAD source → Bake. Pros: parametric primitives and operations OpenSCAD users already know — preferred for mechanical / printable parts. ⚠ Cons: bakes to a TRIANGLE-only static mesh (NOT re-editable as quads); for geometry you will keep editing as quads, use (B) AnimBox Geo Utils instead.

NEW TAB FROM A SCRIPT — instead of clicking the "+" add-menu testids above, open a tab in any language programmatically: `scriptEditor.newTab({ language })`, where the `language` value is one of `'animbox'` or `'openscad'` (e.g. `scriptEditor.newTab({ language: 'openscad' })`). Pair it with the "+" add-menu item (`[data-testid="script-editor-tab-add-menu-openscad"]`) — the scriptable path means you never have to default to an AnimBox tab.

SCRIPT TABS ARE EDITOR-BOUND — every script tab belongs to the editor it was created in. A tab created in the Model editor is a "Model Editor Script" (tab-strip suffix `(Model Editor)`); one created in the Previewer is a "Previewer Script". Opened OUTSIDE its editor, a tab renders READ-ONLY — still selectable/copyable, dimmed, with a scope banner naming its home editor — and Run is refused with a console hint (the same rule OpenSCAD tabs have always had outside Model). The "+ New Script" menu creates the ACTIVE editor's script type. Inside a tab, any code line referencing a FOREIGN editor namespace (e.g. `PreviewEditor.*` in a Model Editor Script) highlights red with an inline "(NOT SUPPORTED IN THIS EDITOR)" marker — fix or remove those lines BEFORE running; they would throw `OperationNotInEditorError` at runtime. To run code for another editor: switch editors first (`await Utils.editor.switchTo("previewer")`), then write it in a tab created THERE.

VERIFY — `await viewport.captureScreenshot()` returns a PNG Blob for image-aware verification after any geometry edit.

ANIMATION (Previewer) — `PreviewEditor.animation.{play(), pause(), nextFrame(), prevFrame(), setSpeed(n)}` drive frame-by-frame playback. Switch first via `await Utils.editor.switchTo("previewer")`.

ASYNC — `await` EVERY call that loads or mutates (`io.openFile`/`io.exportFile`, `animation.*` transport, `material.pbr.<channel>.set`, `dev.verify`, `delete`). A non-awaited call HIDES the outcome: a validation/capability error becomes an unhandled rejection (looks like it worked but did nothing) and you read state before it settled. Two traps: (1) `io.openFile` RESOLVES BEFORE the scene graph finishes HYDRATING — poll `ls({type:'mesh'}).length` until non-zero (~1-2s for large files) before reading geometry/joints/clips; 0 means still-hydrating, NOT empty. (2) `await material.pbr.baseColor.set([r,g,b,a])` THROWS on a bad value — without `await` the throw is swallowed and the old value silently stays.

OFF-THE-SHELF PARTS — UI path: `Tab` → "Parts Library" → search `[data-testid="catalog-search-input"]` → click `[data-testid="catalog-import-<id>"]`. Or use workflow (A) above from script.

API DOCS — `/docs/scripting/api/<moduleId>/<method>` (e.g. `/docs/scripting/api/Mesh/boolean`). Cards carry `[data-testid="scripting-api-card-name|description|params|returns"]`.

VERSION POLL — `/llms-version.json` for current bootstrap version + recentChanges list.

## Intent triage

ASK FIRST, BUILD SECOND. Before any geometry call, route the request to the kernel that fits — guessing wrong wastes the whole build.

Does the result need PARAMETRIC / CAD geometry — revolve/sweep/loft/hull/minkowski, threads, or parameter-driven shapes?
  → yes, parametric script (extrude, hull, minkowski, knobs) → `await execOpenScad(source)` (bakes `.scad` to a mesh). Best for parameter-driven shapes an OpenSCAD user already knows.
     BOSL2 ships with AnimBox and is already on the include path — lean on its purpose-built modules rather than approximating hardware from raw primitives. Start every BOSL2 script with `include <BOSL2/std.scad>`. Fastener work: `include <BOSL2/screws.scad>` gives `screw("M8")` and `threaded_nut()`; `include <BOSL2/threading.scad>` adds `threaded_rod()` for custom thread forms. Curved and blended builds: `include <BOSL2/rounding.scad>` (`round_corners()`, `offset_sweep()`) plus `include <BOSL2/skin.scad>` (`skin()`, `path_sweep()`) with `<BOSL2/beziers.scad>` supplying the curve inputs. Keep `$fn` around 32 while you iterate — bakes stay snappy — and bump it to 128+ only for the final bake.
  → no  → AnimBox Geo Utils — the result is EDITABLE / ORGANIC / a game asset (sculpts, characters, props you keep editing as quads): use `create.*` primitives + `mesh.boolean(other, 'union'|'difference'|'intersection')` + `xform({ t, r, s })`. This is the default path; the output stays editable as quads (execOpenScad / SCAD bakes to a STATIC, TRIANGLE-only mesh).

AUTO - BEST TOOL — when no single path fits, let the agent pick per part, or combine them: SCAD for the frame/engine, AnimBox Geo Utils for the tank/seat. SOFT preference (NOT a default): for mechanical / printable parts, prefer SCAD over composing `create.gear` / `create.threadedScrew` equivalents from raw primitives. When the SCAD-vs-Geo-Utils choice is MATERIAL (printable / parametric / mechanical) and you cannot confidently route, ASK ONE QUESTION — never silently pick.

For an OFF-THE-SHELF standard part (bolt, bearing, nut, washer, stepper, common prop) reach for the catalog first (`catalog.search`); to IMPORT and tweak an existing file use `io.*`. The OpenSCAD kernel is Model-editor-only — `await Utils.editor.switchTo("model")` first if `Utils.editor.current()` is not `'model'`.

VISUAL-VERIFY CADENCE — after EVERY geometry op, `await viewport.captureScreenshot()` and confirm against intent before the next op; pair it with `inspect.measure(a, b)` for signed-distance numbers. Numbers can be right while the picture is wrong.

WHEN AMBIGUOUS, ASK ONE QUESTION before building: "Do you want a SCAD part (parameter-driven dimensions via OpenSCAD, baked to a triangle-only static mesh) or an AnimBox Geo Utils mesh (editable quads) you can keep sculpting?"

## Discovery primitives

Three calls map the territory before reaching for specific methods. Run them first in any new session — every editor exposes the same `.help()` shape, so the pattern is identical whether you opened a Model Editor tab or a Previewer tab.

- `help()` — Print the doctrine block + namespaces grid + top-level callables for the bound editor. The fully-qualified per-editor forms (`ModelEditor.help()`, `PreviewEditor.help()`, `RigEditor.help()`, `AnimEditor.help()`) also work. Drill via `<namespace>.help()` for an operations TOC, then `<namespace>.<name>.help` for Description / Input / Returns on a specific method.
- `<namespace>.help()` — List every operation under one namespace with its one-line description. Example: `io.help()` shows every io-namespace method (openFile, exportFile, importModel, exportModel, etc.).
- `actions.search(query)` — Fuzzy-search Quick Actions by name and category. Returns descriptors with the `id` you can pass to `actions.run(id)` to invoke. `PreviewEditor.actions.search(...)` exposes the same surface scoped to the Previewer.
- `Utils.recipes.list({ editor: "model" })` — List curated cross-API recipes (compound forms, material setups). Read one with `Utils.recipes.code(id)` to ADAPT it, or execute it directly via `Utils.recipes.run(id)`.
- `Utils.examples.list()` — List per-API worked examples. `Utils.examples.code(symbol)` returns the snippet to adapt; `Utils.examples.run(symbol)` runs it through the production runner.

## Editor surfaces

AnimBox runs two main editors today (Model + Previewer); two more (Control Rig + Animation) are inactive in the public beta but ship after. Each sub-page below lists the top quick tasks for that editor plus the methods most worth knowing first.

- `/docs/scripting/claude-chrome/previewer` — Previewer: load assets, list materials and textures, capture screenshots, frame the camera, switch render modes.
- `/docs/scripting/claude-chrome/model-editor` — Model Editor: two modelling paths. Direct path uses `create.*` primitives plus Quick Actions (extrude, bevel, etc.). Parametric path uses the built-in OpenSCAD editor via `scriptEditor.open()` for scripted geometry.
- `/docs/scripting/claude-chrome/control-rig` — Control Rig: rigging editor. The scripting surface is partially live; the editor is inactive in the public beta and ships after.

## How to drive a session

Start by printing `help()` — the same call works in every editor tab. The output leads with the doctrine block — read it. Then drill into a namespace with `<namespace>.help()` (e.g., `io.help()` lists every io-namespace method). For a specific method, read `<namespace>.<name>.help` (e.g., `io.exportFile.help`) for Description / Input / Returns. Quick Actions live alongside the typed methods; `actions.search("extrude")` returns descriptors with action ids, then `actions.run(id)` invokes the chosen one. Read scene state back through `ls()` (or `ls({ selected: true })` for the current selection) and confirm visual results with `viewport.captureScreenshot()`.

## Examples

Worked examples for both Script Editor languages, sourced from the single `getExamplesForLanguage` read path — identical to the in-editor Examples popup, so this reference and the editor never drift. To open a tab in a given language from a script, call `ModelEditor.scriptEditor.newTab({ language })`, where the `language` value is one of `'animbox'` or `'openscad'`.

### AnimBox-JS examples

1143 worked AnimBox-JS examples are available through the single `getExamplesForLanguage` read path (the same set the in-editor Examples popup shows). The full per-callable surface is in the **## API reference** above; copy-paste code for each lives in [/recipe-library.json](/recipe-library.json) and the in-editor Examples popup (Script Editor → Examples). Create an AnimBox-JS tab with `ModelEditor.scriptEditor.newTab({ language: 'animbox' })`. Examples by namespace:

- `AnimEditor` — 25 examples
- `AnimEditor.actions` — 6 examples
- `AnimEditor.animation` — 31 examples
- `AnimEditor.history` — 13 examples
- `AnimEditor.inspect` — 4 examples
- `AnimEditor.io` — 21 examples
- `AnimEditor.scene` — 6 examples
- `AnimEditor.scriptEditor` — 7 examples
- `AnimEditor.viewport` — 80 examples
- `MathLib` — 83 examples
- `ModelEditor` — 26 examples
- `ModelEditor.actions` — 6 examples
- `ModelEditor.catalog` — 9 examples
- `ModelEditor.clipboard` — 5 examples
- `ModelEditor.create` — 50 examples
- `ModelEditor.history` — 13 examples
- `ModelEditor.inspect` — 4 examples
- `ModelEditor.io` — 25 examples
- `ModelEditor.material` — 4 examples
- `ModelEditor.scene` — 6 examples
- `ModelEditor.scriptEditor` — 7 examples
- `ModelEditor.tool` — 12 examples
- `ModelEditor.tool.sculpt` — 3 examples
- `ModelEditor.tool.sim` — 6 examples
- `ModelEditor.viewport` — 80 examples
- `PreviewEditor` — 17 examples
- `PreviewEditor.actions` — 6 examples
- `PreviewEditor.animation` — 31 examples
- `PreviewEditor.create` — 5 examples
- `PreviewEditor.history` — 13 examples
- `PreviewEditor.inspect` — 4 examples
- `PreviewEditor.io` — 24 examples
- `PreviewEditor.scene` — 6 examples
- `PreviewEditor.scriptEditor` — 7 examples
- `PreviewEditor.viewport` — 80 examples
- `Recipes` — 413 examples
- `Utils.path` — 2 examples
- `Utils.wait` — 3 examples

### OpenSCAD examples

1280 worked OpenSCAD examples, rendered from the single `getExamplesForLanguage` read path (the same set the in-editor Examples popup shows). Create a tab in this language with `ModelEditor.scriptEditor.newTab({ language: 'openscad' })`.

#### 608 bearing footprint ring

A solid printable ring matching the footprint of a standard 608 skate/skateboard ball bearing (22 mm OD, 8 mm bore, 7 mm wide). Use it as a press-fit spacer, a bearing-seat gauge, or a drop-in dummy bearing. The outer and inner edges are lightly chamfered so it seats cleanly. Tune the diameters and width to gauge other bearing sizes.

```openscad
// 608 bearing footprint ring
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

outer_d  = 22;   // outer diameter (608 = 22 mm)
bore_d   = 8;    // inner bore diameter (608 = 8 mm)
width    = 7;    // axial width (608 = 7 mm)
edge_cf  = 0.6;  // chamfer on outer + bore edges
$fn = 96;

tube(h = width, od = outer_d, id = bore_d, chamfer = edge_cf, anchor = BOTTOM);
```

#### Bevel gear

A straight-cut bevel gear that transmits motion between two shafts meeting at 90 degrees. Print one of these plus a mating bevel gear (swap teeth and mate_teeth) to build a right-angle drive. Tune the module, tooth count, mate tooth count, face width, and bore.

```openscad
// Bevel gear
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/gears.scad>

teeth       = 24;  // teeth on THIS gear
mate_teeth  = 24;  // teeth on the mating gear (sets the cone angle)
gear_mod    = 2;   // tooth module in mm
face_w      = 8;   // face width (tooth length along the cone) in mm
shaft_d     = 6;   // center shaft bore diameter in mm
$fn = 48;

bevel_gear(mod = gear_mod, teeth = teeth, mate_teeth = mate_teeth,
           face_width = face_w, shaft_diam = shaft_d, shaft_angle = 90,
           pressure_angle = 20, spiral = 0);
```

#### Generic threaded bottle cap

A generic screw-on bottle cap that mates with the generic threaded bottle neck, built by the BOSL2 bottlecaps library. Tune the cap height, thread depth, thread outer diameter, and pitch (keep them matched to your neck) for a snug fit. Pairs with the generic bottle neck recipe.

```openscad
// Generic threaded bottle cap
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/bottlecaps.scad>

cap_h        = 11.2;   // cap height (mm)
cap_wall     = 2;      // wall thickness (mm)
thread_depth = 2.34;   // thread depth (mm) — match the neck thread
thread_od    = 28.58;  // thread outer diameter (mm) — match the neck
pitch        = 4;      // thread pitch (mm) — match the neck
$fn = 96;

generic_bottle_cap(
    height       = cap_h,
    wall         = cap_wall,
    thread_depth = thread_depth,
    thread_od    = thread_od,
    pitch        = pitch,
    texture      = "knurled"
);
```

#### Generic threaded bottle neck

A generic threaded bottle neck finish with a support ring, built by the BOSL2 bottlecaps library. Pairs with the matching generic bottle cap. Tune the neck diameter, thread outer diameter, height, and thread pitch to design your own container opening.

```openscad
// Generic threaded bottle neck
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/bottlecaps.scad>

neck_diam = 25;      // outer diameter of the neck wall (mm)
inner_id  = 21.4;    // inner bore diameter (mm)
thread_od = 27.2;    // outer diameter over the threads (mm)
neck_h    = 17;      // neck height (mm)
pitch     = 3.2;     // thread pitch (mm)
$fn = 96;

generic_bottle_neck(
    neck_d    = neck_diam,
    id        = inner_id,
    thread_od = thread_od,
    height    = neck_h,
    pitch     = pitch
);
```

#### Wall cable clip

A small screw- or adhesive-mount cable clip: a flat backing pad with a rounded C-shaped catch that snaps over a cable to route it along a wall or desk edge. The catch mouth is narrower than the cable so it grips. Tune the cable diameter, mouth opening, backing size, and wall thickness.

```openscad
// Wall cable clip
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

cable_d  = 6;    // diameter of the cable to hold
gap      = 3.5;  // mouth opening width (under cable_d so it grips)
wall     = 2;    // catch wall thickness
pad_w    = 16;   // backing pad width
pad_t    = 3;    // backing pad thickness
clip_len = 12;   // depth of the clip along the cable
$fn = 72;

bore_r  = cable_d / 2;
outer_r = bore_r + wall;
// Catch centre is lowered so the outer body overlaps (fuses with) the pad,
// while the bore floor stays a touch above the pad's top surface.
overlap  = wall * 0.75;
catch_cz = pad_t + outer_r - overlap;

union() {
    // Flat backing pad against the wall, vertical corners rounded.
    cuboid([pad_w, clip_len, pad_t], rounding = 1.5, edges = "Y", anchor = BOTTOM);

    // C-shaped catch: outer cylinder, bore removed, plus a front mouth slot.
    translate([0, 0, catch_cz])
        difference() {
            // Outer body of the catch (axis along Y / cable direction).
            xrot(90) cyl(h = clip_len, r = outer_r, rounding = 0.8);
            // Cable bore.
            xrot(90) cyl(h = clip_len + 0.4, r = bore_r);
            // Mouth slot opening upward (away from the pad).
            up(outer_r / 2)
                cube([gap, clip_len + 0.4, outer_r + 0.2], center = true);
        }
}
```

#### Flat corner brace

A flat L-shaped mending plate that reinforces a 90 degree corner joint. Two perpendicular arms share a rounded outer profile with a screw hole in each arm and the heel. Prints flat as one solid with no supports. Tune the arm length, width, thickness, corner rounding, and screw-hole diameter.

```openscad
// Flat corner brace
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

arm     = 45;   // length of each arm from the heel
width   = 16;   // arm width
thick   = 4;    // plate thickness
round_r = 4;    // outer corner rounding radius
hole_d  = 4.2;  // screw-hole diameter
$fn = 48;

difference() {
    union() {
        // Arm running along +X.
        cuboid([arm, width, thick], rounding = round_r,
               edges = [LEFT + FRONT, LEFT + BACK, RIGHT + FRONT],
               anchor = BOTTOM + LEFT + BACK);
        // Arm running along -Y.
        cuboid([width, arm, thick], rounding = round_r,
               edges = [LEFT + FRONT, RIGHT + FRONT, RIGHT + BACK],
               anchor = BOTTOM + LEFT + BACK);
    }
    // Hole near the end of the X arm.
    translate([arm - width / 2, -width / 2, 0])
        cyl(d = hole_d, h = thick + 2, anchor = CENTER);
    // Hole near the end of the Y arm.
    translate([width / 2, -arm + width / 2, 0])
        cyl(d = hole_d, h = thick + 2, anchor = CENTER);
    // Hole at the heel.
    translate([width / 2, -width / 2, 0])
        cyl(d = hole_d, h = thick + 2, anchor = CENTER);
}
```

#### Rounded decorative bowl

A curved bowl with a flat stable base and a rounded rim, hollowed to a uniform wall and a solid floor. Built by revolving an explicit profile with BOSL2 rotate_sweep. Tune the rim radius, depth, wall and base flat.

```openscad
// Rounded decorative bowl
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

rim_r   = 55;   // outer radius at the rim
depth   = 42;   // bowl depth
wall    = 3;    // wall thickness
floor_t = 4;    // solid floor thickness
base_r  = 24;   // radius of the flat resting base
$fn = 120;

// Outer profile (right half), a smooth curve from base to rim.
outer = [
    [0,            0],
    [base_r,       0],
    [rim_r * 0.78, depth * 0.30],
    [rim_r * 0.97, depth * 0.70],
    [rim_r,        depth],
    [0,            depth],
];
// Inner cavity profile: offset inward by the wall, floor raised by floor_t.
inner = [
    [0,                   floor_t],
    [base_r - wall,       floor_t],
    [rim_r * 0.78 - wall, depth * 0.30 + floor_t * 0.4],
    [rim_r * 0.97 - wall, depth * 0.70],
    [rim_r - wall,        depth],
    [0,                   depth],
];

difference() {
    rotate_sweep(outer, closed = true);
    rotate_sweep(inner, closed = true);
}
```

#### Faceted gem

A solid brilliant-cut style gem: a flat-topped table on a faceted crown over a pointed pavilion, built from stacked BOSL2 regular prisms. Single shell, prints point-down style as one solid (table up). Tune the girdle width, facet count, and crown/pavilion proportions.

```openscad
// Faceted gem
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

sides     = 8;    // number of facets around the girdle
girdle_r  = 26;   // radius at the widest point (girdle)
table_r   = 15;   // radius of the flat top table
crown_h   = 14;   // height of the upper crown (girdle to table)
pavi_h    = 26;   // height of the lower pavilion (girdle to point)
$fn = 8;

union() {
    // Crown: girdle tapering up to the flat table.
    regular_prism(n = sides, r1 = girdle_r, r2 = table_r, h = crown_h, anchor = BOTTOM);
    // Pavilion: girdle tapering down to a near-point, fused at the girdle plane.
    down(pavi_h)
        regular_prism(n = sides, r1 = 0.8, r2 = girdle_r, h = pavi_h, anchor = BOTTOM);
}
```

#### Hanging ornament ball

A solid decorative sphere with an integrated torus hanging loop on top, fused into one printable shell. Tune the ball diameter, the loop size/thickness and the facet count for a smoother surface.

```openscad
// Hanging ornament ball
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

ball_d    = 50;   // sphere diameter
loop_or   = 7;    // loop outer radius (hole size)
loop_thk  = 2.6;  // loop wire thickness (torus minor radius)
$fn = 96;

union() {
    // The decorative ball.
    spheroid(d = ball_d, anchor = CENTER);
    // Hanging loop: a torus standing vertically, overlapping the top of the ball
    // so the two fuse into a single solid (loop sits in the XZ plane).
    up(ball_d / 2 - loop_thk)
        xrot(90)
            torus(or = loop_or, ir = loop_or - 2 * loop_thk);
}
```

#### Picture frame with stand

A desktop photo frame: a rounded rectangular border with a window cut through it, fused to an angled easel leg at the back so it stands on its own. One printable shell, prints flat on its back. Tune the frame size, border width, window and leg angle.

```openscad
// Picture frame with stand
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

frame_w   = 90;   // overall frame width
frame_h   = 120;  // overall frame height
border    = 12;   // border thickness around the photo window
frame_t   = 6;    // frame plate thickness (front to back)
leg_len   = 78;   // length of the easel leg
leg_ang   = 22;   // lean-back angle of the leg (degrees)
$fn = 48;

union() {
    // Frame plate lying in the XY plane (front face up), window cut through.
    difference() {
        cuboid([frame_w, frame_h, frame_t], rounding = 5, edges = "Z", anchor = BOTTOM);
        down(0.5)
            cuboid([frame_w - 2 * border, frame_h - 2 * border, frame_t + 2],
                   rounding = 3, edges = "Z", anchor = BOTTOM);
    }
    // Solid hinge block fused to the lower-back of the frame; the leg grows from it.
    translate([0, -frame_h / 2 + 10, 0])
        union() {
            // Hinge block bridging frame and leg (overlaps the plate solidly).
            cuboid([20, 16, frame_t + 3], rounding = 2, edges = "Z", anchor = BOTTOM);
            // Easel leg leaning out from the hinge block.
            up(frame_t)
                xrot(-leg_ang)
                    cuboid([18, leg_len, 5], rounding = 3, edges = "Z",
                           anchor = FRONT + BOTTOM);
        }
}
```

#### Tealight candle holder

A weighted round holder with a recessed pocket sized for a standard tealight cup and a rounded outer rim. Single shell, prints flat with no supports. Tune the outer diameter, height, the tealight pocket diameter/depth and the rim rounding.

```openscad
// Tealight candle holder
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

outer_d   = 52;   // outer diameter
height    = 26;   // overall height
pocket_d  = 39;   // tealight pocket diameter (standard cup ~38-40mm)
pocket_h  = 13;   // tealight pocket depth (cup height ~10-12mm)
rim_round = 4;    // rounding on the outer top rim
$fn = 96;

difference() {
    // Solid rounded body.
    cyl(d = outer_d, h = height, rounding2 = rim_round,
        chamfer1 = 1, anchor = BOTTOM);
    // Recessed pocket for the tealight cup, open at the top.
    up(height - pocket_h)
        cyl(d = pocket_d, h = pocket_h + 1, rounding1 = 2, anchor = BOTTOM);
}
```

#### Twisted spiral column

A decorative pillar/pedestal with a spiralling fluted shaft between a square base and a square cap, fused into one solid. The shaft is a rounded BOSL2 star profile linearly extruded with a twist. Tune the height, twist, flute count and base size.

```openscad
// Twisted spiral column
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

shaft_h   = 120;  // height of the twisting shaft
twist     = 200;  // total twist over the shaft (degrees)
points    = 6;    // number of flutes around the shaft
shaft_r   = 20;   // outer radius of the shaft
plinth    = 46;   // base/cap square width
$fn = 72;

// Rounded star profile so the twisted flutes have no razor edges (cleaner mesh).
shaft_profile = round_corners(
    star(n = points, r = shaft_r, ir = shaft_r * 0.74),
    radius = 2.5, $fn = 16
);

union() {
    // Square base plinth.
    prismoid(size1 = [plinth, plinth], size2 = [plinth - 4, plinth - 4],
             h = 14, rounding = 3, anchor = BOTTOM);
    // Twisting fluted shaft, overlapping the plinth and cap so all fuse.
    up(12)
        linear_extrude(height = shaft_h, twist = twist, slices = 160, convexity = 12)
            polygon(shaft_profile);
    // Square cap plinth, overlapping the shaft top.
    up(12 + shaft_h - 2)
        prismoid(size1 = [plinth - 4, plinth - 4], size2 = [plinth, plinth],
                 h = 14, rounding = 3, anchor = BOTTOM);
}
```

#### Faceted prismoid vase

An angular tapered vase with a wide square base flaring to a smaller rotated top, hollowed for stems. Built from two BOSL2 prismoids (outer minus inner) with a solid floor. Tune base/top widths, twist, height and wall.

```openscad
// Faceted prismoid vase
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

base_w  = 60;   // base width (square)
top_w   = 46;   // top opening width (square)
vase_h  = 120;  // overall height
wall    = 2.6;  // wall thickness
floor_t = 3;    // floor thickness
edge_r  = 3;    // vertical edge rounding
$fn = 64;

difference() {
    // Outer faceted body.
    prismoid(size1 = [base_w, base_w], size2 = [top_w, top_w],
             h = vase_h, rounding = edge_r, anchor = BOTTOM);
    // Inner cavity, raised above the floor so the base stays solid.
    up(floor_t)
        prismoid(size1 = [base_w - 2 * wall, base_w - 2 * wall],
                 size2 = [top_w - 2 * wall, top_w - 2 * wall],
                 h = vase_h, rounding = max(edge_r - wall, 0.5), anchor = BOTTOM);
}
```

#### Rounded bud vase

A smooth single-shell bud vase with a swelling belly and a narrowed neck, hollowed for water. Built by revolving a rounded profile with BOSL2 rotate_sweep. Tune the base/belly/neck radii, height and wall thickness.

```openscad
// Rounded bud vase
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

base_r  = 22;   // radius at the base
belly_r = 38;   // widest radius (belly)
neck_r  = 18;   // radius at the neck/mouth
vase_h  = 110;  // overall height
wall    = 2.4;  // wall thickness
$fn = 96;

// Outer profile (right half, x>=0) revolved around the Z axis.
function vase_profile(rb, rbelly, rn, h) = [
    [0,        0],
    [rb,       0],
    [rb + 6,   h * 0.18],
    [rbelly,   h * 0.45],
    [rn + 7,   h * 0.78],
    [rn,       h],
    [0,        h],
];

difference() {
    rotate_sweep(vase_profile(base_r, belly_r, neck_r, vase_h), closed = true);
    // Inner cavity: same profile scaled inward by the wall, with a solid floor.
    up(wall)
        rotate_sweep(vase_profile(base_r - wall, belly_r - wall, neck_r - wall, vase_h), closed = true);
}
```

#### Cable management clip

A desk-edge cable management clip: a flat adhesive base with an open C-shaped jaw on top that snaps around a cable bundle and holds it along the desk edge. The C jaw is a ring extruded across the clip depth with a top wedge cut for the snap opening, unioned to the base, so it prints flat-down as one solid. Tune the cable channel diameter, jaw opening, base size, and wall.

```openscad
// Cable management clip
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

cable_d   = 12;   // inner channel diameter (cable bundle)
wall      = 2.8;  // clip wall thickness
gap_w     = 8;    // width of the snap opening at the top
base_w    = 26;   // adhesive base width
depth     = 18;   // clip depth (along the cable run)
base_t    = 3;    // base thickness
$fn = 72;

outer_d = cable_d + 2 * wall;

union() {
    // Adhesive base plate.
    cuboid([base_w, depth, base_t], rounding = 2, except = [TOP, BOTTOM], anchor = BOTTOM);

    // C-jaw: extrude a ring-with-gap profile across the clip depth.
    up(base_t)
        xrot(90)
            linear_extrude(height = depth, center = true)
                difference() {
                    circle(d = outer_d);
                    circle(d = cable_d);
                    // Top opening slot.
                    translate([0, outer_d / 2])
                        square([gap_w, outer_d], center = true);
                }
}
```

#### Business-card holder

An angled desktop business-card holder: a rounded block with a slanted slot that cradles a stack of cards at a readable angle, plus a front cut-away so you can thumb the top card out. Carved entirely by difference from one rounded body so it prints as a single watertight piece. Tune the card width, stack thickness, slot angle, and wall.

```openscad
// Business-card holder
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

card_w     = 92;   // card width (slot length) — standard card ~90mm
stack_t    = 16;   // thickness of card stack the slot holds
slot_angle = 18;   // backward lean of the slot, degrees
wall       = 4;    // surrounding wall thickness
base_h     = 26;   // overall body height
round_r    = 3;    // outer corner rounding
$fn = 48;

body_w = card_w + 2 * wall;
body_d = stack_t + 2 * wall + 14;   // depth: stack + walls + front lip room
body_h = base_h;

difference() {
    cuboid([body_w, body_d, body_h], rounding = round_r, except = [TOP]);

    // Angled slot that holds the card stack (tall thin pocket, leaned back).
    translate([0, 2, body_h - 2])
        rotate([slot_angle, 0, 0])
            cuboid([card_w + 1, stack_t, body_h * 2.2],
                   rounding = 1, except = [TOP, BOTTOM], anchor = TOP);

    // Front thumb cut-away so the top card is reachable.
    translate([0, -body_d / 2 + wall + 1, body_h])
        cuboid([card_w - 24, wall * 3, body_h * 1.4], rounding = 4, except = [TOP], anchor = TOP);
}
```

#### Headphone hook bracket

An under-desk headphone hook bracket: a flat vertical mounting plate (screw-mount) with a hook arm that sweeps out and curls up to cradle a headphone band without creasing it. The side profile (plate + arm + upturned tip) is drawn once and extruded across the width, then screw holes are bored, so it is guaranteed a single watertight solid. Tune the plate size, hook reach, tip height, and width.

```openscad
// Headphone hook bracket
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

plate_h   = 60;   // mount plate height
plate_t   = 6;    // plate / arm material thickness
arm_reach = 45;   // how far the hook sticks out
tip_h     = 24;   // height of the upturned cradle tip
width     = 26;   // width across (band rests here)
$fn = 48;

// Side profile in the YZ-ish plane, drawn in 2D (X = reach, Y = height),
// then extruded across the part width and laid upright.
xrot(90)
linear_extrude(height = width, center = true)
    offset(r = 1.5) offset(r = -1.5)   // round all profile corners (kept < half arm thickness)
        polygon([
            [0,             0],            // plate back-bottom
            [plate_t,       0],            // plate front-bottom
            [plate_t,       plate_h - plate_t],     // arm root
            [plate_t + arm_reach,          plate_h - plate_t],  // arm tip bottom
            [plate_t + arm_reach,          plate_h - plate_t + tip_h], // tip top outer
            [plate_t + arm_reach - plate_t, plate_h - plate_t + tip_h], // tip top inner
            [plate_t + arm_reach - plate_t, plate_h],            // arm tip inner
            [0,             plate_h],       // plate back-top
        ]);
```

#### Monitor-riser foot

A single load-bearing foot/leg for a DIY monitor riser or laptop stand: a hollow tapered pedestal (wide stable base, narrower top) with a thick top deck and a centering spigot on top so a wooden or acrylic shelf board or a stacked foot locates onto it. Carved from a rounded prismoid with an internal cavity, printed open-side-down as one watertight shell. Tune the footprint, height, wall, taper, and spigot.

```openscad
// Monitor-riser foot
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

base_w    = 78;   // bottom footprint width/depth (square, sits on desk)
foot_h    = 50;   // height of one riser foot
top_w     = 60;   // top deck width/depth (narrower for a taper)
wall      = 4;    // shell wall thickness
spigot_d  = 22;   // top centering spigot diameter
round_r   = 6;    // corner rounding
$fn = 64;

union() {
    difference() {
        // Tapered pedestal: wide base, narrower top, rounded.
        prismoid(size1 = [base_w, base_w], size2 = [top_w, top_w], h = foot_h,
                 rounding = round_r, anchor = BOTTOM);
        // Hollow cavity opening downward (prints open-side-down, no supports).
        prismoid(size1 = [base_w - 2 * wall, base_w - 2 * wall],
                 size2 = [top_w - 2 * wall, top_w - 2 * wall],
                 h = foot_h - wall, anchor = BOTTOM);
    }
    // Top centering spigot.
    up(foot_h)
        cyl(h = 8, d = spigot_d, chamfer2 = 1.5, anchor = BOTTOM);
}
```

#### Pen & pencil cup with base tray

A round desktop pen cup with a slightly flared rounded rim and a wide chamfered base disc that doubles as a stability foot and a tiny clip/eraser ledge. Built from a BOSL2 tube on a chamfered cylinder so it prints floor-down with no supports. Tune the outer diameter, height, wall, floor thickness, and base flare.

```openscad
// Pen & pencil cup with base tray
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

cup_d     = 70;   // outer diameter of the cup body
cup_h     = 100;  // overall height
wall      = 2.4;  // wall thickness
floor_t   = 3;    // floor thickness
base_flare = 12;  // extra diameter added at the base foot
$fn = 96;

union() {
    // Wide chamfered base foot for stability, with a solid floor on top.
    cyl(h = floor_t + 4, d = cup_d + base_flare, chamfer1 = 1.2, rounding2 = 2, anchor = BOTTOM);
    // Hollow body wall standing on the base, rounded top rim.
    up(floor_t)
        tube(h = cup_h - floor_t, od = cup_d, wall = wall,
             orounding2 = 1.6, irounding2 = 1.6, anchor = BOTTOM);
}
```

#### Phone stand

A single-piece angled phone stand: a back support leaned at an angle with a front lip ledge that catches the phone, joined to a flat base for stability. Built by unioning a leaned slab, a base plate, and a front lip so it prints as one solid with no supports. Tune the width, lean angle, support height, base depth, and slab thickness.

```openscad
// Phone stand
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

stand_w    = 80;   // width across (phone width + margin)
lean       = 20;   // backward lean of the support from vertical, degrees
support_h  = 95;   // length of the leaning support slab
base_d     = 75;   // base plate depth on the desk
thick      = 8;    // slab / base thickness
lip_h      = 12;   // front catching lip height
$fn = 48;

union() {
    // Flat base plate for stability.
    cuboid([stand_w, base_d, thick], rounding = 2, except = [TOP, BOTTOM], anchor = BOTTOM);

    // Front catching lip running across the width near the base front edge.
    translate([0, -base_d / 2 + thick + 2, 0])
        cuboid([stand_w, thick, thick + lip_h], rounding = 2, except = [BOTTOM], anchor = BOTTOM);

    // Leaning back support: a slab tilted backward, rooted at the base.
    translate([0, -base_d / 2 + thick + 2 + thick, thick / 2])
        xrot(lean)
            cuboid([stand_w, thick, support_h], rounding = 2, except = [], anchor = BOTTOM);
}
```

#### Sticky-note tray

A desktop sticky-note holder: a shallow rounded tray sized for a square sticky pad, with a scooped front thumb cut-out so you can peel the top note off easily, and a solid floor. Carved by difference from one rounded block so the whole tray is a single watertight piece. Tune the pad size, wall, tray height, and floor thickness.

```openscad
// Sticky-note tray
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

pad_size  = 78;   // sticky pad edge length (76mm pad + clearance)
wall      = 3;    // wall thickness
tray_h    = 18;   // overall tray height
floor_t   = 2.5;  // floor thickness
scoop_d   = 30;   // diameter of the front peel scoop
round_r   = 4;    // outer corner rounding
$fn = 64;

body = pad_size + 2 * wall;

difference() {
    cuboid([body, body, tray_h], rounding = round_r, except = [TOP], anchor = BOTTOM);

    // Pad pocket.
    up(floor_t)
        cuboid([pad_size, pad_size, tray_h], rounding = max(round_r - wall, 0.5),
               except = [TOP], anchor = BOTTOM);

    // Front peel scoop: a sphere carved into the front wall + floor lip.
    translate([0, -body / 2, floor_t + 3])
        sphere(d = scoop_d);
}
```

#### Tablet stand

A wider, sturdier cousin of the phone stand sized for tablets: a leaned back support slab with a deep front trough ledge that cradles a tablet, joined to a heavy base plate for stability. Unioned from base, trough lips, and a tilted slab so it prints as one solid, ledge facing up, no supports. Tune the width, lean, support height, base depth, and trough gap.

```openscad
// Tablet stand
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

stand_w    = 180;  // width across (tablet width + margin)
lean       = 22;   // backward lean of the support from vertical, degrees
support_h  = 130;  // length of the leaning support slab
base_d     = 120;  // base plate depth on the desk
thick      = 10;   // slab / base thickness
trough_gap = 16;   // gap between front lip and support = tablet thickness slot
$fn = 48;

union() {
    // Heavy base plate.
    cuboid([stand_w, base_d, thick], rounding = 3, except = [TOP, BOTTOM], anchor = BOTTOM);

    // Front lip forming the outer wall of the cradle trough.
    translate([0, -base_d / 2 + thick + 4, 0])
        cuboid([stand_w, thick, thick + 22], rounding = 2, except = [BOTTOM], anchor = BOTTOM);

    // Leaning back support, set behind the trough gap.
    translate([0, -base_d / 2 + thick + 4 + thick + trough_gap, thick / 2])
        xrot(lean)
            cuboid([stand_w, thick, support_h], rounding = 3, anchor = BOTTOM);
}
```

#### Divided tray (3 compartments)

A shallow desk tray split into three equal compartments by two internal dividers, with rounded outer corners and a solid floor. Carve out each compartment from a rounded block so the whole tray prints as one watertight piece. Tune the footprint, height, wall thickness, and floor thickness.

```openscad
// Divided tray (3 compartments)
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

tray_w     = 120;  // outer width (X) — the long axis split into 3
tray_d     = 50;   // outer depth (Y)
tray_h     = 22;   // outer height (Z)
wall       = 2.4;  // outer wall + divider thickness
floor_t    = 2;    // floor thickness
round_r    = 4;    // outer corner rounding radius
$fn = 48;

compartments = 3;
// Total interior length available after the two outer walls.
inner_len = tray_w - 2 * wall;
// Each pocket width once the (compartments-1) dividers are removed.
pocket_w = (inner_len - (compartments - 1) * wall) / compartments;
pocket_d = tray_d - 2 * wall;
pocket_h = tray_h - floor_t;

difference() {
    cuboid([tray_w, tray_d, tray_h], rounding = round_r, except = [TOP]);
    // One open pocket per compartment, marching along X.
    for (i = [0 : compartments - 1]) {
        // Left edge of interior, then offset by each pocket + divider.
        x0 = -tray_w / 2 + wall + i * (pocket_w + wall);
        translate([x0 + pocket_w / 2, 0, -tray_h / 2 + floor_t])
            cuboid([pocket_w, pocket_d, pocket_h + 1],
                   rounding = max(round_r - wall, 0.5), except = [TOP], anchor = BOTTOM);
    }
}
```

#### Dovetail female socket

A flat plate with a sliding dovetail groove (the female half) bored through it by the BOSL2 joiners library. The matching dovetail male connector slides in for a self-locking joint. Tune the plate size, groove width, height, and slide length.

```openscad
// Dovetail female socket
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/joiners.scad>

plate      = [40, 40, 12];  // base plate size [x, y, z]
tail_w     = 20;            // dovetail width (wide reference dimension)
tail_h     = 8;            // dovetail height (groove depth)
slide      = 30;           // length of the groove along the slide axis
slope      = 5;            // flank slope (match the male part)
entry_slot = 17;           // open entry slot so the male part can drop in
$fn = 32;

diff()
  cuboid(plate)
    attach(TOP, BOTTOM, align = BACK, inside = true, inset = 5)
      tag("remove")
        dovetail("female", slide = slide, width = tail_w,
                 height = tail_h, slope = slope,
                 entry_slot_length = entry_slot);
```

#### Dovetail male connector

A flat connector plate with a sliding dovetail tongue (the male half) generated by the BOSL2 joiners library. Slides into the matching dovetail female socket for a self-locking woodworking-style joint. Tune the plate size, dovetail width, height, and slide length.

```openscad
// Dovetail male connector
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/joiners.scad>

plate      = [40, 30, 8];  // base plate size [x, y, z]
tail_w     = 20;           // dovetail width (wide reference dimension)
tail_h     = 8;            // dovetail height (how far it sticks out)
slide      = 30;           // length of the dovetail along the slide axis
slope      = 5;            // flank slope (higher = shallower angle)
$fn = 32;

cuboid(plate)
  attach(RIGHT, BOTTOM, align = BACK, inset = 5)
    dovetail("male", slide = slide, width = tail_w,
             height = tail_h, slope = slope);
```

#### Drawer organizer insert

A drop-in drawer insert with a grid of pockets for screws, stationery, or small parts. The whole tray is carved from a rounded block so it prints as one watertight piece — set the rows and columns to repartition the grid. Tune the footprint, height, grid size, wall thickness, and floor thickness.

```openscad
// Drawer organizer insert
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

org_w    = 140;  // outer width (X)
org_d    = 90;   // outer depth (Y)
org_h    = 28;   // outer height (Z)
cols     = 4;    // number of pockets across X
rows     = 3;    // number of pockets across Y
wall     = 2;    // outer wall + divider thickness
floor_t  = 2;    // floor thickness
round_r  = 3;    // outer corner rounding radius
$fn = 48;

// Interior span after the outer walls.
inner_w = org_w - 2 * wall;
inner_d = org_d - 2 * wall;
// Each pocket size once the internal dividers are removed.
pocket_w = (inner_w - (cols - 1) * wall) / cols;
pocket_d = (inner_d - (rows - 1) * wall) / rows;
pocket_h = org_h - floor_t;

difference() {
    cuboid([org_w, org_d, org_h], rounding = round_r, except = [TOP]);
    for (cx = [0 : cols - 1], ry = [0 : rows - 1]) {
        x0 = -org_w / 2 + wall + cx * (pocket_w + wall) + pocket_w / 2;
        y0 = -org_d / 2 + wall + ry * (pocket_d + wall) + pocket_d / 2;
        translate([x0, y0, -org_h / 2 + floor_t])
            cuboid([pocket_w, pocket_d, pocket_h + 1],
                   rounding = max(round_r - wall, 0.4), except = [TOP], anchor = BOTTOM);
    }
}
```

#### Flanged bushing

A plain bushing with an integral shoulder flange at one end, so it both press-fits into a bore and seats against the face of the housing to take axial load. Printed as one watertight solid (flange + sleeve share the same bore). Tune the sleeve OD to your bore, the flange diameter for the bearing face, the bore for your shaft, and the lengths.

```openscad
// Flanged bushing
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

sleeve_od  = 12;   // sleeve outer diameter (press-fit)
flange_d   = 18;   // flange outer diameter (seating face)
bore_d     = 8;    // shaft bore diameter
sleeve_len = 14;   // sleeve length below the flange
flange_t   = 3;    // flange thickness
$fn = 96;

difference() {
    union() {
        // flange disc at the bottom
        cyl(h = flange_t, d = flange_d, chamfer2 = 0.8, anchor = BOTTOM);
        // sleeve rising from the top of the flange
        up(flange_t)
            cyl(h = sleeve_len, d = sleeve_od, chamfer2 = 0.8, anchor = BOTTOM);
    }
    // through bore
    down(0.5)
        cyl(h = flange_t + sleeve_len + 1, d = bore_d, anchor = BOTTOM);
}
```

#### Flat-belt pulley with bore

A crowned flat-belt pulley with a bored hub to run on a shaft. The slight barrel crown across the face keeps a flat belt tracked to the centre, and the bore lets it ride on a shaft or bushing. Printed as one watertight solid. Tune the rim diameter, crown, face width, and bore.

```openscad
// Flat-belt pulley with bore
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

rim_d    = 28;   // pulley diameter at the edges of the face
crown    = 1.2;  // extra radius at the centre of the crown
face_w   = 14;   // axial width of the belt face
bore_d   = 8;    // center bore diameter
$fn = 120;

mid_d = rim_d + 2 * crown;   // diameter at the crowned centre

difference() {
    // crowned body of revolution
    rotate_extrude($fn = 120)
        polygon([
            [bore_d/2,  0],
            [rim_d/2,   0],
            [mid_d/2,   face_w/2],
            [rim_d/2,   face_w],
            [bore_d/2,  face_w],
        ]);
    // through bore
    down(1)
        cylinder(h = face_w + 2, d = bore_d, $fn = 96);
}
```

#### Friction-fit box lid

A press-on lid with a downward inner lip that slides snugly inside a matching box opening, plus a finger-grip recess on top. Sized to pair with a rounded box base; adjust the clearance for a looser or tighter fit. Tune the opening size, lip depth, top thickness, and rounding.

```openscad
// Friction-fit box lid
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

open_w    = 80;   // box outer width the lid sits over (X)
open_d    = 55;   // box outer depth the lid sits over (Y)
top_t     = 2.4;  // lid top plate thickness
lip_h     = 8;    // depth of the inner lip
lip_wall  = 2;    // lip wall thickness
clearance = 0.3;  // gap per side for a friction fit
round_r   = 5;    // outer corner rounding radius
$fn = 64;

// Inner opening the lip must drop into (box inner cavity).
inner_w = open_w - 2 * 2.4 - 2 * clearance;
inner_d = open_d - 2 * 2.4 - 2 * clearance;

union() {
    // Top cap plate, rounded only on the vertical corners to match the box footprint.
    cuboid([open_w, open_d, top_t], rounding = round_r, edges = "Z", anchor = BOTTOM);
    // Hollow downward lip that grips the inside of the box.
    down(lip_h)
        difference() {
            cuboid([inner_w, inner_d, lip_h], rounding = max(round_r - 2.4, 0.5), edges = "Z", anchor = BOTTOM);
            up(0.01)
                cuboid([inner_w - 2 * lip_wall, inner_d - 2 * lip_wall, lip_h + 0.02],
                       rounding = max(round_r - 2.4 - lip_wall, 0.4), edges = "Z", anchor = BOTTOM);
        }
}
```

#### Hose guide stake

A ground stake topped with a smooth vertical roller post that steers a garden hose around bed corners without crushing plants. The post, collar, and pointed spike fuse into one printable solid. Tune the spike length, post height/diameter, and base collar.

```openscad
// Hose guide stake
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

spike_len   = 70;  // length of the pointed ground spike
spike_d     = 14;  // spike diameter at the top (tapers to a point)
post_h      = 90;  // height of the hose-guiding post above ground
post_d      = 16;  // post diameter
collar_d    = 34;  // diameter of the base collar / soil stop
collar_h    = 6;   // collar thickness
$fn = 64;

union() {
    // Pointed ground spike, point down.
    down(spike_len)
        cyl(h = spike_len, d1 = 0.5, d2 = spike_d, anchor = BOTTOM);
    // Base collar / soil stop sitting at ground level.
    cyl(h = collar_h, d = collar_d, rounding = 2, anchor = BOTTOM);
    // The guiding post with a rounded cap so the hose slides freely.
    up(collar_h - 0.01)
        cyl(h = post_h, d = post_d, rounding2 = post_d / 2 - 0.5,
            anchor = BOTTOM);
}
```

#### Engraved plant label stake

A flat plant label on a pointed push-in stake, with the plant name engraved into the face via difference so it prints as one connected solid. Tune the label size, stake length, plate thickness, engrave depth, and the text.

```openscad
// Engraved plant label stake
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

label_text   = "BASIL";  // the name to engrave
plate_w      = 60;       // label plate width (X)
plate_h      = 22;       // label plate height (Z of the face)
plate_t      = 3;        // plate thickness (Y)
stake_len    = 45;       // length of the pointed push-in stake below the plate
font_size    = 11;       // engraved text size
engrave_d    = 1.2;      // how deep the text is cut into the face
$fn = 48;

difference() {
    union() {
        // The flat label plate. Rounding must stay under half the thin Y
        // dimension, so keep it modest to avoid BOSL2's size assertion.
        cuboid([plate_w, plate_t, plate_h], rounding = plate_t / 2 - 0.2,
               edges = "Z", anchor = BOTTOM);
        // Pointed stake hanging below the plate (a downward wedge).
        down(stake_len)
            prismoid(size1 = [4, plate_t], size2 = [14, plate_t],
                     h = stake_len, anchor = BOTTOM);
    }
    // Engrave the text into the front face (+Y side).
    up(plate_h / 2)
        fwd(plate_t / 2 - engrave_d)
            xrot(90)
                linear_extrude(height = engrave_d + 0.5)
                    text(label_text, size = font_size, halign = "center",
                         valign = "center", font = "Liberation Sans:style=Bold");
}
```

#### Plant pot with drainage holes

A tapered round plant pot with a solid floor pierced by a ring of drainage holes. Carved from a tapered tube so it prints as one watertight shell. Tune the top/bottom diameters, height, wall, floor thickness, and drainage hole count/size.

```openscad
// Plant pot with drainage holes
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

pot_top_d   = 90;  // outer diameter at the rim (X/Y)
pot_bot_d   = 70;  // outer diameter at the base — smaller for a taper
pot_h       = 80;  // overall height (Z)
wall        = 3;   // side-wall thickness
floor_t     = 4;   // floor thickness
drain_n     = 6;   // number of drainage holes around the centre
drain_d     = 7;   // diameter of each drainage hole
$fn = 96;

// Drainage ring sits inside the base footprint, clear of the wall.
drain_ring_r = (pot_bot_d / 2 - wall) * 0.55;

difference() {
    // Solid tapered body (cone frustum).
    cyl(h = pot_h, d1 = pot_bot_d, d2 = pot_top_d, anchor = BOTTOM);
    // Hollow the inside, leaving the floor.
    up(floor_t)
        cyl(h = pot_h, d1 = pot_bot_d - 2 * wall, d2 = pot_top_d - 2 * wall,
            anchor = BOTTOM);
    // Drainage holes through the floor.
    for (i = [0 : drain_n - 1]) {
        zrot(i * 360 / drain_n)
            right(drain_ring_r)
                cyl(h = floor_t + 2, d = drain_d, anchor = BOTTOM, $fn = 32);
    }
}
```

#### Pot saucer / drip tray

A shallow round saucer that catches drainage water under a plant pot, with a slightly raised inner lip and rounded outer edge. Carved from a rounded disc so it prints as one watertight piece. Tune the outer diameter, height, wall, and floor thickness.

```openscad
// Pot saucer / drip tray
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

saucer_d   = 110;  // outer diameter (X/Y) — make ~20mm bigger than the pot base
saucer_h   = 18;   // overall height (Z)
wall       = 3;    // side-wall thickness
floor_t    = 3;    // floor thickness
edge_r     = 3;    // rounding radius on the outer rim
$fn = 120;

difference() {
    // Solid disc with rounded top + bottom outer edges.
    cyl(h = saucer_h, d = saucer_d, rounding = edge_r, anchor = BOTTOM);
    // Carve the water-catching basin, leaving floor + walls.
    up(floor_t)
        cyl(h = saucer_h, d = saucer_d - 2 * wall,
            rounding2 = max(edge_r - wall, 0.5), anchor = BOTTOM);
}
```

#### Row-marker sign on stake

A small angled signboard on a flat ground stake for labelling vegetable rows, with the crop name engraved into the tilted face via difference so it reads from above. The board, stake, and point fuse into one printable solid. Tune the board size, stake length, tilt, and the text.

```openscad
// Row-marker sign on stake
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

sign_text   = "CARROTS";  // crop name to engrave
board_w     = 70;         // signboard width (X)
board_h     = 26;         // signboard height (Y, before the tilt)
board_t     = 4;          // board thickness
stake_len   = 60;         // length of the flat ground stake
stake_w     = 16;         // stake width
font_size   = 11;         // engraved text size
engrave_d   = 1.2;        // how deep the text is cut
$fn = 48;

union() {
    // Flat ground stake with a pointed tip at the bottom.
    down(stake_len)
        prismoid(size1 = [4, board_t], size2 = [stake_w, board_t],
                 h = stake_len, anchor = BOTTOM);
    // Tilted signboard sitting on top of the stake, engraved on its face.
    // Sink it low enough that the board overlaps the stake top into one shell.
    up(board_h / 2 - 12)
        xrot(-20)
            difference() {
                cuboid([board_w, board_h, board_t],
                       rounding = board_t / 2 - 0.2, edges = "Y");
                // Engrave the crop name into the +Z face.
                up(board_t / 2 - engrave_d)
                    linear_extrude(height = engrave_d + 0.5)
                        text(sign_text, size = font_size, halign = "center",
                             valign = "center",
                             font = "Liberation Sans:style=Bold");
            }
}
```

#### Seed-starting tray (2x2 cells)

A compact seed-starting tray with a 2x2 grid of tapered cells, each with its own drainage hole, joined by a shared rim so the whole tray lifts as one piece. Carved from a solid block so it prints watertight. Tune the cell count per side, cell size/depth, wall, and drain hole size.

```openscad
// Seed-starting tray (2x2 cells)
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

cells       = 2;   // cells per side (2 -> a 2x2 tray)
cell_top    = 34;  // inner width of each cell at the top
cell_depth  = 40;  // cell depth (Z)
wall        = 2.4; // wall between/around cells
floor_t     = 2;   // floor thickness under each cell
drain_d     = 5;   // drainage hole diameter per cell
$fn = 48;

taper      = 6;             // how much narrower the cell base is per side
pitch      = cell_top + wall;
tray_w     = cells * cell_top + (cells + 1) * wall;
tray_h     = cell_depth + floor_t;

difference() {
    // Solid outer block with rounded vertical corners.
    cuboid([tray_w, tray_w, tray_h], rounding = 3, edges = "Z",
           anchor = BOTTOM);
    // Carve each tapered cell.
    for (ix = [0 : cells - 1], iy = [0 : cells - 1]) {
        cx = -tray_w / 2 + wall + cell_top / 2 + ix * pitch;
        cy = -tray_w / 2 + wall + cell_top / 2 + iy * pitch;
        translate([cx, cy, floor_t])
            prismoid(size1 = [cell_top - taper, cell_top - taper],
                     size2 = [cell_top, cell_top],
                     h = cell_depth + 1, rounding = 2, anchor = BOTTOM);
        // Drainage hole through the floor of that cell.
        translate([cx, cy, -1])
            cyl(h = floor_t + 2, d = drain_d, anchor = BOTTOM, $fn = 24);
    }
}
```

#### Trellis / vine clip

A C-shaped open clip that snaps onto a trellis wire or thin stake and gently holds a plant stem in a second loop, training climbers without ties. Built as one wedge-and-ring body so it prints connected. Tune the wire and stem diameters, the snap-gap, wall thickness, and clip depth.

```openscad
// Trellis / vine clip
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

wire_d    = 5;    // diameter of the trellis wire / stake the clip grips
stem_d    = 9;    // diameter of the plant stem the clip cradles
wall      = 2.4;  // wall thickness of both rings
gap_frac  = 0.55; // snap-opening as a fraction of the inner diameter
depth     = 12;   // extrusion depth (clip width along Z)
$fn = 64;

wire_or = wire_d / 2 + wall;
stem_or = stem_d / 2 + wall;
spacing = wire_or + stem_or - wall;  // overlap so the two rings merge

// A single open C-ring: a full ring minus a wedge slice for the snap mouth.
module c_ring(inner_d, w, open_frac) {
    half = asin(min(1, max(0, open_frac)) * inner_d / 2 / (inner_d / 2 + w));
    difference() {
        tube(h = depth, id = inner_d, wall = w, anchor = BOTTOM);
        // Cut the mouth opening facing +X.
        rotate_extrude(angle = 2 * half)
            zrot(-half)
            right(inner_d / 2 - 0.5)
                square([inner_d / 2 + w + 1, depth + 2]);
    }
}

union() {
    // Wire-gripping C-clip, mouth facing outward (+X).
    c_ring(wire_d, wall, gap_frac);
    // Stem-cradling C-clip, mouth facing the other way (-X), offset along X.
    right(spacing)
        zrot(180)
            c_ring(stem_d, wall, gap_frac);
}
```

#### Bottle watering spike

A slow-release watering spike that screws into a standard PET bottle neck and drips into the soil through a hollow pointed stake with side weep holes. Bored hollow with a through-channel so water flows; prints as one shell. Tune the bottle-neck diameter, spike length, bore, and weep holes.

```openscad
// Bottle watering spike
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

neck_d     = 25;  // outer diameter of the bottle-neck collar
neck_h     = 18;  // collar height that grips the inverted bottle neck
spike_len  = 75;  // length of the soil spike below the collar
spike_d    = 18;  // spike diameter at the collar (tapers to a tip)
bore_d     = 6;   // inner water channel diameter
weep_d     = 2.5; // diameter of the side weep holes
$fn = 64;

difference() {
    union() {
        // Collar that slips over / threads into the bottle neck.
        cyl(h = neck_h, d = neck_d, rounding = 2, anchor = BOTTOM);
        // Tapered soil spike pointing down. Overlap into the collar so the
        // two bodies fuse into one shell.
        down(spike_len)
            cyl(h = spike_len + 2, d1 = 2, d2 = spike_d, anchor = BOTTOM);
    }
    // Hollow water channel, open at the top. It stops part-way down where the
    // spike is still safely wider than the bore, so the tapered tip stays a
    // connected solid plug (no severed floating piece).
    bore_stop = 35;  // distance the bore reaches below the collar
    down(bore_stop)
        cyl(h = bore_stop + neck_h + 1, d = bore_d, anchor = BOTTOM);
    // Side weep holes that connect the bore to the soil, kept inside the
    // bored region and shorter than the spike so they don't sever a ring.
    for (i = [0 : 3]) {
        zrot(i * 90)
            down(bore_stop - 8)
                right(spike_d / 6)
                    xrot(90)
                        cyl(h = spike_d * 0.7, d = weep_d, anchor = CENTER,
                            $fn = 20);
    }
}
```

#### GT2 timing pulley

A GT2 timing-belt pulley with 2 mm pitch tooth grooves and a flange on each end to keep the belt tracking. The default 20-tooth size is the common 3D-printer X/Y pulley. Tune the tooth count, bore, belt width, and flange size. Add a flat or grub-screw later if you need to lock it to a shaft.

```openscad
// GT2 timing pulley
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

teeth    = 20;    // number of belt teeth (20 = standard printer pulley)
bore_d   = 5;     // shaft bore diameter in mm
belt_w   = 6;     // belt width / toothed band height in mm
flange_h = 1;     // flange thickness at each end in mm
flange_o = 1.4;   // how far each flange sticks out past the tip radius in mm
$fn = 96;

pitch       = 2;                       // GT2 pitch = 2 mm
pitch_d     = teeth * pitch / PI;      // pitch diameter
groove_r    = 0.555;                   // GT2 groove radius (tooth valley)
groove_off  = 0.254;                   // groove center offset below pitch line
// outer (tip) radius of the toothed band
tip_r       = pitch_d / 2 + 0.15;
// radius the groove centers sit on
groove_ctr_r = pitch_d / 2 + groove_off + groove_r - 0.40;
flange_r    = tip_r + flange_o;

union() {
    difference() {
        union() {
            // toothed band
            cylinder(h = belt_w, r = tip_r, center = false);
            // bottom flange
            cylinder(h = flange_h, r = flange_r, center = false);
            // top flange
            translate([0, 0, belt_w - flange_h])
                cylinder(h = flange_h, r = flange_r, center = false);
        }
        // cut the GT2 tooth grooves into the band
        for (i = [0 : teeth - 1]) {
            ang = i * 360 / teeth;
            rotate([0, 0, ang])
                translate([groove_ctr_r, 0, -1])
                    cylinder(h = belt_w + 2, r = groove_r, center = false, $fn = 24);
        }
        // center bore
        translate([0, 0, -1])
            cylinder(h = belt_w + 2, d = bore_d, center = false);
    }
}
```

#### Right-angle gusset bracket

A heavy-duty 90 degree angle bracket: two square bolt-down plates meeting at a right angle, braced by a full-height central triangular gusset web so it resists bending under load. Each plate carries a bolt hole. Fuses into one watertight solid that prints with the inside corner on the bed. Tune the plate size, thickness, gusset thickness, and bolt-hole diameter.

```openscad
// Right-angle gusset bracket
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

plate    = 40;   // square plate side length
thick    = 5;    // plate thickness
gusset_t = 6;    // gusset web thickness
hole_d   = 5;    // bolt-hole diameter
$fn = 48;

difference() {
    union() {
        // Bottom plate on the bed.
        cuboid([plate, plate, thick], anchor = BOTTOM + LEFT);
        // Upright plate at the left edge.
        cuboid([thick, plate, plate], anchor = BOTTOM + LEFT);
        // Central triangular gusset web in the X-Z plane, spanning both plates.
        translate([0, -gusset_t / 2, 0])
            linear_extrude(height = gusset_t)
                polygon([[thick, thick], [plate, thick], [thick, plate]]);
    }
    // Hole in the bottom plate.
    translate([plate * 0.7, plate / 2, thick / 2])
        cyl(d = hole_d, h = thick + 2, anchor = CENTER);
    // Hole in the upright plate.
    translate([thick / 2, plate / 2, plate * 0.7]) yrot(90)
        cyl(d = hole_d, h = thick + 2, anchor = CENTER);
}
```

#### Heat-set insert boss

A mounting boss with a tapered pilot bore sized for a brass heat-set threaded insert, plus a chamfered lead-in so the insert seats square. Built from the BOSL2 shape library with rounded fillets for strength. A single printable solid. Tune the boss size and the insert pilot diameters.

```openscad
// Heat-set insert boss
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/shapes3d.scad>

boss_d      = 12;    // outside diameter of the boss (mm)
boss_h      = 10;    // boss height (mm)
insert_top  = 5.0;   // pilot diameter at the mouth (mm) — match your insert's body
insert_tip  = 4.6;   // pilot diameter at the bottom (mm) — slight taper grips the melt
insert_h    = 8;     // depth of the insert pilot bore (mm)
$fn = 64;

difference() {
    // Solid boss with a rounded base fillet for a strong joint.
    cyl(d = boss_d, h = boss_h, rounding1 = 1.5, chamfer2 = 0.8, anchor = BOTTOM);
    // Tapered pilot bore for the heat-set insert, open at the top.
    up(boss_h - insert_h)
        cyl(d1 = insert_tip, d2 = insert_top, h = insert_h + 0.02,
            chamfer2 = 0.6, anchor = BOTTOM);
}
```

#### Herringbone gear

A double-helical (herringbone) spur gear. The mirrored helical teeth cancel axial thrust while running smoother and quieter than straight-cut teeth, and the chevron pattern prints cleanly without supports. Tune the module, tooth count, thickness, helix angle, and bore.

```openscad
// Herringbone gear
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/gears.scad>

teeth      = 20;   // number of teeth
gear_mod   = 2;    // tooth module in mm
thickness  = 12;   // gear plate thickness in mm
helix_ang  = 30;   // helix angle in degrees
shaft_d    = 6;    // center shaft bore diameter in mm
$fn = 64;

spur_gear(mod = gear_mod, teeth = teeth, thickness = thickness,
          shaft_diam = shaft_d, helical = helix_ang, herringbone = true,
          pressure_angle = 20);
```

#### Bag / chip-bag sealing clip

A springy C-shaped clip that pinches a folded bag closed. Two parallel jaws join at a solid left spine; the narrow mouth gap at the tips lets the printed plastic flex to grip. Built as a union of the spine plus two jaws so it prints flat as one connected piece. Tune the jaw length, width, gap, and spine thickness.

```openscad
// Bag / chip-bag sealing clip
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

jaw_len   = 70;   // length of the gripping jaws (X)
clip_w    = 16;   // width across the clip (Y)
jaw_t     = 3;    // thickness of each jaw
gap       = 1.4;  // mouth gap between jaw tips (the grip)
spine_h   = 14;   // inner height between the jaws at the spine
$fn = 48;

spine_t = 4;  // thickness of the closed hinge spine on the left
outer_h = spine_h + 2 * jaw_t;
full_l  = jaw_len + spine_t;

// Profile in X (along jaws) / Z (height): a left spine column plus a top and a
// bottom jaw. The jaws angle inward at the tips by (spine_h-gap)/2 so the mouth
// pinches to the gap. Union keeps everything one connected ring.
tip_dy = (spine_h - gap) / 2;  // how far each jaw tip closes in toward center

rotate([90, 0, 0])
linear_extrude(height = clip_w, center = true)
    union() {
        // Left spine joining both jaws.
        left((full_l - spine_t) / 2)
            rect([spine_t, outer_h], rounding = 1);
        // Top jaw, tapering inward toward the tip.
        polygon([
            [-full_l / 2 + spine_t * 0.5, spine_h / 2],
            [ full_l / 2,                 gap / 2],
            [ full_l / 2,                 gap / 2 + jaw_t],
            [-full_l / 2 + spine_t * 0.5, spine_h / 2 + jaw_t],
        ]);
        // Bottom jaw, mirrored.
        polygon([
            [-full_l / 2 + spine_t * 0.5, -spine_h / 2 - jaw_t],
            [ full_l / 2,                 -gap / 2 - jaw_t],
            [ full_l / 2,                 -gap / 2],
            [-full_l / 2 + spine_t * 0.5, -spine_h / 2],
        ]);
    }
```

#### Beveled drink coaster

A solid round drink coaster with a beveled top edge and a shallow recessed well to catch condensation drips, plus a fine bottom chamfer for a clean first layer. Tune the diameter, thickness, bevel, and well depth.

```openscad
// Beveled drink coaster
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

coaster_d  = 95;   // outer diameter
coaster_h  = 8;    // overall thickness
top_bevel  = 3;    // chamfer on the top outer edge
well_depth = 1.6;  // depth of the central condensation well
well_inset = 8;    // ring of flat rim left around the well
$fn = 128;

difference() {
    // Solid disc, top edge beveled, base lightly chamfered for first-layer adhesion.
    cyl(h = coaster_h, d = coaster_d,
        chamfer2 = top_bevel, chamfer1 = 0.8, anchor = BOTTOM);
    // Shallow recessed well on top to hold drips, with a soft rounded lip.
    up(coaster_h - well_depth)
        cyl(h = well_depth + 1, d = coaster_d - 2 * (top_bevel + well_inset),
            rounding1 = 1.2, anchor = BOTTOM);
}
```

#### Fridge note clip with magnet pocket

A small spring clip for pinning notes to a fridge. Two tapering jaws join at a solid spine and pinch to a narrow mouth gap to grip paper; a round pocket is carved into the back of the spine to glue in a magnet. Built as a union minus the pocket so it prints as one connected piece. Tune the jaw size, gap, and magnet diameter/depth.

```openscad
// Fridge note clip with magnet pocket
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

body_w    = 28;   // width of the clip (Y)
jaw_len   = 30;   // length of the jaws (X)
jaw_t     = 2.8;  // thickness of each jaw
gap       = 1.0;  // mouth gap at the jaw tips
mouth_h   = 8;    // inner height between jaws at the spine
mag_d     = 12;   // magnet diameter
mag_depth = 2.5;  // magnet pocket depth
$fn = 64;

spine_t = 6;  // thickness of the closed spine (holds the magnet)
outer_h = mouth_h + 2 * jaw_t;
full_l  = jaw_len + spine_t;

difference() {
    // C-clip body: union of spine + two tapering jaws, extruded along Y.
    rotate([90, 0, 0])
    linear_extrude(height = body_w, center = true)
        union() {
            left((full_l - spine_t) / 2)
                rect([spine_t, outer_h], rounding = 1);
            polygon([
                [-full_l / 2 + spine_t * 0.5,  mouth_h / 2],
                [ full_l / 2,                  gap / 2],
                [ full_l / 2,                  gap / 2 + jaw_t],
                [-full_l / 2 + spine_t * 0.5,  mouth_h / 2 + jaw_t],
            ]);
            polygon([
                [-full_l / 2 + spine_t * 0.5, -mouth_h / 2 - jaw_t],
                [ full_l / 2,                 -gap / 2 - jaw_t],
                [ full_l / 2,                 -gap / 2],
                [-full_l / 2 + spine_t * 0.5, -mouth_h / 2],
            ]);
        }
    // Magnet pocket bored into the back (left) face of the spine, toward -X.
    translate([-full_l / 2 - 0.01, 0, 0])
        rotate([0, 90, 0])
            cyl(h = mag_depth + 0.02, d = mag_d, anchor = TOP);
}
```

#### Napkin ring

A simple round napkin ring — a short tube with softly rounded inner and outer rims so it slides over a rolled napkin without catching. Prints flat on its rim with no supports. Tune the inner diameter, wall thickness, and height.

```openscad
// Napkin ring
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

inner_d = 42;   // inner diameter (napkin bundle size)
wall    = 4;    // ring wall thickness
ring_h  = 30;   // ring height
rim_r   = 2;    // rounding on the top and bottom rims
$fn = 128;

tube(h = ring_h, id = inner_d, wall = wall,
     orounding1 = rim_r, orounding2 = rim_r,
     irounding1 = rim_r, irounding2 = rim_r,
     anchor = BOTTOM);
```

#### Soap dish with drain slots

A rounded-rectangle soap dish with a raised lip and a floor cut by parallel drain slots so water runs off and the bar dries. The slotted floor keeps everything one watertight print. Tune the footprint, height, wall, and slot count/width.

```openscad
// Soap dish with drain slots
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

dish_w   = 110;  // outer width (X)
dish_d   = 75;   // outer depth (Y)
dish_h   = 20;   // outer height (Z)
wall     = 2.6;  // outer wall thickness
floor_t  = 2.2;  // slotted floor thickness
round_r  = 6;    // outer corner rounding
slots    = 7;    // number of drain slots
slot_w   = 4;    // width of each drain slot
$fn = 64;

include <BOSL2/std.scad>

inner_w = dish_w - 2 * wall;
inner_d = dish_d - 2 * wall;
pitch   = inner_w / slots;

difference() {
    // Hollow rounded tray.
    difference() {
        cuboid([dish_w, dish_d, dish_h], rounding = round_r, except = [TOP]);
        up(floor_t)
            cuboid([inner_w, inner_d, dish_h], rounding = max(round_r - wall, 1),
                   except = [TOP], anchor = BOTTOM);
    }
    // Drain slots cut through the floor, running along Y.
    for (i = [0 : slots - 1]) {
        x = -inner_w / 2 + pitch / 2 + i * pitch;
        translate([x, 0, -dish_h / 2 - 1])
            cuboid([slot_w, inner_d - 8, floor_t + 2], rounding = slot_w / 2 - 0.2,
                   edges = [FRONT, BACK], anchor = BOTTOM);
    }
}
```

#### Sink sponge holder

A sink-side sponge tray with tall slotted side walls for airflow and a slotted floor for drainage, so a wet sponge dries from every side. Walls and floor are cut from a rounded open box so it stays one watertight print. Tune the footprint, height, wall, and slot spacing.

```openscad
// Sink sponge holder
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

tray_w   = 130;  // outer width (X)
tray_d   = 75;   // outer depth (Y)
tray_h   = 45;   // outer height (Z)
wall     = 2.6;  // wall thickness
floor_t  = 2.2;  // floor thickness
round_r  = 5;    // outer corner rounding
slot_w   = 6;    // width of the ventilation/drain slots
$fn = 48;

inner_w = tray_w - 2 * wall;
inner_d = tray_d - 2 * wall;

// Slots along the long walls (cut in X-direction openings through Y walls).
nx = floor(inner_w / (slot_w * 2));
pitch_x = inner_w / nx;
// Floor drain slots running along X.
nf = floor(inner_d / (slot_w * 2));
pitch_f = inner_d / nf;

difference() {
    // Hollow rounded tray.
    difference() {
        cuboid([tray_w, tray_d, tray_h], rounding = round_r, except = [TOP]);
        up(floor_t)
            cuboid([inner_w, inner_d, tray_h], rounding = max(round_r - wall, 1),
                   except = [TOP], anchor = BOTTOM);
    }
    // Vertical vent slots through both long (front/back) walls.
    for (i = [0 : nx - 1]) {
        x = -inner_w / 2 + pitch_x / 2 + i * pitch_x;
        translate([x, 0, floor_t + (tray_h - floor_t) / 2])
            cuboid([slot_w, tray_d + 2, tray_h - floor_t - 8],
                   rounding = slot_w / 2 - 0.2, edges = [TOP, BOTTOM]);
    }
    // Floor drain slots running along X.
    for (j = [0 : nf - 1]) {
        y = -inner_d / 2 + pitch_f / 2 + j * pitch_f;
        translate([0, y, -tray_h / 2 - 1])
            cuboid([inner_w - 10, slot_w, floor_t + 2],
                   rounding = min(slot_w / 2 - 0.2, (floor_t + 2) / 2 - 0.4),
                   edges = [LEFT, RIGHT], anchor = BOTTOM);
    }
}
```

#### Toothbrush holder

A round bathroom cup with a perforated top deck — drilled holes hold toothbrushes upright while the open base below lets water drain to the floor of the cup. One watertight print with a chamfered base. Tune the diameter, height, hole count, and hole size.

```openscad
// Toothbrush holder
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

cup_d    = 70;   // outer diameter
cup_h    = 100;  // overall height
wall     = 2.6;  // wall thickness
floor_t  = 3;    // base floor thickness
deck_t   = 4;    // thickness of the perforated top deck
holes    = 4;    // number of brush holes
hole_d   = 13;   // diameter of each brush hole
$fn = 96;

ring_r = cup_d / 4;  // radius of the hole circle

difference() {
    union() {
        // Outer wall.
        up(floor_t)
            tube(h = cup_h - floor_t, od = cup_d, wall = wall, anchor = BOTTOM);
        // Solid floor.
        cyl(h = floor_t, d = cup_d, chamfer1 = 0.8, anchor = BOTTOM);
        // Top perforated deck spanning the bore.
        up(cup_h - deck_t)
            cyl(h = deck_t, d = cup_d - 0.01, anchor = BOTTOM);
    }
    // Brush holes through the deck.
    for (i = [0 : holes - 1]) {
        a = i * 360 / holes;
        translate([ring_r * cos(a), ring_r * sin(a), cup_h - deck_t - 1])
            cyl(h = deck_t + 2, d = hole_d, anchor = BOTTOM);
    }
}
```

#### Cutlery caddy (4 bays)

A countertop cutlery caddy split into four open bays by internal dividers, with rounded corners and a slotted floor so rinse water drains away. Pockets and slots are carved from a rounded block so it prints as one watertight piece. Tune the footprint, height, walls, and bay count.

```openscad
// Cutlery caddy (4 bays)
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

caddy_w  = 150;  // outer width (X) — split into bays
caddy_d  = 65;   // outer depth (Y)
caddy_h  = 110;  // outer height (Z)
wall     = 2.6;  // outer wall + divider thickness
floor_t  = 2.4;  // floor thickness
round_r  = 5;    // outer corner rounding
bays     = 4;    // number of cutlery bays
$fn = 48;

inner_w  = caddy_w - 2 * wall;
pocket_w = (inner_w - (bays - 1) * wall) / bays;
pocket_d = caddy_d - 2 * wall;
pocket_h = caddy_h - floor_t;

difference() {
    cuboid([caddy_w, caddy_d, caddy_h], rounding = round_r, except = [TOP]);
    // Open bay per compartment.
    for (i = [0 : bays - 1]) {
        x0 = -caddy_w / 2 + wall + i * (pocket_w + wall);
        translate([x0 + pocket_w / 2, 0, -caddy_h / 2 + floor_t])
            cuboid([pocket_w, pocket_d, pocket_h + 1],
                   rounding = max(round_r - wall, 0.6), except = [TOP], anchor = BOTTOM);
        // Two drain slots through the floor of each bay.
        for (s = [-1, 1])
            translate([x0 + pocket_w / 2 + s * pocket_w / 4, 0, -caddy_h / 2 - 1])
                cuboid([3.5, pocket_d - 10, floor_t + 2], rounding = 1.5,
                       edges = [FRONT, BACK], anchor = BOTTOM);
    }
}
```

#### Idler pulley with bore

A grooved idler/guide pulley for round belt or cord, with a center bore to run on a shaft or bushing. The V-groove keeps the belt tracked; two flanges contain it. Printed as one watertight solid. Tune the outer diameter, groove depth, width, and bore to suit your belt and shaft.

```openscad
// Idler pulley with bore
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

outer_d    = 24;   // flange (rim) outer diameter
groove_d   = 18;   // diameter at the bottom of the V-groove
width      = 10;   // overall axial width
bore_d     = 6;    // center bore diameter
flange_t   = 2;    // thickness of each outer flange
$fn = 120;

groove_w = width - 2 * flange_t;   // axial width of the groove mouth

difference() {
    // solid body of revolution: two flanges + V-groove between them
    rotate_extrude($fn = 120)
        polygon([
            [bore_d/2,        0],
            [outer_d/2,       0],
            [outer_d/2,       flange_t],
            [groove_d/2,      width/2],
            [outer_d/2,       width - flange_t],
            [outer_d/2,       width],
            [bore_d/2,        width],
        ]);
    // clean through bore
    down(1)
        cylinder(h = width + 2, d = bore_d, $fn = 96);
}
```

#### Knuckle hinge leaf

One leaf of a classic knuckle (butt) hinge — a flat plate with rounded barrel knuckles and a through pin bore, built from BOSL2 attachable primitives as a single printable solid. Print two and join with a pin rod. Tune the leaf size, knuckle count, barrel diameter, and pin bore.

```openscad
// Knuckle hinge leaf
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

leaf_w     = 40;   // leaf width along the hinge axis
leaf_d     = 25;   // leaf depth (away from the barrel)
leaf_t     = 3;    // leaf plate thickness
barrel_d   = 9;    // outer diameter of the barrel knuckles
pin_d      = 3.2;  // pin bore diameter through the knuckles
knuckles   = 3;    // number of knuckles on this leaf
gap        = 0.4;  // gap between this leaf's knuckles (for the mating leaf)
$fn = 48;

barrel_r  = barrel_d / 2;
seg_pitch = leaf_w / (2 * knuckles - 1);   // knuckle + matching gap

difference() {
  union() {
    // Flat leaf plate; barrel axis runs along X, plate reaches back from it
    translate([0, barrel_r, 0])
      cuboid([leaf_w, leaf_d, leaf_t]);
    // Knuckle barrels along the X axis, overlapping the plate front edge
    for (i = [0 : knuckles - 1]) {
      xpos = -leaf_w/2 + seg_pitch/2 + i * 2 * seg_pitch;
      translate([xpos, 0, 0])
        xcyl(l = seg_pitch - gap, d = barrel_d);
    }
  }
  // Pin bore through every knuckle
  xcyl(l = leaf_w + 2, d = pin_d);
}
```

#### Knurled knob (threaded bore)

A grippy diamond-knurled control knob with an internally threaded blind bore so it screws onto a bolt or shaft, generated by the BOSL2 cylinder texture + threading libraries. A single printable solid. Tune the knob diameter, height, thread size and pitch.

```openscad
// Knurled knob (threaded bore)
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/threading.scad>

knob_d   = 30;    // outside diameter of the knob (mm)
knob_h   = 16;    // overall knob height (mm)
thread_d = 8;     // bore thread major diameter (mm)
pitch    = 1.25;  // bore thread pitch (mm)
bore_h   = 12;    // depth of the threaded bore (mm)
$fn = 96;

difference() {
    // Knurled grip body with a domed-ish flat top.
    cyl(d = knob_d, h = knob_h, rounding2 = 3, chamfer1 = 1,
        texture = "diamonds", tex_size = [3, 3], tex_depth = 0.9,
        anchor = BOTTOM);
    // Internal thread, cut from the bottom up.
    up(-0.01)
        threaded_nut_hole();
}

module threaded_nut_hole() {
    // Female thread carved into the body.
    threaded_rod(d = thread_d, l = bore_h + 0.02, pitch = pitch,
                 internal = true, bevel = false, blunt_start = false,
                 anchor = BOTTOM);
}
```

#### Rounded L-bracket

A right-angle mounting bracket with two filleted flanges and countersunk-free thru-holes on each face. The 90 degree inside corner is reinforced so it prints as one watertight solid. Tune the flange length, width, plate thickness, corner rounding, and bolt-hole diameter.

```openscad
// Rounded L-bracket
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

leg     = 40;   // length of each flange
width   = 30;   // bracket width (depth)
thick   = 4;    // plate thickness
round_r = 3;    // outer corner rounding radius
hole_d  = 4.5;  // bolt-hole diameter
$fn = 48;

difference() {
    union() {
        // Horizontal flange lying on the bed.
        cuboid([leg, width, thick], rounding = round_r,
               edges = [FRONT + LEFT, FRONT + RIGHT, BACK + LEFT, BACK + RIGHT],
               anchor = BOTTOM + LEFT);
        // Vertical flange standing up at the left edge.
        left(0)
            cuboid([thick, width, leg], rounding = round_r,
                   edges = [FRONT + TOP, FRONT + BOTTOM, BACK + TOP, BACK + BOTTOM],
                   anchor = BOTTOM + LEFT);
    }
    // Bolt hole through the horizontal flange.
    up(-1) translate([leg * 0.6, 0, 0])
        cyl(d = hole_d, h = thick + 2, anchor = BOTTOM);
    // Bolt hole through the vertical flange.
    translate([thick / 2, 0, leg * 0.6])
        xrot(0) yrot(90)
            cyl(d = hole_d, h = thick + 2, anchor = CENTER);
}
```

#### M8 hex bolt

A real ISO-metric M8 hex-head bolt with a properly cut helical thread, generated by the BOSL2 screws library. A single printable solid (head + threaded shaft). Tune the metric size, shank length, threaded length, and fit tolerance.

```openscad
// M8 hex bolt
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/screws.scad>

bolt_size  = "M8";   // metric thread designation (e.g. M6, M8, M10)
bolt_len   = 30;     // overall shaft length below the head (mm)
thread_len = 22;     // length of the threaded section (mm), rest is plain shank
tolerance  = "6g";   // ISO external-thread fit class
$fn = 48;

screw(bolt_size, head = "hex", length = bolt_len, thread_len = thread_len,
      tolerance = tolerance, anchor = BOTTOM);
```

#### M8 hex nut

A real ISO-metric M8 hex nut with an internally cut helical thread, generated by the BOSL2 screws library. A single printable solid that mates with an M8 bolt. Tune the metric size, nut thickness, and internal-thread fit tolerance.

```openscad
// M8 hex nut
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/screws.scad>

nut_size  = "M8";   // metric thread designation (e.g. M6, M8, M10)
thickness = 6.5;    // nut height (mm)
tolerance = "6H";   // ISO internal-thread fit class
$fn = 64;

nut(nut_size, thickness = thickness, thread = "coarse",
    tolerance = tolerance, anchor = BOTTOM);
```

#### NEMA17 motor mount plate

An L-shaped NEMA17 stepper mount: a vertical face plate with the standard 22 mm pilot bore and four 31 mm-spaced M3 bolt holes, joined to a horizontal base flange (with its own slots) by a stiffening rib. Dimensions follow the NEMA17 standard. Prints as one solid. Tune the plate size, thickness, bore, bolt-hole diameter, and base depth.

```openscad
// NEMA17 motor mount plate
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

plate    = 50;    // square face-plate side length
thick    = 5;     // plate thickness
bore_d   = 23;    // center pilot bore (NEMA17 boss is 22 mm)
hole_sp  = 31;    // NEMA17 bolt-hole spacing (centre to centre)
bolt_d   = 3.4;   // M3 clearance hole
base_d   = 35;    // depth of the horizontal base flange
$fn = 56;

difference() {
    union() {
        // Vertical face plate, standing in the X=0 plane.
        cuboid([thick, plate, plate], rounding = 3,
               edges = [FRONT + TOP, FRONT + BOTTOM, BACK + TOP, BACK + BOTTOM],
               anchor = BOTTOM + LEFT);
        // Horizontal base flange.
        cuboid([base_d, plate, thick], rounding = 3,
               edges = [RIGHT + FRONT, RIGHT + BACK],
               anchor = BOTTOM + LEFT);
        // Triangular stiffening ribs on each side.
        for (y = [-plate / 2, plate / 2 - thick])
            translate([0, y, 0])
                linear_extrude(height = thick)
                    polygon([[0, 0], [base_d * 0.7, 0], [0, plate * 0.55]]);
    }
    // Center pilot bore through the face plate.
    translate([thick / 2, plate / 2, plate / 2]) yrot(90)
        cyl(d = bore_d, h = thick + 2, anchor = CENTER);
    // Four NEMA17 bolt holes.
    for (dy = [-hole_sp / 2, hole_sp / 2], dz = [-hole_sp / 2, hole_sp / 2])
        translate([thick / 2, plate / 2 + dy, plate / 2 + dz]) yrot(90)
            cyl(d = bolt_d, h = thick + 2, anchor = CENTER);
    // Two base mounting holes.
    for (dy = [-plate / 3, plate / 3])
        translate([base_d * 0.7, plate / 2 + dy, thick / 2])
            cyl(d = bolt_d + 1, h = thick + 2, anchor = CENTER);
}
```

#### PCO-1810 bottle cap

A standards-compliant PCO-1810 screw cap that fits the common 28mm PET soda/water bottle neck finish, generated by the BOSL2 bottlecaps library. Tune the cap height, wall thickness, and knurl texture for grip.

```openscad
// PCO-1810 bottle cap
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/bottlecaps.scad>

cap_h    = 14.5;     // overall cap height (mm) — must clear the tamper ring (>=14.1)
cap_wall = 1.6;      // side + top wall thickness (mm)
$fn = 96;

pco1810_cap(h = cap_h, wall = cap_wall, texture = "knurled");
```

#### Pencil & tool cup

A round desktop cup for pens, pencils, and small tools, with a rounded top rim and a solid floor. Built from a BOSL2 tube with a chamfered base disc so it prints cleanly without supports. Tune the outer diameter, height, wall thickness, and floor thickness.

```openscad
// Pencil & tool cup
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

cup_d   = 75;   // outer diameter
cup_h   = 95;   // overall height
wall    = 2.4;  // wall thickness
floor_t = 3;    // floor thickness
rim_r   = 1.5;  // rounding radius on the top rim
$fn = 96;

union() {
    // Hollow wall: a tube standing on the floor, top rim rounded.
    up(floor_t)
        tube(h = cup_h - floor_t, od = cup_d, wall = wall,
             orounding2 = rim_r, irounding2 = rim_r, anchor = BOTTOM);
    // Solid floor disc, slightly chamfered at the base for a clean first layer.
    cyl(h = floor_t, d = cup_d, chamfer1 = 0.8, anchor = BOTTOM);
}
```

#### Round pill container body

A pocket-sized round pill bottle body with an external screw thread at the mouth, rounded shoulders, and a closed bottom, built with the BOSL2 threading helix. Pairs with a matching screw lid. Tune the body diameter, height, wall thickness, and thread pitch.

```openscad
// Round pill container body
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/threading.scad>

body_d   = 32;    // outer body diameter (mm)
body_h   = 45;    // total height including the threaded neck (mm)
wall     = 1.8;   // wall + bottom thickness (mm)
neck_d   = 24;    // threaded neck outer diameter (mm)
neck_h   = 10;    // threaded neck height (mm)
pitch    = 3;     // thread pitch (mm)
$fn = 96;

thread_depth = pitch * 0.5;
inner_d      = body_d - 2*wall;
neck_id      = neck_d - 2*wall;
shoulder_h   = 6;                       // tapered shoulder height (mm)
barrel_h     = body_h - neck_h - shoulder_h;

union() {
    // closed-bottom barrel
    difference() {
        cyl(d = body_d, h = barrel_h, anchor = BOTTOM, rounding1 = 3);
        up(wall) cyl(d = inner_d, h = barrel_h, anchor = BOTTOM);
    }
    // tapered shoulder from barrel down-to neck (hollow)
    up(barrel_h)
        difference() {
            cyl(d1 = body_d, d2 = neck_d, h = shoulder_h, anchor = BOTTOM);
            cyl(d1 = inner_d, d2 = neck_id, h = shoulder_h + 0.02, anchor = BOTTOM);
        }
    // threaded neck tube
    up(barrel_h + shoulder_h)
        tube(od = neck_d, id = neck_id, h = neck_h, anchor = BOTTOM);
    // external thread fused into the neck wall
    up(barrel_h + shoulder_h)
        thread_helix(
            d            = neck_d - thread_depth,
            pitch        = pitch,
            thread_depth = thread_depth,
            flank_angle  = 30,
            turns        = (neck_h - pitch) / pitch,
            lead_in      = pitch,
            anchor       = BOTTOM
        );
}
```

#### 2:1 reduction pinion

A 12-tooth pinion that meshes with the 24-tooth spur gear in this set to give an exact 2:1 gear ratio. Print this and the spur gear, set them one pitch-distance apart, and the small gear turns twice for every turn of the large one. Tune the module, tooth count, thickness, and bore.

```openscad
// 2:1 reduction pinion
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/gears.scad>

teeth     = 12;   // teeth on this pinion (24/12 = 2:1 vs the spur gear)
gear_mod  = 2;    // tooth module in mm (must match the mating gear)
thickness = 8;    // gear plate thickness in mm
shaft_d   = 5;    // center shaft bore diameter in mm
$fn = 64;

spur_gear(mod = gear_mod, teeth = teeth, thickness = thickness,
          shaft_diam = shaft_d, pressure_angle = 20);
```

#### Pipe / rod mounting clamp

A one-piece pipe stand-off: a closed cylindrical collar that a pipe or rod passes through, fused to a flat mounting foot with two bolt holes that screws to a wall or panel. The bore is sized for a slip fit. Prints flat as a single watertight solid, no supports. Tune the pipe diameter, wall thickness, collar length, foot width, and bolt-hole size.

```openscad
// Pipe / rod mounting clamp
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

pipe_d   = 20;   // diameter of the pipe / rod it holds
wall     = 4;    // collar wall thickness
length   = 22;   // collar length along the pipe axis
foot_w   = 18;   // width of each mounting foot
hole_d   = 4.5;  // bolt-hole diameter
$fn = 64;

collar_od = pipe_d + 2 * wall;
foot_th   = wall;                 // foot thickness
span      = collar_od + 2 * foot_w; // overall foot span

difference() {
    union() {
        // Collar tube lying along the X axis, resting on the bed.
        up(collar_od / 2)
            xrot(90)
                tube(id = pipe_d, wall = wall, h = length, anchor = CENTER);
        // Flat mounting foot under the collar.
        cuboid([length, span, foot_th], rounding = 3, except = [TOP],
               anchor = BOTTOM);
    }
    // Two bolt holes through the feet, clear of the collar.
    for (y = [collar_od / 2 + foot_w / 2, -(collar_od / 2 + foot_w / 2)])
        translate([0, y, foot_th / 2])
            cyl(d = hole_d, h = foot_th + 2, anchor = CENTER);
}
```

#### Rack (linear gear)

A straight gear rack: the flat, linear counterpart of a spur gear. A meshing pinion of the same module converts rotation into linear travel. Tune the module, number of teeth (rack length), thickness, width, and the solid backing below the teeth.

```openscad
// Rack (linear gear)
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/gears.scad>

gear_mod   = 2;    // tooth module in mm (match the pinion)
teeth      = 12;   // number of teeth (sets rack length)
thickness  = 8;    // overall height in mm
width      = 10;   // depth of the rack along the tooth in mm
backing    = 4;    // solid material below the tooth roots in mm
$fn = 32;

rack(mod = gear_mod, teeth = teeth, thickness = thickness,
     width = width, backing = backing, pressure_angle = 20);
```

#### Internal ring gear

An internal ring (annular) gear with teeth cut on the inside, the outer ring of a planetary gearset. A meshing spur gear of the same module rolls around the inside. Tune the module, tooth count, thickness, and the solid backing wall thickness behind the teeth.

```openscad
// Internal ring gear
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/gears.scad>

teeth      = 48;   // number of internal teeth
gear_mod   = 2;    // tooth module in mm (match the meshing spur gear)
thickness  = 8;    // ring thickness in mm
backing    = 4;    // solid wall thickness behind the teeth in mm
$fn = 64;

ring_gear(mod = gear_mod, teeth = teeth, thickness = thickness,
          backing = backing, pressure_angle = 20);
```

#### Bore roller / conveyor wheel

A barrel-shaped roller (conveyor / guide wheel) with a center bore to spin on a shaft or bushing. The crowned (slightly barrelled) profile self-centers a belt or load and the rounded ends resist edge wear. Printed as one watertight solid. Tune the body diameter, crown, length, and bore.

```openscad
// Bore roller / conveyor wheel
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

body_d   = 20;   // diameter at the ends of the roller
crown    = 1.5;  // extra radius bulge at the middle (barrel crown)
length   = 24;   // axial length of the roller
bore_d   = 8;    // center bore diameter
end_rnd  = 1.0;  // rounding on the end edges
$fn = 120;

mid_d = body_d + 2 * crown;   // diameter at the crowned middle

difference() {
    // barrelled body of revolution
    rotate_extrude($fn = 120)
        polygon([
            [bore_d/2,   0],
            [body_d/2,   0],
            [mid_d/2,    length/2],
            [body_d/2,   length],
            [bore_d/2,   length],
        ]);
    // through bore
    down(1)
        cylinder(h = length + 2, d = bore_d, $fn = 96);
}
```

#### Rounded box with lid

A clean filleted enclosure with rounded edges and a friction-fit lid, using BOSL2 rounding + attachments. Tune the outer size, wall thickness, corner rounding, and lid height.

```openscad
// Rounded box with lid
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

box_w   = 60;   // outer width
box_d   = 40;   // outer depth
box_h   = 30;   // outer height (base part)
wall    = 2;    // wall thickness
round_r = 4;    // corner rounding radius
$fn = 48;

// Hollow base: outer rounded cuboid minus an inner rounded cavity.
difference() {
    cuboid([box_w, box_d, box_h], rounding = round_r, except = [TOP]);
    up(wall)
        cuboid([box_w - 2 * wall, box_d - 2 * wall, box_h], rounding = round_r - wall > 0 ? round_r - wall : 0.5, except = [TOP]);
}
```

#### Rounded storage box base

A hollow box base with softly rounded outer corners and an open top, sized for small parts and hardware. Built from a rounded BOSL2 cuboid with an inner cavity subtracted. Tune the footprint, height, wall thickness, corner rounding, and floor thickness.

```openscad
// Rounded storage box base
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

box_w   = 80;   // outer width (X)
box_d   = 55;   // outer depth (Y)
box_h   = 35;   // outer height (Z)
wall    = 2.4;  // side wall thickness
floor_t = 2.4;  // floor thickness
round_r = 5;    // outer corner rounding radius
$fn = 64;

difference() {
    // Outer rounded shell, sharp top edge for a clean rim.
    cuboid([box_w, box_d, box_h], rounding = round_r, except = [TOP]);
    // Inner cavity: floor left solid, open + slightly over-cut at the top.
    up(-box_h / 2 + floor_t)
        cuboid(
            [box_w - 2 * wall, box_d - 2 * wall, box_h],
            rounding = max(round_r - wall, 0.5),
            except = [TOP],
            anchor = BOTTOM
        );
}
```

#### Capsule (stadium solid)

A cylinder capped by hemispheres on both ends — the 3D stadium / pill shape, built by revolving a stadium profile into one watertight solid. Tune the radius and the straight mid-section length.

```openscad
// Capsule (stadium solid)
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

cap_r    = 12;   // radius of the tube and the end caps
mid_len  = 40;   // length of the straight middle section
segs     = 64;   // surface resolution

half = mid_len / 2;

// Half-profile (X = radius, Y = height): bottom cap arc, straight side, top cap arc.
prof = concat(
    [ for (a = [-90 : 180 / segs : -0.0001])
        [cap_r * cos(a), -half + cap_r * sin(a)] ],          // bottom hemisphere
    [ for (a = [0 : 180 / segs : 90])
        [cap_r * cos(a),  half + cap_r * sin(a)] ],          // top hemisphere
    [[0, half + cap_r], [0, -half - cap_r]]                  // close along the axis
);

rotate_extrude($fn = segs)
    polygon(prof);
```

#### Chamfered box

A solid box with flat 45-degree chamfers cut on every edge for a crisp faceted look. Tune the side lengths and the chamfer depth.

```openscad
// Chamfered box
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

box_w    = 50;   // width  (X)
box_d    = 50;   // depth  (Y)
box_h    = 30;   // height (Z)
chamf    = 5;    // chamfer depth on every edge
$fn = 32;

cuboid([box_w, box_d, box_h], chamfer = chamf);
```

#### Filleted wedge ramp

A right-triangular wedge / ramp solid with its long top edge rounded into a smooth fillet. Tune the footprint, the rise, and the fillet radius.

```openscad
// Filleted wedge ramp
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

base_len = 60;   // ramp run (X)
width    = 30;   // ramp width (Y)
rise     = 28;   // ramp height (Z)
fillet_r = 6;    // radius of the rounded top edge
$fn = 48;

// A wedge minus a rounded prism subtracted from its sharp apex edge,
// realised by intersecting the wedge with an over-sized rounded block.
intersection() {
    wedge([base_len, width, rise]);
    translate([0, 0, -1])
        cuboid([base_len + 2, width, rise + 2], rounding = fillet_r,
               edges = [TOP + FRONT], anchor = CENTER);
}
```

#### Cone frustum

A truncated cone (frustum) with a wider base and narrower top, edges softly rounded. Tune the two radii, the height, and the rounding.

```openscad
// Cone frustum
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

r_base   = 25;   // bottom radius
r_top    = 14;   // top radius
frust_h  = 35;   // height
round_r  = 2;    // rim rounding radius
$fn = 64;

cyl(h = frust_h, r1 = r_base, r2 = r_top, rounding = round_r);
```

#### Hexagonal prism

A regular six-sided prism with softly rounded vertical edges. Tune the across-flats radius, the height, and the side count to make other regular prisms.

```openscad
// Hexagonal prism
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

sides    = 6;    // number of sides (6 = hex, 8 = oct, ...)
radius   = 20;   // circumscribed radius (center to vertex)
prism_h  = 30;   // height
round_r  = 1.5;  // vertical edge rounding
$fn = 48;

regular_prism(n = sides, h = prism_h, r = radius, rounding = round_r);
```

#### Octagonal prism

A regular eight-sided prism with a chamfered top and bottom rim for a faceted token look. Tune the radius, height, side count, and chamfer.

```openscad
// Octagonal prism
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

sides    = 8;    // number of sides (8 = oct)
radius   = 18;   // circumscribed radius (center to vertex)
prism_h  = 22;   // height
chamf    = 2;    // top/bottom rim chamfer
$fn = 48;

regular_prism(n = sides, h = prism_h, r = radius, chamfer = chamf);
```

#### Onion dome finial

A bulbous onion-dome solid that swells out then tapers to a point, sitting on a short cylindrical drum — a classic finial / cupola cap. Tune the bulge, height, and base.

```openscad
// Onion dome finial
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

base_r   = 16;   // drum (base) radius
bulge_r  = 22;   // widest radius of the bulb
dome_h   = 50;   // total height of the bulb above the drum
drum_h   = 8;    // height of the cylindrical base drum
segs     = 96;   // surface resolution

// Profile in the X(radius)-Y(height) plane, revolved into a solid.
// Drum then an onion curve: out to the bulge, in to a point at the top.
prof = concat(
    [[0, 0], [base_r, 0], [base_r, drum_h]],
    [ for (i = [0 : segs])
        let (
            t  = i / segs,                          // 0..1 up the bulb
            // radius: swell to bulge near the bottom, taper to 0 at the top
            rr = (bulge_r) * sin(180 * pow(t, 0.7)) * (1 - t) + base_r * (1 - t),
            zz = drum_h + dome_h * t
        )
        [max(rr, 0.01), zz]
    ],
    [[0, drum_h + dome_h]]
);

rotate_extrude($fn = segs)
    polygon(prof);
```

#### Rounded cuboid

A solid box with all twelve edges rounded into smooth fillets. Tune the three side lengths and the corner radius.

```openscad
// Rounded cuboid
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

box_w   = 60;   // width  (X)
box_d   = 40;   // depth  (Y)
box_h   = 25;   // height (Z)
round_r = 6;    // edge rounding radius
$fn = 48;

cuboid([box_w, box_d, box_h], rounding = round_r);
```

#### Spindle / lens solid

A symmetric spindle (lens) made by revolving a circular arc — a fat lens that tapers to points at both poles. Tune the equatorial radius and the bulge height.

```openscad
// Spindle / lens solid
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

eq_radius = 22;   // equatorial radius (widest point)
half_h    = 26;   // half-height (pole to equator)
segs      = 96;   // surface resolution

// Circular arc through the bottom pole (0,-half_h), the equator (eq_radius,0)
// and the top pole (0,half_h). Center sits on the X axis at cx.
arc_r = (eq_radius * eq_radius + half_h * half_h) / (2 * eq_radius);
cx    = eq_radius - arc_r;                       // arc center X (may be negative)
ang   = atan2(half_h, -cx);                      // half-sweep of the arc

// Right-side profile from bottom pole up to top pole (all X >= 0), then
// close straight down the revolution axis.
prof = concat(
    [ for (a = [-ang : 2 * ang / segs : ang])
        [max(cx + arc_r * cos(a), 0), arc_r * sin(a)] ],
    [[0, half_h], [0, -half_h]]
);

rotate_extrude($fn = segs)
    polygon(prof);
```

#### Super-egg (superellipsoid)

A Piet Hein style super-egg — a superellipsoid that stands on its own end. Tune the radii and the superellipse exponent for a rounder or boxier profile.

```openscad
// Super-egg (superellipsoid)
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

rad_xy = 22;    // equatorial radius (X and Y)
rad_z  = 32;    // polar half-height (Z)
expo   = 2.5;   // superellipse exponent (>2 = squarer, <2 = pointier)
segs   = 96;    // surface resolution

// Build a superellipse cross-section and revolve it into a solid of revolution.
prof = [
    for (a = [0 : 360 / segs : 360 - 0.001])
        let (
            ca = cos(a), sa = sin(a),
            x = rad_xy * sign(ca) * pow(abs(ca), 2 / expo),
            y = rad_z  * sign(sa) * pow(abs(sa), 2 / expo)
        )
        if (x >= 0) [x, y]
];

rotate_extrude($fn = segs)
    polygon(prof);
```

#### Torus ring

A solid donut/ring shape defined by the major (centerline) radius and the minor (tube) radius. Tune both radii and the facet counts.

```openscad
// Torus ring
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

r_major = 30;   // distance from center to tube center
r_minor = 9;    // tube radius
seg_maj = 64;   // segments around the ring
seg_min = 32;   // segments around the tube

torus(r_maj = r_major, r_min = r_minor, $fn = seg_maj);
```

#### Shelf bracket with gusset

A load-bearing shelf bracket: a vertical wall plate, a horizontal shelf flange, and a triangular diagonal gusset web tying the two together for stiffness. Wall and shelf carry mounting holes. The whole part fuses into one watertight solid. Tune the wall height, shelf reach, width, thickness, and hole diameter.

```openscad
// Shelf bracket with gusset
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

wall_h  = 50;   // height of the wall-mount plate
reach   = 45;   // how far the shelf flange sticks out
width   = 24;   // bracket width
thick   = 5;    // plate / gusset thickness
hole_d  = 5;    // mounting-hole diameter
$fn = 48;

difference() {
    union() {
        // Vertical wall plate (mounts to the wall, sits in the X=0 plane).
        cuboid([thick, width, wall_h], anchor = BOTTOM + LEFT);
        // Horizontal shelf flange.
        cuboid([reach, width, thick], anchor = BOTTOM + LEFT);
        // Diagonal triangular gusset in the X-Z plane, extruded across the width.
        translate([0, -width / 2, 0])
            linear_extrude(height = width)
                polygon([[0, 0], [reach, 0], [0, wall_h]]);
    }
    // Two holes in the wall plate.
    for (z = [wall_h * 0.55, wall_h * 0.85])
        translate([thick / 2, 0, z]) yrot(90)
            cyl(d = hole_d, h = thick + 2, anchor = CENTER);
    // Two holes in the shelf flange.
    for (x = [reach * 0.55, reach * 0.85])
        translate([x, 0, thick / 2])
            cyl(d = hole_d, h = thick + 2, anchor = CENTER);
}
```

#### Stepped shoulder bushing

A two-diameter shoulder bushing: a smaller pilot diameter that drops into a bore and a larger shoulder that registers depth and takes face load. Printed as one watertight solid with a clean through bore. Tune the pilot and shoulder diameters and lengths, plus the bore for your shaft.

```openscad
// Stepped shoulder bushing
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

pilot_d    = 10;   // pilot (smaller) outer diameter — fits the bore
shoulder_d = 16;   // shoulder (larger) outer diameter — registers depth
bore_d     = 6;    // shaft through bore
pilot_len  = 10;   // length of the pilot section
shoulder_t = 4;    // thickness of the shoulder section
$fn = 96;

difference() {
    union() {
        // shoulder at the bottom
        cyl(h = shoulder_t, d = shoulder_d, chamfer1 = 0.8, anchor = BOTTOM);
        // pilot rising from the shoulder
        up(shoulder_t)
            cyl(h = pilot_len, d = pilot_d, chamfer2 = 0.8, anchor = BOTTOM);
    }
    // through bore
    down(0.5)
        cyl(h = shoulder_t + pilot_len + 1, d = bore_d, anchor = BOTTOM);
}
```

#### Plain sleeve bushing

A simple cylindrical plain (journal) bushing — a printable sleeve that press-fits into a housing bore and gives a shaft a low-friction running surface. The inner and outer edges are chamfered for easy insertion. Tune the outer diameter to your housing, the bore to your shaft (leave running clearance), and the length to suit.

```openscad
// Plain sleeve bushing
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

outer_d  = 12;   // outer diameter (press-fit into housing)
bore_d   = 8;    // shaft bore diameter (add running clearance)
length   = 14;   // axial length of the sleeve
lead_cf  = 1.0;  // lead-in chamfer on both ends
$fn = 96;

tube(h = length, od = outer_d, id = bore_d, chamfer = lead_cf, anchor = BOTTOM);
```

#### Snap pin (sprung)

A two-pronged snap pin that clicks into a matching socket and locks with a sprung nub. Generated by the BOSL2 joiners library so the spring slot, nub depth, and clearance are correct. Tune the radius, length, snap depth, prong thickness, and fit clearance.

```openscad
// Snap pin (sprung)
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/joiners.scad>

pin_r      = 3.5;   // outer radius of the pin shaft
pin_l      = 16;    // overall length of the pin
nub_depth  = 9;     // distance from tip to the locking nub
snap       = 0.6;   // depth the locking nub springs out
thickness  = 1.2;   // wall thickness of each prong
clearance  = 0.2;   // fit clearance against the socket
$fn = 48;

snap_pin(r = pin_r, l = pin_l, nub_depth = nub_depth, snap = snap,
         thickness = thickness, clearance = clearance,
         anchor = CENTER, orient = UP);
```

#### Snap-pin socket block

A solid mounting block with a sprung snap-pin socket bored into it — the receiver for the BOSL2 snap pin. The socket geometry comes from the BOSL2 joiners library so the locking-nub catch matches the pin exactly. Tune the block size, socket radius, length, and snap depth.

```openscad
// Snap-pin socket block
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/joiners.scad>

block       = [16, 16, 18];  // outer block size [x, y, z]
socket_r    = 3.5;           // socket radius (match the pin radius)
socket_l    = 16;            // socket depth / length
nub_depth   = 9;             // distance to the locking-nub catch
snap        = 0.6;           // depth of the nub catch
$fn = 48;

diff()
  cuboid(block)
    attach(TOP, TOP, inside = true)
      tag("remove")
        snap_pin_socket(r = socket_r, l = socket_l,
                        nub_depth = nub_depth, snap = snap);
```

#### Spur gear (involute)

A real meshing spur gear with a correct involute tooth profile and a center bore, generated by the BOSL2 gears library. Tune the module (tooth size), tooth count, thickness, and shaft diameter.

```openscad
// Spur gear (involute)
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/gears.scad>

teeth     = 24;   // number of teeth
gear_mod  = 2;    // module (mm of pitch diameter per tooth)
thickness = 8;    // gear thickness
shaft_d   = 6;    // center bore diameter
$fn = 48;

spur_gear(mod = gear_mod, teeth = teeth, thickness = thickness, shaft_diam = shaft_d);
```

#### Stacking parts bin

An open-front-friendly parts bin whose rounded base nests a matching lip on the bin below, so several stack stably on a shelf. The shell is carved hollow from a rounded block with a recessed underside rim. Tune the footprint, height, wall thickness, floor thickness, and the nesting lip depth.

```openscad
// Stacking parts bin
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

bin_w    = 70;   // outer width (X)
bin_d    = 100;  // outer depth (Y)
bin_h    = 45;   // outer height (Z)
wall     = 2.4;  // wall thickness
floor_t  = 3.6;  // floor thickness (> lip_h so the floor stays solid)
lip_h    = 2;    // depth of the nesting recess under the base
round_r  = 4;    // outer corner rounding radius
$fn = 48;

difference() {
    cuboid([bin_w, bin_d, bin_h], rounding = round_r, except = [TOP]);
    // Interior pocket, open top.
    up(-bin_h / 2 + floor_t)
        cuboid([bin_w - 2 * wall, bin_d - 2 * wall, bin_h],
               rounding = max(round_r - wall, 0.5), except = [TOP], anchor = BOTTOM);
    // Nesting recess in the underside: a shallow pocket matching the rim of the
    // bin below. Kept shallower than the floor so the base stays watertight.
    down(bin_h / 2 + 0.01)
        cuboid([bin_w - 2 * wall + 0.6, bin_d - 2 * wall + 0.6, lip_h],
               rounding = max(round_r - wall, 0.5), except = [TOP], anchor = BOTTOM);
}
```

#### Rubber-foot bumper socket

A screw-down socket that grips a push-in rubber bumper foot: a rounded cylindrical body with a counterbored cavity for the rubber foot's stem and a central screw hole through the bottom. Tune body diameter/height, socket cavity diameter/depth, and screw hole diameter.

```openscad
// Rubber-foot bumper socket
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

body_d    = 24;   // outer body diameter (mm)
body_h    = 12;   // overall body height (mm)
socket_d  = 14;   // rubber-foot cavity diameter (mm)
socket_h  = 7;    // cavity depth from the top (mm)
hole_d    = 4.2;  // central mounting screw hole diameter (mm)
$fn = 64;

difference() {
    // rounded body
    cyl(h = body_h, d = body_d, rounding1 = 2, rounding2 = 4, anchor = BOTTOM);
    // socket cavity for the rubber foot, opening at the top
    up(body_h - socket_h)
        cyl(h = socket_h + 0.1, d = socket_d, rounding1 = 1, anchor = BOTTOM);
    // central mounting screw hole through the floor
    cyl(h = body_h * 3, d = hole_d);
    // counterbore so a flat screw head sits flush on the underside
    down(0.01)
        cyl(h = 3, d = hole_d * 2, anchor = BOTTOM);
}
```

#### Corner brace with triangular gusset

A 90-degree corner brace: two flat mounting tabs joined by a tall triangular gusset web for stiffness, with one screw hole per tab. Tune tab length, plate thickness, gusset height/web thickness, and hole diameter.

```openscad
// Corner brace with triangular gusset
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

tab     = 35;   // length of each mounting tab (mm)
wide    = 22;   // brace width (mm)
thick   = 4;    // tab/plate thickness (mm)
gusset  = 28;   // gusset height up each leg (mm)
web     = 3;    // gusset web thickness (mm)
hole_d  = 5;    // screw hole diameter (mm)
$fn = 48;

difference() {
    union() {
        // horizontal tab on the build plate
        cuboid([tab, wide, thick], rounding = 2, edges = "Z",
               anchor = BOTTOM + LEFT + FRONT);
        // vertical tab standing at the corner
        cuboid([thick, wide, tab], rounding = 2, edges = "Z",
               anchor = BOTTOM + LEFT + FRONT);
        // central triangular gusset web bridging the two tabs.
        // polygon lies in X-Z (corner cross-section); extruded across Y.
        // origin sunk to (0,0) so the triangle legs overlap both plates.
        translate([0, wide / 2 + web / 2, 0])
            rotate([90, 0, 0])
            linear_extrude(height = web)
                polygon([[0, 0], [gusset, 0], [0, gusset]]);
    }
    // horizontal tab hole
    translate([tab * 0.75, wide / 2, thick / 2])
        cyl(h = thick * 3, d = hole_d);
    // vertical tab hole
    translate([thick / 2, wide / 2, tab * 0.75])
        yrot(90)
        cyl(h = thick * 3, d = hole_d);
}
```

#### Rounded L-bracket

A right-angle mounting L-bracket with rounded outer corners, an inner fillet weld at the corner for strength, and four screw holes (two per leg). Tune leg length, width, thickness, hole diameter, and fillet radius.

```openscad
// Rounded L-bracket
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

leg     = 40;   // length of each leg measured from the corner (mm)
wide    = 30;   // bracket width (mm)
thick   = 4;    // plate thickness (mm)
hole_d  = 4.5;  // screw hole diameter (mm)
fillet  = 6;    // inner corner gusset size (mm)
$fn = 48;

// A solid right-triangle (legs along +X and +Y) with the hypotenuse
// rounded concavely toward the corner — the cross-section of a weld bead.
module fillet_profile(s) {
    difference() {
        square([s, s]);
        translate([s, s]) circle(r = s);
    }
}

difference() {
    union() {
        // horizontal leg: thickness in Z, lies on the build plate
        cuboid([leg, wide, thick], rounding = 2, edges = "Z",
               anchor = BOTTOM + LEFT + FRONT);
        // vertical leg: thickness in X, stands up at the corner
        cuboid([thick, wide, leg], rounding = 2, edges = "Z",
               anchor = BOTTOM + LEFT + FRONT);
        // inner fillet weld bead bridging the two legs along the width:
        // a rounded right-triangle extruded across the full width.
        translate([thick, wide, thick])
            xrot(90)
            linear_extrude(height = wide)
                fillet_profile(fillet);
    }
    // horizontal-leg screw holes
    for (x = [leg * 0.55, leg * 0.85])
        translate([x, wide / 2, thick / 2])
            cyl(h = thick * 3, d = hole_d);
    // vertical-leg screw holes
    for (z = [leg * 0.55, leg * 0.85])
        translate([thick / 2, wide / 2, z])
            xrot(0) yrot(90)
                cyl(h = thick * 3, d = hole_d);
}
```

#### Threaded leveling foot

An adjustable leveling foot: a wide rounded base disk topped by a real metric-style threaded stud that screws into a furniture insert. Tune base diameter/height, stud thread diameter, pitch, and stud length. Pairs with a matching threaded insert/nut.

```openscad
// Threaded leveling foot
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/threading.scad>

base_d   = 40;   // base disk diameter (mm)
base_h   = 8;    // base disk height (mm)
stud_d   = 8;    // stud thread outer diameter (mm)
pitch    = 1.25; // thread pitch (mm)
stud_l   = 20;   // threaded stud length (mm)
$fn = 64;

union() {
    // rounded foot base that contacts the floor
    cyl(h = base_h, d = base_d, rounding1 = 1.5, rounding2 = 3, anchor = BOTTOM);
    // short solid neck to blend the threads onto the base
    up(base_h - 0.01)
        cyl(h = 4, d = stud_d + 2, chamfer2 = 1, anchor = BOTTOM);
    // real threaded stud, sunk into the neck so the volumes overlap
    up(base_h + 1)
        threaded_rod(d = stud_d, l = stud_l, pitch = pitch,
                     bevel2 = true, blunt_start = true, anchor = BOTTOM);
}
```

#### Panel snap clip

A cantilever snap-fit clip for retaining a panel or wire: a screw-down base with an upright flexible arm ending in a hooked barb that snaps over a panel edge. Two base bolt holes. Tune base size, arm height/thickness, barb depth, and bolt hole diameter.

```openscad
// Panel snap clip
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

base_l   = 30;   // base length (mm)
base_w   = 16;   // base width (mm)
base_t   = 4;    // base thickness (mm)
arm_h    = 18;   // cantilever arm height (mm)
arm_t    = 3;    // arm thickness (mm)
barb     = 4;    // hook barb overhang depth (mm)
hole_d   = 4;    // base bolt hole diameter (mm)
$fn = 48;

difference() {
    union() {
        // mounting base
        translate([0, 0, base_t / 2])
            cuboid([base_l, base_w, base_t], rounding = 2, edges = "Z");
        // flexible cantilever arm rising from one end of the base
        translate([-base_l / 2 + arm_t / 2, 0, base_t])
            cuboid([arm_t, base_w * 0.7, arm_h],
                   rounding = 1, edges = [FRONT + TOP, BACK + TOP],
                   anchor = BOTTOM);
        // hooked barb at the top of the arm (extends inward over the panel).
        // built as an extruded right-triangle so it prints with a sloped
        // self-supporting underside.
        translate([-base_l / 2, base_w * 0.7 / 2, base_t + arm_h - 2])
            rotate([90, 0, 0])
            linear_extrude(height = base_w * 0.7)
                polygon([
                    [0, 2],
                    [arm_t + barb, 2],
                    [0, -barb * 1.4]
                ]);
    }
    // base bolt holes
    for (x = [base_l * 0.18, base_l * 0.42])
        translate([x, 0, base_t / 2])
            cyl(h = base_t * 4, d = hole_d);
}
```

#### Right-angle gusset plate

A flat triangular gusset plate for reinforcing 90-degree frame joints: a right-triangle web with a thickened ribbed hypotenuse and three bolt holes (one near each corner). Lies flat for printing. Tune leg length, thickness, rib width, and bolt hole diameter.

```openscad
// Right-angle gusset plate
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

leg     = 50;   // length of each right-angle leg (mm)
thick   = 4;    // plate thickness (mm)
rib     = 6;    // raised rib width along the hypotenuse (mm)
rib_h   = 3;    // extra rib height above the plate (mm)
hole_d  = 5;    // bolt hole diameter (mm)
$fn = 48;

inset = leg * 0.18;   // bolt-hole inset from corners

difference() {
    union() {
        // flat triangular web (corner at origin, legs along +X and +Y)
        linear_extrude(height = thick)
            offset(r = 3) offset(r = -3)   // round the sharp tip a touch
                polygon([[0, 0], [leg, 0], [0, leg]]);
        // raised stiffening rib along the hypotenuse
        translate([0, 0, thick])
            linear_extrude(height = rib_h)
                polygon([
                    [leg - rib, 0], [leg, 0],
                    [0, leg], [0, leg - rib]
                ]);
    }
    // three bolt holes near the corners
    for (p = [[inset, inset], [leg - inset * 2, inset], [inset, leg - inset * 2]])
        translate([p[0], p[1], 0])
            cyl(h = (thick + rib_h) * 4, d = hole_d);
}
```

#### Shelf bracket with diagonal strut

A wall-mount shelf bracket: a vertical wall plate, a horizontal shelf plate, and a diagonal strut between them for load support. Wall plate has two keyhole-free round screw holes; shelf plate has one. Tune arm lengths, thickness, width, strut width, and hole diameter.

```openscad
// Shelf bracket with diagonal strut
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

arm     = 60;   // horizontal shelf arm length (mm)
rise    = 60;   // vertical wall plate height (mm)
wide    = 25;   // bracket width (mm)
thick   = 5;    // plate thickness (mm)
strut_w = 6;    // diagonal strut thickness (mm)
hole_d  = 5;    // screw hole diameter (mm)
$fn = 48;

difference() {
    union() {
        // vertical wall plate (thickness in X)
        cuboid([thick, wide, rise], rounding = 2, edges = "Z",
               anchor = BOTTOM + LEFT + FRONT);
        // horizontal shelf plate (thickness in Z)
        cuboid([arm, wide, thick], rounding = 2, edges = "Z",
               anchor = BOTTOM + LEFT + FRONT);
        // diagonal strut: a thick band from wall up-point to shelf out-point.
        // built as an extruded quadrilateral in the X-Z corner plane.
        translate([0, wide / 2 + strut_w / 2, 0])
            rotate([90, 0, 0])
            linear_extrude(height = strut_w)
                polygon([
                    [0, 0],
                    [arm * 0.85, thick],
                    [thick, rise * 0.85]
                ]);
    }
    // wall plate screw holes
    for (z = [rise * 0.30, rise * 0.65])
        translate([thick / 2, wide / 2, z])
            yrot(90)
            cyl(h = thick * 3, d = hole_d);
    // shelf plate screw hole
    translate([arm * 0.78, wide / 2, thick / 2])
        cyl(h = thick * 3, d = hole_d);
}
```

#### Tube / rod clamp with mounting feet

A saddle clamp that holds a tube or rod against a surface: a rounded cradle block with a semicircular channel and two flat mounting feet, each with a bolt hole. Tune tube diameter, wall thickness, foot length, base thickness, width, and bolt hole diameter.

```openscad
// Tube / rod clamp with mounting feet
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

tube_d   = 20;   // tube/rod outer diameter to clamp (mm)
wall     = 4;    // cradle wall above the tube (mm)
foot     = 14;   // length of each mounting foot (mm)
base_t   = 4;    // base plate thickness under the feet (mm)
wide     = 18;   // clamp width along the tube axis (mm)
bolt_d   = 4.5;  // mounting bolt hole diameter (mm)
$fn = 64;

tube_r    = tube_d / 2;
axis_z    = base_t + tube_r;           // tube channel centre height
block_h   = axis_z + wall;             // top of the cradle block
block_w   = tube_d + 2 * wall;         // cradle block width
span      = block_w + 2 * foot;        // total footprint width
foot_ctr  = block_w / 2 + foot / 2;    // bolt-hole x offset

difference() {
    union() {
        // base plate spanning the full footprint with the two feet
        translate([0, 0, base_t / 2])
            cuboid([span, wide, base_t], rounding = 2, edges = "Z");
        // solid cradle block sitting on the base
        translate([0, 0, block_h / 2])
            cuboid([block_w, wide, block_h], rounding = 3,
                   edges = "Z");
    }
    // semicircular tube channel through the block (axis along Y)
    up(axis_z)
        yrot(90)
        cyl(h = wide + 1, d = tube_d, anchor = CENTER);
    // bolt holes through each foot
    for (x = [-foot_ctr, foot_ctr])
        translate([x, 0, base_t / 2])
            cyl(h = base_t * 4, d = bolt_d);
}
```

#### Threaded jar body

A round storage jar with an external screw thread at the mouth and a flat closed bottom, built with the BOSL2 threading helix. Pairs with the matching threaded lid recipe. Tune the body diameter, height, wall thickness, and thread pitch.

```openscad
// Threaded jar body
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/threading.scad>

body_d   = 50;    // outer body diameter (mm)
body_h   = 60;    // total jar height including the threaded neck (mm)
wall     = 2.4;   // wall + bottom thickness (mm)
pitch    = 4;     // thread pitch (mm)
neck_h   = 12;    // height of the threaded neck section (mm)
$fn = 96;

thread_depth = pitch * 0.5;   // derived thread depth (mm)
inner_d      = body_d - 2*wall;

union() {
    // hollow body shell with a closed welded floor
    tube(od = body_d, id = inner_d, h = body_h, anchor = BOTTOM);
    cyl(d = inner_d + 0.02, h = wall, anchor = BOTTOM);   // welded floor
    // external thread fused into the neck wall (root overlaps the wall)
    up(body_h - neck_h)
        thread_helix(
            d            = body_d - thread_depth,
            pitch        = pitch,
            thread_depth = thread_depth,
            flank_angle  = 30,
            turns        = (neck_h - pitch) / pitch,
            lead_in      = pitch,
            anchor       = BOTTOM
        );
}
```

#### Threaded jar lid

A round screw-on lid with an internal thread and a knurled grip skirt, built with the BOSL2 threading helix. Mates with the matching threaded jar body recipe — keep the jar diameter and pitch in sync. Tune the lid diameter, height, wall thickness, and pitch.

```openscad
// Threaded jar lid
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/threading.scad>

jar_d     = 50;    // jar neck outer diameter to fit over (mm)
lid_h     = 14;    // total lid height (mm)
top_t     = 2.4;   // top wall thickness (mm)
skirt_t   = 3;     // skirt wall thickness (mm)
pitch     = 4;     // thread pitch (mm) — match the jar
tol       = 0.4;   // clearance for a free-running fit (mm)
$fn = 96;

thread_depth = pitch * 0.5;
outer_d      = jar_d + 2*skirt_t;
bore_d       = jar_d + tol;            // inner skirt diameter

difference() {
    union() {
        // outer lid shell (closed top, open bottom)
        cyl(d = outer_d, h = lid_h, anchor = BOTTOM, rounding2 = 2);
        // internal thread fused to the bore wall
        internal_thread();
    }
    // hollow out the skirt bore from the underside, leaving the top closed
    up(top_t)
        cyl(d = bore_d, h = lid_h, anchor = BOTTOM);
}

module internal_thread() {
    up(top_t)
        thread_helix(
            d            = bore_d + thread_depth,
            pitch        = pitch,
            thread_depth = thread_depth,
            flank_angle  = 30,
            turns        = (lid_h - top_t - pitch) / pitch,
            lead_in      = pitch,
            internal     = true,
            anchor       = BOTTOM
        );
}
```

#### Threaded rod (metric)

A real metric threaded rod (all-thread / studding) with a true helical ISO thread profile and blunt printable ends, generated by the BOSL2 threading library. A single printable solid. Tune the major diameter, pitch, and overall length.

```openscad
// Threaded rod (metric)
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/threading.scad>

rod_d  = 8;     // major (outer) diameter (mm)
pitch  = 1.25;  // thread pitch (mm) — 1.25 is standard M8 coarse
length = 40;    // overall rod length (mm)
$fn = 64;

threaded_rod(d = rod_d, l = length, pitch = pitch,
             blunt_start = true, bevel = true, anchor = BOTTOM);
```

#### Thrust washer

A flat annular thrust washer that sits between a rotating part and a fixed face to carry axial load and reduce wear. Both faces are flat for full bearing contact, with chamfered edges so it does not snag. Tune the outer and inner diameters to your shaft and seat, and the thickness for the load.

```openscad
// Thrust washer
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

outer_d   = 20;   // outer diameter
bore_d    = 10;   // inner bore diameter (shaft)
thickness = 2;    // washer thickness
edge_cf   = 0.5;  // edge chamfer
$fn = 120;

tube(h = thickness, od = outer_d, id = bore_d, chamfer = edge_cf, anchor = BOTTOM);
```

#### Thumb screw

A real thumb screw: a wide knurled finger grip head fused to a properly threaded metric shaft for tool-free tightening, built from the BOSL2 cylinder texture + threading libraries. A single printable solid. Tune the head diameter, shaft size, pitch and length.

```openscad
// Thumb screw
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/threading.scad>

head_d   = 22;    // knurled grip head diameter (mm)
head_h   = 8;     // head height (mm)
thread_d = 6;     // shaft thread major diameter (mm)
pitch    = 1.0;   // thread pitch (mm) — 1.0 is M6 coarse
shaft_l  = 20;    // threaded shaft length below the head (mm)
$fn = 72;

// Knurled grip head sitting on the build plate.
cyl(d = head_d, h = head_h, rounding1 = 2, chamfer2 = 1,
    texture = "diamonds", tex_size = [3, 3], tex_depth = 0.9,
    anchor = BOTTOM);

// Threaded shaft growing upward out of the head's top face.
up(head_h - 0.5)
    threaded_rod(d = thread_d, l = shaft_l + 0.5, pitch = pitch,
                 blunt_start = true, bevel2 = true, bevel1 = false,
                 anchor = BOTTOM);
```

#### Tool caddy with handle

A two-pocket carry caddy with a central spine and a rounded grab handle, for ferrying tools, brushes, or craft supplies. The pockets, handle hole, and rounded body are all carved from one rounded block so it prints as a single watertight piece. Tune the footprint, height, wall thickness, and handle dimensions.

```openscad
// Tool caddy with handle
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

cad_w     = 130;  // outer width (X)
cad_d     = 70;   // outer depth (Y)
body_h    = 55;   // height of the body up to the handle bridge
wall      = 2.6;  // wall + spine thickness
floor_t   = 2.6;  // floor thickness
handle_h  = 28;   // height of the handle arch above the body
$fn = 40;

// Two pockets either side of a central spine.
pocket_w = (cad_w - 2 * wall - wall) / 2;
pocket_d = cad_d - 2 * wall;
pocket_h = body_h - floor_t;
total_h  = body_h + handle_h;

difference() {
    // Body block plus a handle slab on top, rounded vertical corners.
    union() {
        cuboid([cad_w, cad_d, body_h], rounding = 5, edges = "Z", anchor = BOTTOM);
        // Central handle slab (spans the width), overlapping the body so they fuse.
        // Round only the TOP edges so the slab/body interface stays a clean plane.
        translate([0, 0, body_h - 2])
            cuboid([cad_w, wall * 3, handle_h + 2], rounding = 3,
                   edges = TOP, anchor = BOTTOM);
    }
    // Left and right pockets.
    for (s = [-1, 1])
        translate([s * (wall / 2 + pocket_w / 2), 0, floor_t])
            cuboid([pocket_w, pocket_d, pocket_h + 1],
                   rounding = 3, edges = "Z", anchor = BOTTOM);
    // Hand hole through the slab (plain slot, no end rounding to stay manifold).
    up(body_h + handle_h * 0.45)
        ycyl(h = cad_d + 1, d = handle_h * 0.7);
}
```

#### T-slot joiner clip

A T-slot snap joiner clip from the BOSL2 joiners library — the printable connector that plugs two panels together edge-to-edge with a self-retaining barbed tongue. Tune the length, width, base thickness, and flank angle.

```openscad
// T-slot joiner clip
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/joiners.scad>

joiner_l   = 40;   // length of the joiner along the join axis
joiner_w   = 10;   // width of the tongue
base_t     = 10;   // base / wall thickness it mounts to
flank_ang  = 30;   // barb flank angle in degrees
$fn = 32;

joiner(l = joiner_l, w = joiner_w, base = base_t, ang = flank_ang);
```

#### Broom / tool clip

A spring-grip wall clip for broom, mop, or rake handles: a screw-mount backplate with a C-shaped jaw whose mouth is narrower than the handle so it snaps on and holds. Tune the handle diameter, mouth opening, jaw thickness, and backplate.

```openscad
// Broom / tool clip
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

handle_d  = 24;   // diameter of the handle to grip
gap       = 14;   // mouth opening (under handle_d so it grips)
jaw_t     = 4;    // jaw wall thickness
plate_w   = 40;   // backplate width
plate_t   = 5;    // backplate thickness
plate_h   = 30;   // backplate height (along the handle axis)
screw_d   = 4.5;  // screw clearance bore
$fn = 72;

bore_r  = handle_d / 2;
outer_r = bore_r + jaw_t;
overlap = jaw_t * 0.8;
// Jaw centre lifted off the plate so the outer ring fuses with the backplate.
jaw_y   = plate_t + outer_r - overlap;

union() {
    // Backplate flush to wall (back at y=0, extends to y=plate_t).
    difference() {
        cuboid([plate_w, plate_t, plate_h], rounding = 3, edges = "Y", anchor = BOTTOM + FRONT);
        translate([0, 0, plate_h * 0.5]) {
            ycyl(h = plate_t * 3, d = screw_d, anchor = CENTER);
            translate([0, plate_t, 0]) ycyl(h = screw_d, d1 = screw_d * 2.2, d2 = screw_d, anchor = FRONT);
        }
    }

    // C-shaped jaw: axis runs vertically (Z), opening faces away from the wall (+Y).
    translate([0, jaw_y, plate_h * 0.5])
        difference() {
            zcyl(h = plate_h, r = outer_r, rounding = 1);
            zcyl(h = plate_h + 0.4, r = bore_r);
            // Mouth slot opening toward +Y (away from the wall).
            fwd(0) back(outer_r / 2)
                cube([gap, outer_r + 0.4, plate_h + 0.4], center = true);
        }
}
```

#### Screw-mount cable clip

A screw-mount cable clip with a flat slotted backing tab and a rounded saddle that holds one or more cables against a wall or desk edge. The screw slot lets you snug it down over the cables. Tune the cable diameter, saddle width, tab length, and screw bore.

```openscad
// Screw-mount cable clip
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

cable_d   = 7;    // diameter of cable(s) to hold
saddle_w  = 14;   // width of the holding saddle (along cable axis)
wall      = 2.5;  // saddle wall thickness
tab_len   = 14;   // length of the screw tab beside the saddle
tab_t     = 3;    // backing / tab thickness
screw_d   = 4;    // screw clearance bore
$fn = 64;

bore_r  = cable_d / 2;
outer_r = bore_r + wall;
overlap = wall * 0.7;
saddle_z = tab_t + outer_r - overlap;

union() {
    // Backing strip the whole assembly sits on (back on z=0, against wall when
    // laid flat; printable as a single flat-bottomed solid).
    base_w = saddle_w;
    base_l = outer_r * 2 + tab_len;
    cuboid([base_w, base_l, tab_t], rounding = 1.5, edges = "Z", anchor = BOTTOM);

    // Screw tab: extends out one side with a countersunk hole.
    difference() {
        translate([0, base_l/2 - tab_len/2, 0])
            cuboid([base_w * 0.8, tab_len, tab_t], rounding = 3, edges = "Z", anchor = BOTTOM);
        translate([0, base_l/2 - tab_len/2 + 2, 0]) {
            cyl(h = tab_t * 3, d = screw_d, anchor = CENTER);
            up(tab_t) cyl(h = screw_d, d1 = screw_d * 2.2, d2 = screw_d, anchor = TOP);
        }
    }

    // Cable saddle: a tube section (axis along the cable / Y) with the top
    // opened so cables press in, sitting over the base near the other side.
    translate([0, -base_l/2 + outer_r, saddle_z])
        difference() {
            xcyl(h = saddle_w, r = outer_r, rounding = 1);
            xcyl(h = saddle_w + 0.4, r = bore_r);
            up(outer_r/2) cube([saddle_w + 0.4, gap_open(), outer_r + 0.4], center = true);
        }
}

// Mouth opening a touch narrower than the cable so cables snap and stay.
function gap_open() = cable_d * 0.55;
```

#### Double coat hook

A double coat hook with two outward-curving prongs on a single screw-mount backplate, ideal for hanging two coats or a coat and a scarf. Tune the backplate size, prong reach, prong thickness, and screw bore.

```openscad
// Double coat hook
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

plate_w   = 34;   // backplate width
plate_h   = 60;   // backplate height
plate_t   = 6;    // backplate thickness
prong_len = 22;   // horizontal reach of each prong
prong_r   = 5;    // prong radius
screw_d   = 4.5;  // screw clearance bore
$fn = 56;

module prong(z) {
    // A prong sweeps straight out from the wall then tips up into a small hook.
    pts = concat(
        [ for (x = [0:2:prong_len]) [x, 0, 0] ],
        [ for (a = [0:8:120]) [prong_len + sin(a) * prong_r * 1.4,
                               0,
                               (1 - cos(a)) * prong_r * 1.4] ]
    );
    translate([0, plate_t * 0.5, z])
        path_sweep(circle(r = prong_r), path3d(deduplicate(pts)), closed = false);
}

union() {
    difference() {
        cuboid([plate_w, plate_t, plate_h], rounding = 5, edges = "Y", anchor = BOTTOM + BACK);
        // Two screw holes (top and bottom) with countersinks.
        for (z = [plate_h - 10, 10]) {
            up(z) ycyl(h = plate_t * 3, d = screw_d, anchor = CENTER);
            up(z) back(0.1) ycyl(h = screw_d, d1 = screw_d * 2.2, d2 = screw_d, anchor = BACK);
        }
    }
    prong(plate_h * 0.62);
    prong(plate_h * 0.30);
}
```

#### French-cleat hook

A French-cleat hanger: the back face is bevelled at 45 degrees to drop over a matching wall-mounted cleat rail, and the front carries an upward hook arm. Self-locking and tool-free to mount. Tune the body size, cleat angle, hook reach, and arm thickness.

```openscad
// French-cleat hook
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

body_w    = 40;   // width of the cleat block
body_h    = 50;   // height of the cleat block
body_t    = 18;   // depth (thickness) of the block, front-to-back
cleat_ang = 45;   // bevel angle of the cleat slot
arm_reach = 28;   // how far the front hook reaches out
arm_r     = 5;    // hook arm radius
$fn = 56;

// Body sits with its back face on the y=0 plane and extends forward to y=body_t.
union() {
    // Cleat body with a 45-degree wedge groove cut into the back face so it
    // hooks downward onto a matching wall rail. The groove is kept shallow
    // (depth < body_t) so the block stays one connected piece.
    difference() {
        cuboid([body_w, body_t, body_h], rounding = 2, edges = "Y", anchor = BOTTOM + FRONT);
        // Wedge slot: removes a 45deg triangular notch from the BACK face in
        // the upper region. The back face is at y=0; the cutter is centred just
        // behind it and rotated so its lower face slopes up-and-forward,
        // forming the cleat's bearing surface. Kept short in depth so the front
        // half of the block stays solid and connected.
        translate([0, 0, body_h * 0.72])
            xrot(cleat_ang)
            cuboid([body_w + 2, body_t * 0.9, body_h], anchor = BACK);
    }

    // Front hook arm: sweeps out from the FRONT face (+Y) and curls up (+Z).
    // Path is built directly in (y=out, z=up); start slightly inside the body
    // so the swept tube fuses with the block.
    pts = concat(
        [ for (y = [-arm_r:2:arm_reach]) [0, y, 0] ],
        [ for (a = [0:8:120]) [0,
                               arm_reach + sin(a) * arm_r * 1.6,
                               (1 - cos(a)) * arm_r * 1.6] ]
    );
    translate([0, body_t, body_h * 0.20])
        path_sweep(circle(r = arm_r), deduplicate(pts), closed = false);
}
```

#### J-hook wall hanger

A deep J-shaped hook on a screw-mount backplate for hanging tools, mugs, utensils, or cables. The long curved tail makes items easy to lift on and off. Tune the backplate, hook drop, hook radius, and bar thickness.

```openscad
// J-hook wall hanger
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

plate_w  = 26;   // backplate width
plate_h  = 30;   // backplate height
plate_t  = 5;    // backplate thickness
reach    = 24;   // how far the hook reaches out from the wall
drop     = 28;   // how far the J curls back down
bar_r    = 4;    // thickness (radius) of the hook bar
screw_d  = 4.5;  // screw clearance bore
$fn = 56;

union() {
    difference() {
        cuboid([plate_w, plate_t, plate_h], rounding = 4, edges = "Y", anchor = BOTTOM + BACK);
        up(plate_h - 8) ycyl(h = plate_t * 3, d = screw_d, anchor = CENTER);
        up(plate_h - 8) back(0.1) ycyl(h = screw_d, d1 = screw_d * 2.2, d2 = screw_d, anchor = BACK);
    }

    // The J: out from the wall, then a half-circle curl down and back.
    out_pts  = [ for (x = [0:2:reach]) [x, 0, 0] ];
    curl_pts = [ for (a = [0:8:180]) [reach + sin(a) * (drop/2),
                                      0,
                                      -(1 - cos(a)) * (drop/2)] ];
    pts = deduplicate(concat(out_pts, curl_pts));
    translate([0, plate_t * 0.5, plate_h * 0.5])
        path_sweep(circle(r = bar_r), path3d(pts), closed = false);
}
```

#### Key rail with hooks

A horizontal screw-mount key rail: a rounded backing bar with a row of small upward hooks for keys, lanyards, or jewellery, plus screw holes at each end. Tune the rail length, number of hooks, hook size, and bar thickness.

```openscad
// Key rail with hooks
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

rail_len  = 120;  // overall length of the rail
rail_h    = 22;   // height of the backing bar
rail_t    = 6;    // thickness of the backing bar
n_hooks   = 4;    // number of key hooks
hook_r    = 2.5;  // hook bar radius
screw_d   = 4.5;  // screw clearance bore
$fn = 40;

module key_hook() {
    // Small hook: out from the bar then curling up.
    pts = concat(
        [ for (y = [-hook_r:1:6]) [0, y, 0] ],
        [ for (a = [0:12:150]) [0, 6 + sin(a) * hook_r * 2, (1 - cos(a)) * hook_r * 2] ]
    );
    path_sweep(circle(r = hook_r), deduplicate(pts), closed = false);
}

union() {
    // Backing bar: back face on y=0, extends forward to y=rail_t.
    difference() {
        cuboid([rail_len, rail_t, rail_h], rounding = 3, edges = "Y", anchor = BOTTOM + FRONT);
        // Screw holes near each end with countersinks (drilled along +Y).
        for (sx = [-1, 1]) {
            translate([sx * (rail_len/2 - 9), 0, rail_h * 0.5])
                ycyl(h = rail_t * 3, d = screw_d, anchor = CENTER);
            translate([sx * (rail_len/2 - 9), rail_t, rail_h * 0.5])
                ycyl(h = screw_d, d1 = screw_d * 2.2, d2 = screw_d, anchor = FRONT);
        }
    }
    // Row of evenly spaced hooks along the lower front edge.
    for (i = [0 : n_hooks - 1]) {
        x = -rail_len/2 + rail_len * (i + 0.5) / n_hooks;
        translate([x, rail_t, rail_h * 0.25]) key_hook();
    }
}
```

#### Pegboard hook

A pegboard hook that plugs into standard 1-inch-pitch pegboard: two rear pegs drop into adjacent holes and a forward arm holds tools. Tune the peg diameter, pegboard pitch, board thickness, arm reach, and bar thickness. Default pitch 25.4 mm and peg 5.5 mm suit common 6 mm pegboard.

```openscad
// Pegboard hook
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

peg_d     = 5.5;   // pegboard peg diameter (fits ~6mm holes)
pitch     = 25.4;  // pegboard hole pitch (1 inch)
board_t   = 6;     // pegboard panel thickness
arm_reach = 35;    // how far the front arm sticks out
bar_r     = 4;     // arm / spine thickness (radius)
$fn = 48;

// Geometry: a vertical spine sits against the front of the board (y=0 plane is
// the board's front face). Two pegs poke back (-Y) into holes one pitch apart;
// the lower peg hooks down behind the board to lock. A front arm sweeps out.
spine_h = pitch + bar_r * 2;

union() {
    // Vertical spine against the board face (swept for a rounded body).
    spine_pts = [ for (z = [0:2:spine_h]) [0, bar_r, z] ];
    path_sweep(circle(r = bar_r), deduplicate(spine_pts), closed = false);

    // Top peg: straight back into the upper hole.
    top_pts = [ for (y = [bar_r : -2 : -(board_t + 3)]) [0, y, spine_h] ];
    path_sweep(circle(r = peg_d/2), deduplicate(top_pts), closed = false);

    // Bottom peg: back into the lower hole then curl DOWN behind the board so
    // the hook cannot pull straight out (the classic pegboard lock).
    bot_back = [ for (y = [bar_r : -2 : -(board_t + 2)]) [0, y, 0] ];
    bot_curl = [ for (a = [0:12:90]) [0,
                                      -(board_t + 2) - sin(a) * 4,
                                      -(1 - cos(a)) * 6] ];
    path_sweep(circle(r = peg_d/2), deduplicate(concat(bot_back, bot_curl)), closed = false);

    // Front holding arm sweeps out (+Y) from the lower spine and tips up.
    arm = concat(
        [ for (y = [bar_r : 2 : arm_reach]) [0, y, bar_r] ],
        [ for (a = [0:10:120]) [0, arm_reach + sin(a) * bar_r * 1.5, bar_r + (1 - cos(a)) * bar_r * 1.5] ]
    );
    path_sweep(circle(r = bar_r), deduplicate(arm), closed = false);
}
```

#### Screw-mount wall hook

A simple screw-mount wall hook: a flat rounded backplate with a countersunk screw hole and an upward-curving arm to hang coats, bags, or towels. Tune the backplate size, arm reach, hook depth, and screw bore.

```openscad
// Screw-mount wall hook
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>

plate_w   = 30;   // backplate width (against wall)
plate_h   = 44;   // backplate height
plate_t   = 5;    // backplate thickness
arm_reach = 26;   // how far the hook sticks out from the wall
arm_r     = 5;    // radius of the round hook arm
screw_d   = 4.5;  // screw clearance bore diameter
$fn = 64;

union() {
    // Flat backplate flush against the wall (wall face = back, at y=0 plane).
    difference() {
        cuboid([plate_w, plate_t, plate_h], rounding = 4, edges = "Y", anchor = BOTTOM + BACK);
        // Countersunk screw hole near the top, drilled through the plate (along +Y).
        up(plate_h - 9)
            ycyl(h = plate_t * 3, d = screw_d, anchor = CENTER);
        // Countersink cone on the front face.
        up(plate_h - 9) back(0.1)
            ycyl(h = screw_d, d1 = screw_d * 2.2, d2 = screw_d, anchor = BACK);
        // Lower screw hole for stability.
        up(9)
            ycyl(h = plate_t * 3, d = screw_d, anchor = CENTER);
        up(9) back(0.1)
            ycyl(h = screw_d, d1 = screw_d * 2.2, d2 = screw_d, anchor = BACK);
    }

    // The hook arm: a curved tube sweeping out from the lower-middle of the plate
    // and curling upward to catch the hung item.
    arm_z = plate_h * 0.35;
    pts = [
        for (a = [0:6:90])
            [ sin(a) * arm_reach, plate_t * 0.5 + (1 - cos(a)) * (arm_reach * 0.6) ]
    ];
    // Build the arm as a swept circle along an L-then-up path in the XZ-ish plane.
    translate([0, plate_t * 0.5, arm_z])
    yrot(0)
    path_sweep(circle(r = arm_r),
        path3d([ for (p = pts) [p[0], 0, p[1]] ]),
        closed = false);
}
```

#### Wing nut

A real wing nut: an internally threaded barrel with two flat finger wings for hand-tightening, built from the BOSL2 threading and shape libraries. A single printable solid. Tune the thread size and pitch, body height, and wing span.

```openscad
// Wing nut
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/threading.scad>

thread_d  = 8;     // thread major diameter (mm)
pitch     = 1.25;  // thread pitch (mm)
body_h    = 10;    // height of the threaded barrel (mm)
wing_span = 38;    // total tip-to-tip span across the wings (mm)
wing_h    = 7;     // wing height (mm)
$fn = 72;

difference() {
    union() {
        // Threaded barrel.
        cyl(d = thread_d + 6, h = body_h, rounding = 1, anchor = BOTTOM);
        // Two finger wings.
        up(wing_h / 2)
            prismoid(size1 = [wing_span, 5], size2 = [wing_span * 0.55, 3.5],
                     h = wing_h, rounding = 1.0, anchor = CENTER);
    }
    // Internal thread through the barrel.
    down(0.01)
        threaded_rod(d = thread_d, l = body_h + 0.02, pitch = pitch,
                     internal = true, bevel = false, blunt_start = false,
                     anchor = BOTTOM);
}
```

#### Worm gear

The wheel half of a worm drive: a throated spur gear whose teeth are curved to wrap around a mating worm screw. Pairing a worm with this wheel gives a high reduction, self-locking right-angle drive. Tune the module, tooth count, worm diameter, worm starts, and bore.

```openscad
// Worm gear
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/gears.scad>

teeth       = 30;  // teeth on the worm wheel
gear_mod    = 2;   // tooth module in mm (match the worm)
worm_d      = 15;  // pitch diameter of the mating worm in mm
worm_starts = 1;   // number of thread starts on the worm
shaft_d     = 6;   // center shaft bore diameter in mm
$fn = 48;

worm_gear(mod = gear_mod, teeth = teeth, worm_diam = worm_d,
          worm_starts = worm_starts, shaft_diam = shaft_d,
          pressure_angle = 20);
```

#### Worm screw (drive)

The screw half of a worm drive: a helical thread that turns the mating worm wheel. One full turn of the worm advances the wheel by one tooth (per start), giving a large, self-locking reduction. Print this plus the worm gear wheel. Tune the module, length, diameter, and thread starts.

```openscad
// Worm screw (drive)
//
// Source: BOSL2 (BelfrySCAD) — BSD-2-Clause
// https://github.com/BelfrySCAD/BOSL2
// Generated from the library above, used under its license.

include <BOSL2/std.scad>
include <BOSL2/gears.scad>

gear_mod    = 2;   // tooth module in mm (match the worm wheel)
worm_d      = 15;  // pitch diameter in mm (match the wheel's worm_diam)
worm_len    = 40;  // worm length in mm
worm_starts = 1;   // number of thread starts
$fn = 48;

worm(mod = gear_mod, d = worm_d, l = worm_len, starts = worm_starts,
     pressure_angle = 20);
```

#### res = accumulate(func, list, [init]);

Applies a 2-arg function cumulatively to the items of a list, returning a list of every result.

```openscad
echo(accumulate(function (a,b) a+b, [3,4,5],0));  // ECHO: [3,7,12]
```

#### acme_threaded_nut(nutwidth, id, h|height|thickness, tpi|pitch=, [shape=], ...) [ATTACHMENTS];

Creates an ACME threaded nut.

```openscad
acme_threaded_nut(nutwidth=16, id=3/8*INCH, h=8, tpi=8, $slop=0.05);
acme_threaded_nut(nutwidth=16, id=3/8*INCH, h=10, tpi=12, starts=3, $slop=0.1, $fa=1, $fs=1, ibevel=false);
acme_threaded_nut(nutwidth=16, id=3/8*INCH, h=10, tpi=12, starts=3, $slop=0.1, $fa=1, $fs=1, blunt_start=false);
```

#### acme_threaded_rod(d, l|length, tpi|pitch=, [internal=], ...) [ATTACHMENTS];

Creates an ACME threaded rod.

```openscad
projection(cut=true)
acme_threaded_rod(d=10, l=15, pitch=2, orient=BACK);
```

#### a = acosh(x);

Returns the hyperbolic arc-cosine of the given value.

```openscad
// acosh — Returns the hyperbolic arc-cosine of the given value.
// Usage: a = acosh(x);
```

#### v_new = add_scalar(v, s);

Adds a scalar value to every item in a vector.

```openscad
a = add_scalar([1,2,3],3);            // Returns: [4,5,6]
```

#### hyp = adj_ang_to_hyp(adj,ang);

Returns the hypotenuse length from the length of the adjacent side and the reference reference angle.

```openscad
hyp = adj_ang_to_hyp(4,60);  // Returns: 8
```

#### opp = adj_ang_to_opp(adj,ang);

Returns the opposite side length from the length of the adjacent side and the reference reference angle.

```openscad
opp = adj_ang_to_opp(8,45);  // Returns: 8
```

#### ang = adj_hyp_to_ang(adj,hyp);

Returns the reference angle from the lengths of the adjacent side and the hypotenuse.

```openscad
ang = adj_hyp_to_ang(4,8);  // Returns: 60 degrees
```

#### opp = adj_hyp_to_opp(adj,hyp);

Returns the opposite side length from the lengths of the adjacent side and the hypotenuse.

```openscad
opp = adj_hyp_to_opp(4,5);  // Returns: 3
```

#### ang = adj_opp_to_ang(adj,opp);

Returns the reference angle from the lengths of the adjacent and opposite sides.

```openscad
ang = adj_opp_to_ang(sqrt(3)/2,0.5);  // Returns: 30 degrees
```

#### hyp = adj_opp_to_hyp(adj,opp);

Returns the hypotenuse length from the lengths of the adjacent and opposite sides.

```openscad
hyp = adj_opp_to_hyp(3,4);  // Returns: 5
```

#### mat = affine2d_identify();

Returns a 2D (3x3) identity transformation matrix.

```openscad
mat = affine2d_identity();
// Returns:
//   [
//     [1, 0, 0],
//     [0, 1, 0],
//     [0, 0, 1]
//   ]
```

#### mat = affine2d_mirror(v);

Returns a 2D (3x3) reflection transformation matrix.

```openscad
mat = affine2d_mirror([0,1]);
// Returns:
//   [
//     [ 1, 0, 0],
//     [ 0,-1, 0],
//     [ 0, 0, 1]
//   ]
```

#### mat = affine2d_scale(v);

Returns a 2D (3x3) scaling transformation matrix.

```openscad
mat = affine2d_scale([3,4]);
// Returns:
//   [
//     [3, 0, 0],
//     [0, 4, 0],
//     [0, 0, 1]
//   ]
```

#### mat = affine2d_skew(xa);

Returns a 2D (3x3) skewing transformation matrix.

```openscad
mat = affine2d_skew(xa=45,ya=-45);
// Returns approximately:
//   [
//     [ 1, 1, 0],
//     [-1, 1, 0],
//     [ 0, 0, 1]
//   ]
```

#### mat = affine2d_translate(v);

Returns a 2D (3x3) translation transformation matrix.

```openscad
mat = affine2d_translate([30,40]);
// Returns:
//   [
//     [1, 0, 30],
//     [0, 1, 40],
//     [0, 0,  1]
//   ]
```

#### mat = affine2d_zrot(ang);

Returns a 2D (3x3) rotation transformation matrix.

```openscad
mat = affine2d_zrot(90);
// Returns:
//   [
//     [0,-1, 0],
//     [1, 0, 0],
//     [0, 0, 1]
//   ]
```

#### mat = affine3d_identity();

Returns a 3D (4x4) identity transformation matrix.

```openscad
mat = affine2d_identity();
// Returns:
//   [
//     [1, 0, 0, 0],
//     [0, 1, 0, 0],
//     [0, 0, 1, 0],
//     [0, 0, 0, 1]
//   ]
```

#### mat = affine3d_mirror(v);

Returns a 3D (4x4) reflection transformation matrix.

```openscad
mat = affine3d_mirror([1,0,0]);
// Returns:
//   [
//     [-1, 0, 0, 0],
//     [ 0, 1, 0, 0],
//     [ 0, 0, 1, 0],
//     [ 0, 0, 0, 1]
//   ]
```

#### mat = affine3d_rot_by_axis(u, ang);

Returns a 3D (4x4) arbitrary-axis rotation transformation matrix.

```openscad
mat = affine3d_rot_by_axis([1,1,1], 120);
// Returns approx:
//   [
//     [ 0, 0, 1, 0],
//     [ 1, 0, 0, 0],
//     [ 0, 1, 0, 0],
//     [ 0, 0, 0, 1]
//   ]
```

#### mat = affine3d_rot_from_to(from, to);

Returns a 3D (4x4) tilt rotation transformation matrix.

```openscad
mat = affine3d_rot_from_to(UP, RIGHT);
// Returns:
//   [
//     [ 0, 0, 1, 0],
//     [ 0, 1, 0, 0],
//     [-1, 0, 0, 0],
//     [ 0, 0, 0, 1]
//   ]
```

#### mat = affine3d_scale(v);

Returns a 3D (4x4) scaling transformation matrix.

```openscad
mat = affine3d_scale([3,4,5]);
// Returns:
//   [
//     [3, 0, 0, 0],
//     [0, 4, 0, 0],
//     [0, 0, 5, 0],
//     [0, 0, 0, 1]
//   ]
```

#### mat = affine3d_skew([sxy=], [sxz=], [syx=], [syz=], [szx=], [szy=]);

Returns a 3D (4x4) skewing transformation matrix.

```openscad
mat = affine3d_skew(sxy=2,szx=3);
// Returns:
//   [
//     [ 1, 2, 0, 0],
//     [ 0, 1, 0, 0],
//     [ 0, 0, 1, 0],
//     [ 3, 0, 0, 1]
//   ]
```

#### mat = affine3d_skew_xy(xa);

Returns a 3D (4x4) XY-plane skewing transformation matrix.

```openscad
mat = affine3d_skew_xy(xa=45,ya=-45);
// Returns:
//   [
//     [ 1, 0, 1, 0],
//     [ 0, 1,-1, 0],
//     [ 0, 0, 1, 0],
//     [ 0, 0, 0, 1]
//   ]
```

#### mat = affine3d_skew_xz(xa);

Returns a 3D (4x4) XZ-plane skewing transformation matrix.

```openscad
mat = affine3d_skew_xz(xa=45,za=-45);
// Returns:
//   [
//     [ 1, 1, 0, 0],
//     [ 0, 1, 0, 0],
//     [ 0,-1, 1, 0],
//     [ 0, 0, 0, 1]
//   ]
```

#### mat = affine3d_skew_yz(ya);

Returns a 3D (4x4) YZ-plane skewing transformation matrix.

```openscad
mat = affine3d_skew_yz(ya=45,za=-45);
// Returns:
//   [
//     [ 1, 0, 0, 0],
//     [ 1, 1, 0, 0],
//     [-1, 0, 1, 0],
//     [ 0, 0, 0, 1]
//   ]
```

#### mat = affine3d_translate(v);

Returns a 3D (4x4) translation transformation matrix.

```openscad
mat = affine2d_translate([30,40,50]);
// Returns:
//   [
//     [1, 0, 0, 30],
//     [0, 1, 0, 40],
//     [0, 0, 1, 50]
//     [0, 0, 0,  1]
//   ]
```

#### mat = affine3d_xrot(ang);

Returns a 3D (4x4) X-axis rotation transformation matrix.

```openscad
mat = affine3d_xrot(90);
// Returns:
//   [
//     [1, 0, 0, 0],
//     [0, 0,-1, 0],
//     [0, 1, 0, 0],
//     [0, 0, 0, 1]
//   ]
```

#### mat = affine3d_yrot(ang);

Returns a 3D (4x4) Y-axis rotation transformation matrix.

```openscad
mat = affine3d_yrot(90);
// Returns:
//   [
//     [ 0, 0, 1, 0],
//     [ 0, 1, 0, 0],
//     [-1, 0, 0, 0],
//     [ 0, 0, 0, 1]
//   ]
```

#### mat = affine3d_zrot(ang);

Returns a 3D (4x4) Z-axis rotation transformation matrix.

```openscad
mat = affine3d_zrot(90);
// Returns:
//   [
//     [ 0,-1, 0, 0],
//     [ 1, 0, 0, 0],
//     [ 0, 0, 1, 0],
//     [ 0, 0, 0, 1]
//   ]
```

#### PARENT() align(anchor, [align], [inside=], [inset=], [shiftout=], [overlap=]) CHILDREN;

Position children with alignment to parent edges.

```openscad
cuboid([20,35,25])
align(RIGHT)
color("lightgreen")cuboid([5,1,9]);
```

#### newpoly = align_polygon(reference, poly, [angles], [cp], [tran], [return_ind]);

Find best alignment of a 2d polygon to a reference 2d polygon over a set of transformations.

```openscad
ellipse = yscale(3,circle(r=10, $fn=32));
tri = move([-50/3,-9],
subdivide_path([[0,0], [50,0], [0,27]], 32));
aligned = align_polygon(ellipse,tri, [0:5:180]);
color("white")stroke(tri,width=.5,closed=true);
stroke(ellipse, width=.5, closed=true);
color("blue")stroke(aligned,width=.5,closed=true);
```

#### bool = all(l);

Returns true if all items in the argument list are true.

```openscad
test1 = all([0,false,undef]);  // Returns false.
test2 = all([1,false,undef]);  // Returns false.
test3 = all([1,5,true]);       // Returns true.
test4 = all([[0,0], [0,0]]);   // Returns true.
test5 = all([[0,0], [1,0]]);   // Returns true.
test6 = all([[1,1], [1,1]]);   // Returns true.
```

#### bool = all_defined(v, [recursive]);

Returns true if all items in the given array are defined.

```openscad
bool = all_defined([undef,undef,undef]);    // Returns: false
bool = all_defined([undef,42,undef]);       // Returns: false
bool = all_defined([34,42,87]);             // Returns: true
bool = all_defined([23,34,[undef]]);        // Returns: true
bool = all_defined([23,34,[undef]],recursive=true);  // Returns: false
bool = all_defined([23,34,[42]],recursive=true);     // Returns: true
```

#### b = all_equal(vec, [eps]);

Returns true if all items in a list are approximately equal to each other.

```openscad
// all_equal — Returns true if all items in a list are approximately equal to each other.
// Usage: b = all_equal(vec, [eps]);
```

#### bool = all_integer(x);

Returns true if all of the numbers in the argument are integers.

```openscad
b = all_integer(true);  // Returns: false
b = all_integer("foo"); // Returns: false
b = all_integer(4);     // Returns: true
b = all_integer(4.5);   // Returns: false
b = all_integer([]);    // Returns: false
b = all_integer([3,4,5]);   // Returns: true
b = all_integer([3,4.2,5]); // Returns: false
b = all_integer([3,[4,7],5]); // Returns: false
```

#### test = all_negative(x, [eps]);

Returns true if the value(s) given are less than zero.

```openscad
a = all_negative(-2);  // Returns: true.
b = all_negative(0);  // Returns: false.
c = all_negative(2);  // Returns: false.
d = all_negative([0,0,0]);  // Returns: false.
e = all_negative([0,1,2]);  // Returns: false.
f = all_negative([3,1,2]);  // Returns: false.
g = all_negative([3,-1,2]);  // Returns: false.
h = all_negative([-3,-1,-2]);  // Returns: true.
```

#### all_nonnegative(x, [eps]);

Returns true if the value(s) given are greater than or equal to zero.

```openscad
a = all_nonnegative(-2);  // Returns: false.
b = all_nonnegative(0);  // Returns: true.
c = all_nonnegative(2);  // Returns: true.
d = all_nonnegative([0,0,0]);  // Returns: true.
e = all_nonnegative([0,1,2]);  // Returns: true.
f = all_nonnegative([0,-1,-2]);  // Returns: false.
g = all_nonnegative([3,1,2]);  // Returns: true.
h = all_nonnegative([3,-1,2]);  // Returns: false.
i = all_nonnegative([-3,-1,-2]);  // Returns: false.
```

#### all_nonpositive(x, [eps]);

Returns true if the value(s) given are less than or equal to zero.

```openscad
a = all_nonpositive(-2);  // Returns: true.
b = all_nonpositive(0);  // Returns: true.
c = all_nonpositive(2);  // Returns: false.
d = all_nonpositive([0,0,0]);  // Returns: true.
e = all_nonpositive([0,1,2]);  // Returns: false.
f = all_nonpositive([3,1,2]);  // Returns: false.
g = all_nonpositive([3,-1,2]);  // Returns: false.
h = all_nonpositive([-3,-1,-2]);  // Returns: true.
```

#### test = all_nonzero(x, [eps]);

Returns true if the value(s) given are not approximately zero.

```openscad
a = all_nonzero(0);  // Returns: false.
b = all_nonzero(1e-3);  // Returns: true.
c = all_nonzero([0,0,0]);  // Returns: false.
d = all_nonzero([0,0,1e-3]);  // Returns: false.
e = all_nonzero([1e-3,1e-3,1e-3]);  // Returns: true.
```

#### test = all_positive(x,[eps]);

Returns true if the value(s) given are greater than zero.

```openscad
a = all_positive(-2);  // Returns: false.
b = all_positive(0);  // Returns: false.
c = all_positive(2);  // Returns: true.
d = all_positive([0,0,0]);  // Returns: false.
e = all_positive([0,1,2]);  // Returns: false.
f = all_positive([3,1,2]);  // Returns: true.
g = all_positive([3,-1,2]);  // Returns: false.
```

#### x = all_zero(x, [eps]);

Returns true if the value(s) given are approximately zero.

```openscad
a = all_zero(0);  // Returns: true.
b = all_zero(1e-3);  // Returns: false.
c = all_zero([0,0,0]);  // Returns: true.
d = all_zero([0,0,1e-3]);  // Returns: false.
```

#### pt = altaz_to_xyz(alt, az, r);

Convert altitude/azimuth/range to 3D cartesian coordinates.

```openscad
xyz = altaz_to_xyz(20,30,40);
xyz = altaz_to_xyz([40,60,50]);
```

#### anchor_arrow([s], [color], [flag], [anchor=], [orient=], [spin=]) [ATTACHMENTS];

Shows a 3d anchor orientation arrow.

```openscad
anchor_arrow(s=20);
```

#### anchor_arrow2d([s], [color]);

Shows a 2d anchor orientation arrow.

```openscad
anchor_arrow2d(s=20);
```

#### hyp = ang_adj_to_hyp(ang,adj);

Returns the hypotenuse length from the reference reference angle and length of the adjacent side.

```openscad
hyp = ang_adj_to_hyp(60,4);  // Returns: 8
```

#### opp = ang_adj_to_opp(ang,adj);

Returns the opposite side length from the reference reference angle and the length of the adjacent side.

```openscad
opp = ang_adj_to_opp(45,8);  // Returns: 8
```

#### adj = ang_hyp_to_adj(ang,hyp);

Returns the adjacent side length from the reference reference angle and the length of the hypotenuse.

```openscad
adj = ang_hyp_to_adj(60,8);  // Returns: 4
```

#### opp = ang_hyp_to_opp(ang,hyp);

Returns the opposite side length from the reference reference angle and the length of the hypotenuse.

```openscad
opp = ang_hyp_to_opp(30,8);  // Returns: 4
```

#### adj = ang_opp_to_adj(ang,opp);

Returns the adjacent side length from the reference reference angle and the length of the opposite side.

```openscad
adj = ang_opp_to_adj(45,8);  // Returns: 8
```

#### hyp = ang_opp_to_hyp(ang,opp);

Returns the hypotenuse length from the reference reference angle and the length of the opposite side.

```openscad
hyp = opp_ang_to_hyp(30,4);  // Returns: 8
```

#### bool = any(l);

Returns true if any item in the argument list is true.

```openscad
any([0,false,undef]);  // Returns false.
any([1,false,undef]);  // Returns true.
any([1,5,true]);       // Returns true.
any([[0,0], [0,0]]);   // Returns true.
any([[0,0], [1,0]]);   // Returns true.
```

#### bool = any_defined(v, [recursive]);

Returns true if any item in the argument list is not `undef`.

```openscad
bool = any_defined([undef,undef,undef]);    // Returns: false
bool = any_defined([undef,42,undef]);       // Returns: true
bool = any_defined([34,42,87]);             // Returns: true
bool = any_defined([undef,undef,[undef]]);  // Returns: true
bool = any_defined([undef,undef,[undef]],recursive=true);  // Returns: false
bool = any_defined([undef,undef,[42]],recursive=true);     // Returns: true
```

#### pts = apply(transform, points);

Applies a transformation matrix to a point, list of points, array of points, or a VNF.

```openscad
path1 = path3d(circle(r=40));
tmat = xrot(45);
path2 = apply(tmat, path1);
#stroke(path1,closed=true);
stroke(path2,closed=true);
```

#### apply_folding_hinges_and_snaps(thick, [foldangle=], [hinges=], [snaps=], [sockets=], [snaplen=], [snapdiam=], [hingegap=], [layerheight=], [$slop=]) CHILDREN;

Adds snap shapes and removes living hinges from a child shape.

```openscad
size=100;
apply_folding_hinges_and_snaps(
thick=3, foldangle=acos(1/3),
hinges=[
for (a=[0,120,240], b=[-size/2,size/4]) each [
[200, polar_to_xy(b,a), a+90]
]
],
snaps=[
for (a=[0,120,240]) each [
[rot(a,p=[ size/4, 0        ]), a+90],
[rot(a,p=[-size/2,-size/2.33]), a-90]
]
],
sockets=[
for (a=[0,120,240]) each [
[rot(a,p=[ size/4, 0        ]), a+90],
[rot(a,p=[-size/2, size/2.33]), a+90]
]
]
) {
$fn=3;
difference() {
cylinder(r=size-1, h=3);
down(0.01) cylinder(r=size/4.5, h=3.1, spin=180);
down(0.01) for (a=[0:120:359.9]) zrot(a) right(size/2) cylinder(r=size/4.5, h=3.1);
}
}
```

#### test = approx(a, b, [eps])

Returns true if two values are equal to within a small epsilon value.

```openscad
test1 = approx(-0.3333333333,-1/3);  // Returns: true
test2 = approx(0.3333333333,1/3);    // Returns: true
test3 = approx(0.3333,1/3);          // Returns: false
test4 = approx(0.3333,1/3,eps=1e-3); // Returns: true
test5 = approx(PI,3.1415926536);     // Returns: true
test6 = approx([0,0,sin(45)],[0,0,sqrt(2)/2]);  // Returns: true
```

#### path=arc(n, r|d=, angle);

Draws a 2D pie-slice or returns 2D or 3D path forming an arc.

```openscad
arc(n=4, r=30, angle=30, wedge=true);
arc(r=30, angle=30, wedge=true);
arc(d=60, angle=30, wedge=true);
arc(d=60, angle=120);
arc(d=60, angle=120, wedge=true);
arc(r=30, angle=[75,135], wedge=true);
arc(r=30, start=45, angle=75, wedge=true);
arc(width=60, thickness=20);
arc(cp=[-10,5], points=[[20,10],[0,35]], wedge=true);
arc(points=[[30,-5],[20,10],[-10,20]], wedge=true);
```

#### arc_copies(n, r|d=, [sa=], [ea=], [rot=]) CHILDREN;

Distributes duplicates of children along an arc.

```openscad
#cube(size=[10,3,3],center=true);
arc_copies(d=40, n=5) cube(size=[10,3,3],center=true);
```

#### are_ends_equal(list, [eps]);

Returns true if the first and last items in a list are approximately equal.

```openscad
// are_ends_equal — Returns true if the first and last items in a list are approximately equal.
// Usage: are_ends_equal(list, [eps]);
```

#### bool = are_points_on_plane(points, plane, [eps]);

Determine if all of the listed points are on a plane.

```openscad
// are_points_on_plane — Determine if all of the listed points are on a plane.
// Usage: bool = are_points_on_plane(points, plane, [eps]);
```

#### bool = are_polygons_equal(poly1, poly2, [eps])

Check if two polygons (not necessarily in the same point order) are equal.

```openscad
are_polygons_equal(pentagon(r=4),
rot(360/5, p=pentagon(r=4))); // returns true
are_polygons_equal(pentagon(r=4),
rot(90, p=pentagon(r=4)));    // returns false
```

#### b = are_regions_equal(region1, region2, [either_winding])

Returns true if given regions are the same polygons.

```openscad
// are_regions_equal — Returns true if given regions are the same polygons.
// Usage: b = are_regions_equal(region1, region2, [either_winding])
```

#### a = asinh(x);

Returns the hyperbolic arc-sine of the given value.

```openscad
// asinh — Returns the hyperbolic arc-sine of the given value.
// Usage: a = asinh(x);
```

#### assert_approx(got, expected, [info]);

Assert that a value is approximately what was expected.

```openscad
assert_approx(1/3, 0.333333333333333, str("number=",1,", denom=",3));
```

#### assert_equal(got, expected, [info]);

Assert that a value is expected.

```openscad
assert_equal(3*9, 27, str("a=",3,", b=",9));
```

#### newpoly = associate_vertices(polygons, split);

Create vertex association to control how {{skin()}} links vertices.

```openscad
sq = regular_ngon(4,side=2);
hex = apply(rot(15),hexagon(side=2));
skin([sq,hex], slices=10, refine=10, method="distance", z=[0,4]);
```

#### a = atanh(x);

Returns the hyperbolic arc-tangent of the given value.

```openscad
// atanh — Returns the hyperbolic arc-tangent of the given value.
// Usage: a = atanh(x);
```

#### PARENT() attach(parent, child, [align=], [spin=], [overlap=], [inside=], [inset=], [shiftout=]) CHILDREN;

Attaches children to a parent object at an anchor point and with anchor orientation.

```openscad
cuboid(50)
attach(TOP,BOT) cylinder(d1=30,d2=15,h=25);
```

#### geom = attach_geom(...);

Returns the internal geometry description of an attachable object.

```openscad
geom = attach_geom();
```

#### PARENT() attach_part(name, [ind]) CHILDREN;

Select a named attachable part for subsequent attachment operations

```openscad
tube(ir1=10,ir2=20,h=20, wall=3){
color("lightblue")attach(RIGHT,BOT) cuboid(4);
color("pink")
attach_part("inside")
attach(BACK,BOT) cuboid(4);
}
```

#### PARENT() attach_prism(profile, anchor, [fillet], [rounding], [l=/h=/length=/height=], [endpoint=], [T=], [shift=], [scale=], [inside=], [n=], [n_base=], [n_end=], [k=], [k_base=], [k_end=], [overlap=], [uniform=], [smooth_normals=], [edge_r=], [edge_joint=], [edge_k=], [debug=] ) CHILDREN;

Attach a filleted prism with optional rounded end

```openscad
cuboid(20)
attach_prism(circle(r=4,$fn=32), TOP, fillet=2, rounding=1.5, l=7);
```

#### attachable(anchor, spin, two_d=true, size=, [size2=], [shift=], [override=], ...) {OBJECT; children();}

Manages the anchoring, spin, orientation, and attachments for an object.

```openscad
attachable(anchor, spin, orient, size=size) {
cube(size, center=true);
children();
}
```

#### x = auto_profile_shift(teeth, [pressure_angle], [helical], [profile_shift=]);

Returns the recommended profile shift for a gear.

```openscad
// auto_profile_shift — Returns the recommended profile shift for a gear.
// Usage: x = auto_profile_shift(teeth, [pressure_angle], [helical], [profile_shift=]);
```

#### back(y) CHILDREN;

Translates children backward (Y+).

```openscad
#sphere(d=10);
back(20) sphere(d=10);
```

#### back_half([s], [y]) CHILDREN;

Masks the front half of an object along the X-Z plane, leaving the back half.

```openscad
back_half() sphere(r=20);
back_half(y=8) sphere(r=20);
```

#### x = back_substitute(R, b, [transpose]);

Solve an upper triangular system, Rx=b.

```openscad
// back_substitute — Solve an upper triangular system, Rx=b.
// Usage: x = back_substitute(R, b, [transpose]);
```

#### ball_bearing(trade_size, [id=], [od=], [width=], [shield=], [flange=], [fd=], [fw=], [rounding=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];

Creates a standardized ball bearing assembly.

```openscad
ball_bearing("608", $fn=72);
```

#### info = ball_bearing_info(trade_size);

Returns size info for a standardized ball bearing assembly.

```openscad
// ball_bearing_info — Returns size info for a standardized ball bearing assembly.
// Usage: info = ball_bearing_info(trade_size);
```

#### ball_screw_rod(d, l|length, pitch, [ball_diam], [ball_arc], [internal=], ...) [ATTACHMENTS];

Creates a ball screw rod.

```openscad
projection(cut=true) ball_screw_rod(d=10, l=15, pitch=5, ball_diam=4, ball_arc=100, orient=BACK, $fn=24, blunt_start=false);
```

#### bent_cutout_mask(r|radius, thickness, path);

Create a mask for making a round-edged cutout in a cylindrical shell.

```openscad
bent_cutout_mask(10, 1, apply(xscale(3),circle(r=3)),$fn=64);
```

#### gear_dist(mod=|diam_pitch=|circ_pitch=, teeth, mate_teeth, [shaft_angle], [shaft_diam], [face_width=], [hide=], [spiral=], [cutter_radius=], [right_handed=], [pressure_angle=], [backing=|thickness=|bottom=], [cone_backing=], [backlash=], [slices=], [internal=], [gear_spin=], ...) [ATTACHMENTS];

Creates a straight, zerol, or spiral bevel gear.

```openscad
bevel_gear(
circ_pitch=5, teeth=36, mate_teeth=36,
shaft_diam=5, spiral=0
);
```

#### ang = bevel_pitch_angle(teeth, mate_teeth, [drive_angle=]);

Returns the pitch cone angle for a bevel gear.

```openscad
ang = bevel_pitch_angle(teeth=18, mate_teeth=30);
```

#### pts = bez_begin(pt, a, r, [p=]);

Calculates starting bezier path control points.

```openscad
bezpath = flatten([
bez_begin([-50,  0],  45,20),
bez_tang ([  0,  0],-135,20),
bez_joint([ 20,-25], 135, 90, 10, 15),
bez_end  ([ 50,  0], -90,20),
]);
debug_bezier(bezpath);
```

#### pts = bez_end(pt, a, r, [p=]);

Calculates ending bezier path control points.

```openscad
// bez_end — Calculates ending bezier path control points.
// Usage: pts = bez_end(pt, a, r, [p=]);
```

#### pts = bez_joint(pt, a1, a2, r1, r2, [p1=], [p2=]);

Calculates control points for a disjointed corner bezier path joint.

```openscad
// bez_joint — Calculates control points for a disjointed corner bezier path joint.
// Usage: pts = bez_joint(pt, a1, a2, r1, r2, [p1=], [p2=]);
```

#### pts = bez_tang(pt, a, r1, r2, [p=]);

Calculates control points for a smooth bezier path joint.

```openscad
// bez_tang — Calculates control points for a smooth bezier path joint.
// Usage: pts = bez_tang(pt, a, r1, r2, [p=]);
```

#### u = bezier_closest_point(bezier, pt, [max_err]);

Finds the closest position on a bezier curve to a given point.

```openscad
pt = [40,15];
bez = [[0,0], [20,40], [60,-25], [80,0]];
u = bezier_closest_point(bez, pt);
debug_bezier(bez, N=len(bez)-1);
color("red") translate(pt) sphere(r=1);
color("blue") translate(bezier_points(bez,u)) sphere(r=1);
```

#### crv = bezier_curvature(curve, u);

Returns the curvature at one or more given positions along a bezier curve.

```openscad
// bezier_curvature — Returns the curvature at one or more given positions along a bezier curve.
// Usage: crv = bezier_curvature(curve, u);
```

#### path = bezier_curve(bezier, [splinesteps], [endpoint]);

Computes a specified number of points on a bezier curve.

```openscad
bez = [[0,0], [30,30], [80,0]];
move_copies(bezier_curve(bez, 8)) sphere(r=1.5, $fn=12);
debug_bezier(bez, N=len(bez)-1);
```

#### deriv = bezier_derivative(bezier, u, [order]);

Evaluates the derivative of the bezier curve at the given point or points.

```openscad
// bezier_derivative — Evaluates the derivative of the bezier curve at the given point or points.
// Usage: deriv = bezier_derivative(bezier, u, [order]);
```

#### pathlen = bezier_length(bezier, [start_u], [end_u], [max_deflect]);

Approximate the length of part of a bezier curve.

```openscad
bez = [[0,0], [5,35], [60,-25], [80,0]];
echo(bezier_length(bez));
```

#### u = bezier_line_intersection(bezier, line);

Calculates where a bezier curve intersects a line.

```openscad
// bezier_line_intersection — Calculates where a bezier curve intersects a line.
// Usage: u = bezier_line_intersection(bezier, line);
```

#### patch = bezier_patch_flat(size, [N=], [spin=], [orient=], [trans=]);

Creates a flat bezier patch.

```openscad
patch = bezier_patch_flat(size=[100,100]);
debug_bezier_patches([patch], size=1, showcps=true);
```

#### n = bezier_patch_normals(patch, u, v);

Computes surface normals for one or more places on a bezier surface patch.

```openscad
patch = [
// u=0,v=0                                         u=1,v=0
[[-50,-50,  0], [-16,-50,  20], [ 16,-50, -20], [50,-50,  0]],
[[-50,-16, 40], [-16,-16,  20], [ 16,-16, -20], [50,-16, 70]],
[[-50, 16, 20], [-16, 16, -20], [ 16, 37,  20], [70, 16, 20]],
[[-50, 50,  0], [73, 50, -40], [ 16, 50,  20], [50, 50,  0]],
// u=0,v=1                                         u=1,v=1
];
vnf_polyhedron(bezier_vnf(patch,splinesteps=30));
uv = lerpn(0,1,12);
pts = bezier_patch_points(patch, uv, uv);
normals = bezier_patch_normals(patch, uv, uv);
for(i=idx(uv),j=idx(uv)){
stroke([pts[i][j],pts[i][j]-6*normals[i][j]], width=0.5,
endcap1="dot",endcap2="arrow2",color="blue");
}
```

#### pt = bezier_patch_points(patch, u, v);

Computes one or more specified points across a bezier surface patch.

```openscad
patch = [
[[-50,-50,  0], [-16,-50,  20], [ 16,-50,  20], [50,-50,  0]],
[[-50,-16, 20], [-16,-16,  40], [ 16,-16,  40], [50,-16, 20]],
[[-50, 16, 20], [-16, 16,  40], [ 16, 16,  40], [50, 16, 20]],
[[-50, 50,  0], [-16, 50,  20], [ 16, 50,  20], [50, 50,  0]]
];
debug_bezier_patches(patches=[patch], size=1, showcps=true);
pt = bezier_patch_points(patch, 0.6, 0.75);
translate(pt) color("magenta") sphere(d=3, $fn=12);
```

#### rpatch = bezier_patch_reverse(patch);

Reverses the orientation of a bezier patch.

```openscad
// bezier_patch_reverse — Reverses the orientation of a bezier patch.
// Usage: rpatch = bezier_patch_reverse(patch);
```

#### pt = bezier_points(bezier, u);

Computes one or more specified points along a bezier curve.

```openscad
bez = [[0,0], [30,30], [80,0]];
debug_bezier(bez, N=len(bez)-1);
translate(bezier_points(bez, 0.3)) color("red") sphere(1);
```

#### vnf = bezier_sheet(patch, delta, [splinesteps=], [style=]);

Creates a thin sheet from a bezier patch by extruding in normal to the patch

```openscad
patch = [
// u=0,v=0                                         u=1,v=0
[[-50,-50,  0], [-16,-50,  20], [ 16,-50, -20], [50,-50,  0]],
[[-50,-16, 20], [-16,-16,  20], [ 16,-16, -20], [50,-16, 20]],
[[-50, 16, 20], [-16, 16, -20], [ 16, 16,  20], [50, 16, 20]],
[[-50, 50,  0], [-16, 50, -20], [ 16, 50,  20], [50, 50,  0]],
// u=0,v=1                                         u=1,v=1
];
vnf_polyhedron(bezier_sheet(patch, [0,-10]));
```

#### bezier_sweep(shape, bezier, [splinesteps], [method], [endpoint=], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [scale=], [scale_by_length=], [last_normal=], [caps=], [style=], [convexity=], [anchor=], [cp=], [spin=], [orient=], [atype=]) [ATTACHMENTS];

Sweep a 2d polygon path along a bezier with exact tangents

```openscad
$fn=32;   
bez = [[0,0,5],
[0,0,10],
[15,7,9],
[17,2,4],       
];
color("lightblue")
cyl(r=2,h=5,anchor=BOT);
path_sweep(circle(r=2),
bezier_curve(bez, 6));
```

#### tanvec = bezier_tangent(bezier, u);

Calculates unit tangent vectors along the bezier curve at one or more given positions.

```openscad
// bezier_tangent — Calculates unit tangent vectors along the bezier curve at one or more given positions.
// Usage: tanvec = bezier_tangent(bezier, u);
```

#### vnf = bezier_vnf(patches, [splinesteps], [style]);

Generates a (probably non-manifold) VNF for one or more bezier surface patches.

```openscad
patch = [
// u=0,v=0                                         u=1,v=0
[[-50,-50,  0], [-16,-50,  20], [ 16,-50, -20], [50,-50,  0]],
[[-50,-16, 20], [-16,-16,  20], [ 16,-16, -20], [50,-16, 20]],
[[-50, 16, 20], [-16, 16, -20], [ 16, 16,  20], [50, 16, 20]],
[[-50, 50,  0], [-16, 50, -20], [ 16, 50,  20], [50, 50,  0]],
// u=0,v=1                                         u=1,v=1
];
vnf = bezier_vnf(patch, splinesteps=16);
vnf_polyhedron(vnf);
```

#### vnf = bezier_vnf_degenerate_patch(patch, [splinesteps], [reverse]);

Generates a VNF for a degenerate bezier surface patch.

```openscad
splinesteps=8;
patch=[
repeat([-12.5, 12.5, 15],5),
[[-6.25, 11.25, 15], [-6.25, 8.75, 15], [-6.25, 6.25, 15], [-8.75, 6.25, 15], [-11.25, 6.25, 15]],
[[0, 10, 15], [0, 5, 15], [0, 0, 15], [-5, 0, 15], [-10, 0, 15]],
[[0, 10, 8.75], [0, 5, 8.75], [0, 0, 8.75], [-5, 0, 8.75], [-10, 0, 8.75]],
[[0, 10, 2.5], [0, 5, 2.5], [0, 0, 2.5], [-5, 0, 2.5], [-10, 0, 2.5]]
];
vnf_wireframe((bezier_vnf(patch, splinesteps)),width=0.1);
color("red")move_copies(flatten(patch)) sphere(r=0.3,$fn=9);
```

#### bezpath = bezpath_close_to_axis(bezpath, [axis], [N]);

Closes a 2D bezier path to the specified axis.

```openscad
bez = [[50,30], [40,10], [10,50], [0,30],
[-10, 10], [-30,10], [-50,20]];
closed = bezpath_close_to_axis(bez);
debug_bezier(closed);
```

#### res = bezpath_closest_point(bezpath, pt, [N], [max_err]);

Finds the closest point on a bezier path to a given point.

```openscad
pt = [100,0];
bez = [[0,0], [20,40], [60,-25], [80,0],
[100,25], [140,25], [160,0]];
pos = bezpath_closest_point(bez, pt);
xy = bezpath_points(bez,pos[0],pos[1]);
debug_bezier(bez, N=3);
color("red") translate(pt) sphere(r=1);
color("blue") translate(xy) sphere(r=1);
```

#### path = bezpath_curve(bezpath, [splinesteps], [N], [endpoint], [order=])

Converts bezier path into a path of points.

```openscad
bez = [
[0,0], [-5,30],
[20,60], [50,50], [110,30],
[60,25], [70,0], [80,-25],
[80,-50], [50,-50]
];
path = bezpath_curve(bez);
stroke(path,dots=true,dots_color="red");
```

#### plen = bezpath_length(path, [N], [max_deflect]);

Approximate the length of a bezier path.

```openscad
// bezpath_length — Approximate the length of a bezier path.
// Usage: plen = bezpath_length(path, [N], [max_deflect]);
```

#### bezpath = bezpath_offset(offset, bezier, [N]);

Forms a closed bezier path loop with a translated and reversed copy of itself.

```openscad
bez = [[50,30], [40,10], [10,50], [0,30], [-10, 10], [-30,10], [-50,20]];
closed = bezpath_offset([0,-5], bez);
debug_bezier(closed);
```

#### pt = bezpath_points(bezpath, curveind, u, [N]);

Computes one or more specified points along a bezier path.

```openscad
// bezpath_points — Computes one or more specified points along a bezier path.
// Usage: pt = bezpath_points(bezpath, curveind, u, [N]);
```

#### bezier_sweep(shape, bezier, [splinesteps], [method], [endpoint=], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [scale=], [scale_by_length=], [last_normal=], [caps=], [style=], [convexity=], [anchor=], [cp=], [spin=], [orient=], [atype=]) [ATTACHMENTS];

Sweep a 2d polygon path along a bezier path with exact tangents

```openscad
bezpath = flatten([
bez_begin([0,0,0], UP, 3),
bez_tang([0,0,1],UP,8,p=52),
bez_end  ([8,9,3], FWD,10)
]);
cyl(d=4,h=3,anchor=TOP,$fn=12);
path_sweep(circle(r=2,$fn=12),
bezpath_curve(bezpath));
```

#### x = bilerp(pts, x, y);

Bi-linear interpolation between four values

```openscad
// bilerp — Bi-linear interpolation between four values
// Usage: x = bilerp(pts, x, y);
```

#### x = binomial(n);

Returns the binomial coefficients of the integer `n`.

```openscad
x = binomial(3);  // Returns: [1,3,3,1]
y = binomial(4);  // Returns: [1,4,6,4,1]
z = binomial(6);  // Returns: [1,6,15,20,15,6,1]
```

#### x = binomial_coefficient(n, k);

Returns the `k`-th binomial coefficient of the integer `n`.

```openscad
x = binomial_coefficient(3,2);  // Returns: 3
y = binomial_coefficient(10,6); // Returns: 210
```

#### idx = binsearch(key,list, [cmp]);

Does a binary search of a sorted list to find the index of a given value.

```openscad
items = unique(rands(0,100,10000));
idx = binsearch(44, items);
```

#### bmat = block_matrix([[M11, M12,...],[M21, M22,...], ... ]);

Make a new matrix from a block of matrices.

```openscad
A = [[1,2],
[3,4]];
B = ident(2);
C = block_matrix([[A,B],[B,A],[A,B]]);
// Returns:
//        [[1, 2, 1, 0],
//         [3, 4, 0, 1],
//         [1, 0, 1, 2],
//         [0, 1, 3, 4],
//         [1, 2, 1, 0],
//         [3, 4, 0, 1]]);
D = block_matrix([[A,B], ident(4)]);
// Returns:
//        [[1, 2, 1, 0],
//         [3, 4, 0, 1],
//         [1, 0, 0, 0],
//         [0, 1, 0, 0],
//         [0, 0, 1, 0],
//         [0, 0, 0, 1]]);
E = [["one", "two"], [3,4]];
F = block_matrix([[E,E]]);
// Returns:
//        [["one", "two", "one", "two"],
//         [    3,     4,     3,     4]]
```

#### bosl_required(version);

Asserts that the current version of the library is at least the given version.

```openscad
// bosl_required — Asserts that the current version of the library is at least the given version.
// Usage: bosl_required(version);
```

#### ver = bosl_version();

Returns the BOSL2 version as a list.

```openscad
// bosl_version — Returns the BOSL2 version as a list.
// Usage: ver = bosl_version();
```

#### ver = bosl_version_num();

Returns the BOSL2 version as a float.

```openscad
// bosl_version_num — Returns the BOSL2 version as a float.
// Usage: ver = bosl_version_num();
```

#### ver = bosl_version_str();

Returns the BOSL2 version as a string.

```openscad
// bosl_version_str — Returns the BOSL2 version as a string.
// Usage: ver = bosl_version_str();
```

#### bottle_adapter_cap_to_cap(wall, [texture]) [ATTACHMENTS];

Creates a generic adaptor between a cap and a cap.

```openscad
bottle_adapter_cap_to_cap();
```

#### bottle_adapter_neck_to_cap(wall, [texture], ...) [ATTACHMENTS];

Creates a generic adaptor between a neck and a cap.

```openscad
bottle_adapter_neck_to_cap();
```

#### bottle_adapter_neck_to_neck(...) [ATTACHMENTS];

Creates a generic adaptor between a neck and a neck.

```openscad
bottle_adapter_neck_to_neck();
```

#### bottom_half([s], [z]) CHILDREN;

Masks the top half of an object along the X-Y plane, leaving the bottom half.

```openscad
bottom_half() sphere(r=20);
bottom_half(z=-10) sphere(r=20);
```

#### bounding_box([excess],[planar]) CHILDREN;

Creates the smallest bounding box that contains all the children.

```openscad
module shapes() {
translate([10,8,4]) cube(5);
translate([3,0,12]) cube(2);
}
#bounding_box() shapes();
shapes();
```

#### sublist = bselect(list, index);

Select list items using boolean index list.

```openscad
a = bselect([3,4,5,6,7], [false,true,true,false,true]);  // Returns: [4,5,7]
```

#### bspp_threaded_rod(size, [internal=], ...) [ATTACHMENTS];

Creates British Standard Parallel Pipe (BSPP) threading.

```openscad
bspp_threaded_rod(size=3/8, length = 10, $fn=72);
bspp_threaded_rod(size=1/2, length = 10, $fn=72, bevel=true);
bspp_threaded_rod(size=1/2, length = 10, left_handed=true, $fn=72);
bspp_threaded_rod(size=3/4, length = 10, internal=true, $fn=96);
```

#### buttress_threaded_nut(nutwidth, id, h|height|thickness, pitch, ...) [ATTACHMENTS];

Creates a buttress-threaded nut.

```openscad
buttress_threaded_nut(nutwidth=16, id=8, h=8, pitch=1.25, left_handed=true, $slop=0.05, $fa=1, $fs=1);
```

#### buttress_threaded_rod(d, l|length, pitch, [internal=], ...) [ATTACHMENTS];

Creates a buttress-threaded rod.

```openscad
projection(cut=true)
buttress_threaded_rod(d=10, l=15, pitch=2, orient=BACK);
```

#### w = c_conj(z)

Returns the complex conjugate of the input.

```openscad
// c_conj — Returns the complex conjugate of the input.
// Usage: w = c_conj(z)
```

#### x = c_div(z1,z2)

Divides two complex numbers.

```openscad
// c_div — Divides two complex numbers.
// Usage: x = c_div(z1,z2)
```

#### I = c_ident(n)

Returns an n by n complex identity matrix.

```openscad
// c_ident — Returns an n by n complex identity matrix.
// Usage: I = c_ident(n)
```

#### x = c_imag(z)

Returns the imaginary part of a complex number, vector or matrix..

```openscad
// c_imag — Returns the imaginary part of a complex number, vector or matrix..
// Usage: x = c_imag(z)
```

#### c = c_mul(z1,z2)

Multiplies two complex numbers.

```openscad
// c_mul — Multiplies two complex numbers.
// Usage: c = c_mul(z1,z2)
```

#### n = c_norm(z)

Returns the norm of a complex number or vector.

```openscad
// c_norm — Returns the norm of a complex number or vector.
// Usage: n = c_norm(z)
```

#### x = c_real(z)

Returns the real part of a complex number, vector or matrix..

```openscad
// c_real — Returns the real part of a complex number, vector or matrix..
// Usage: x = c_real(z)
```

#### path = catenary(width, droop=|angle=, n=);

Returns a 2D Catenary chain or arch path.

```openscad
stroke(catenary(100, droop=30));
```

#### newpoly = ccw_polygon(poly);

Return counter-clockwise version of a polygon.

```openscad
// ccw_polygon — Return counter-clockwise version of a polygon.
// Usage: newpoly = ccw_polygon(poly);
```

#### c = centroid(object, [eps]);

Compute centroid of a 2d or 3d polygon or a VNF.

```openscad
path = [
[-10,10], [-5,15], [15,15], [20,0],
[15,-5], [25,-20], [25,-27], [15,-20],
[0,-30], [-15,-25], [-5,-5]
];
linear_extrude(height=0.01) polygon(path);
cp = centroid(path);
color("red") move(cp) sphere(d=2);
```

#### chain_hull() CHILDREN;

Performs the union of hull operations between consecutive pairs of children.

```openscad
chain_hull() {
cube(5, center=true);
translate([30, 0, 0]) sphere(d=15);
translate([60, 30, 0]) cylinder(d=10, h=20);
translate([60, 60, 0]) cube([10,1,20], center=false);
}
```

#### chamfer_corner_mask(chamfer) [ATTACHMENTS];

Creates a shape to chamfer a 90° corner.

```openscad
chamfer_corner_mask(chamfer=10);
```

#### chamfer_cylinder_mask(r|d=, chamfer, [ang], [from_end]) [ATTACHMENTS];

Creates a shape to chamfer the end of a cylinder.

```openscad
difference() {
cylinder(r=50, h=100, center=true);
up(50) #chamfer_cylinder_mask(r=50, chamfer=10);
}
```

#### chamfer_edge_mask(l|h=|length=|height=, chamfer, [excess]) [ATTACHMENTS];

Creates a shape to chamfer a 90° edge.

```openscad
chamfer_edge_mask(l=50, chamfer=10);
```

#### change_anchors([named],[alias=],[remove=]) PARENT() CHILDREN;

Changes the named anchors inherited from the parent

```openscad
// change_anchors — Changes the named anchors inherited from the parent
// Usage: change_anchors([named],[alias=],[remove=]) PARENT() CHILDREN;
```

#### L = cholesky(A);

Compute the Cholesky factorization of a matrix.

```openscad
// cholesky — Compute the Cholesky factorization of a matrix.
// Usage: L = cholesky(A);
```

#### circle(r|d=, ...) [ATTACHMENTS];

Creates the approximation of a circle.

```openscad
circle(r=25);
```

#### circ = circle_2tangents(r|d=, pt1, pt2, pt3, [tangents=]);

Given two 2d or 3d rays, find a circle tangent to both.

```openscad
pts = [[40,40], [10,10], [55,5]];  rad = 10;
circ = circle_2tangents(r=rad, pt1=pts[0], pt2=pts[1], pt3=pts[2]);
stroke(pts, endcaps="arrow2");
color("red") move(circ[0]) circle(r=rad);
```

#### circ = circle_3points(pt1, pt2, pt3);

Find a circle passing through three 2d or 3d points.

```openscad
pts = [[60,40], [10,10], [65,5]];
circ = circle_3points(pts[0], pts[1], pts[2]);
translate(circ[0]) color("green") stroke(circle(r=circ[1]),closed=true,$fn=72);
translate(circ[0]) color("red") circle(d=3, $fn=12);
move_copies(pts) color("blue") circle(d=3, $fn=12);
```

#### pts = circle_circle_intersection(r1|d1=, cp1, r2|d2=, cp2, [eps]);

Find the intersection points of two 2d circles.

```openscad
$fn=32;
cp1 = [4,4];  r1 = 3;
cp2 = [7,7];  r2 = 2;
pts = circle_circle_intersection(r1, cp1, r2, cp2);
move(cp1) stroke(circle(r=r1), width=0.2, closed=true);
move(cp2) stroke(circle(r=r2), width=0.2, closed=true);
color("red") move_copies(pts) circle(r=.3);
```

#### segs = circle_circle_tangents(r1|d1=, cp1, r2|d2=, cp2);

Find tangents to a pair of circles in 2d.

```openscad
$fn=32;
cp1 = [3,4];  r1 = 2;
cp2 = [7,10]; r2 = 3;
pts = circle_circle_tangents(r1, cp1, r2, cp2);
move(cp1) stroke(circle(r=r1), width=0.2, closed=true);
move(cp2) stroke(circle(r=r2), width=0.2, closed=true);
colors = ["green","black","blue","red"];
for(i=[0:len(pts)-1]) color(colors[i]) stroke(pts[i],width=0.2);
```

#### pts = circle_line_intersection(r|d=, cp, line, [bounded], [eps=]);

Find the intersection points between a 2d circle and a line, ray or segment.

```openscad
line = [[-15,2], [15,7]];
cp = [1,2]; r = 10;
translate(cp) circle(r=r);
color("black") stroke(line, endcaps="arrow2", width=0.5);
isects = circle_line_intersection(r=r, cp=cp, line=line);
color("red") move_copies(isects) circle(d=1);
```

#### tangents = circle_point_tangents(r|d=, cp, pt);

Given a circle and point, find tangents to circle passing through the point.

```openscad
cp = [-10,-10];  r = 30;  pt = [30,10];
tanpts = circle_point_tangents(r=r, cp=cp, pt=pt);
color("yellow") translate(cp) circle(r=r);
color("cyan") for(tp=tanpts) {stroke([tp,pt]); stroke([tp,cp]);}
color("red") move_copies(tanpts) circle(d=3,$fn=12);
color("blue") move_copies([cp,pt]) circle(d=3,$fn=12);
```

#### circ_pitch = circular_pitch(circ_pitch);

Returns tooth density expressed as "circular pitch".

```openscad
$fn=144;
teeth=20;
circ_pitch = circular_pitch(diam_pitch=8);
pr = pitch_radius(circ_pitch, teeth);
stroke(spur_gear2d(circ_pitch, teeth), width=0.1);
color("cyan")
dashed_stroke(circle(r=pr), width=0.1);
color("black") {
stroke(
arc(r=pr, start=90+90/teeth, angle=-360/teeth),
width=0.2, endcaps="arrow");
back(pr+1) right(3)
zrot(30) text("Circular Pitch", size=1);
}
```

#### newpoly = clockwise_polygon(poly);

Return clockwise version of a polygon.

```openscad
// clockwise_polygon — Return clockwise version of a polygon.
// Usage: newpoly = clockwise_polygon(poly);
```

#### index = closest_point(pt, points);

Finds the closest point in a list of points.

```openscad
// closest_point — Finds the closest point in a list of points.
// Usage: index = closest_point(pt, points);
```

#### color_overlaps([color]) CHILDREN;

Shows ghostly children, with overlaps highlighted in color.

```openscad
color_overlaps() {
circle(d=50);
left(20) circle(d=50);
right(20) circle(d=50);
}
```

#### color_this([c]) CHILDREN;

Sets the color for children at the current level only.

```openscad
cuboid([10,10,5])
color_this("green")attach(TOP,BOT) cuboid([9,9,4.5])
attach(TOP,BOT) cuboid([8,8,4])
color_this("purple") attach(TOP,BOT) cuboid([7,7,3.5])
attach(TOP,BOT) cuboid([6,6,3])
color_this("cyan")attach(TOP,BOT) cuboid([5,5,2.5])
attach(TOP,BOT) cuboid([4,4,2]);
```

#### list = column(M, i);

Extract a column from a matrix.

```openscad
M = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]];
a = column(M,2);      // Returns [3, 7, 11, 15]
b = column(M,0);      // Returns [1, 5, 9, 13]
N = [ [1,2], [3], [4,5], [6,7,8] ];
c = column(N,1);      // Returns [1,undef,5,7]
data = [[1,[3,4]], [3, [9,3]], [4, [3,1]]];   // Matrix with non-numeric entries
d = column(data,0);   // Returns [1,3,4]
e = column(data,1);   // Returns [[3,4],[9,3],[3,1]]
```

#### list = combinations(l, [n]);

Returns a list of all combinations of the list entries.

```openscad
pairs = combinations([3,4,5,6]);  // Returns: [[3,4],[3,5],[3,6],[4,5],[4,6],[5,6]]
triplets = combinations([3,4,5,6],n=3);  // Returns: [[3,4,5],[3,4,6],[3,5,6],[4,5,6]]
```

#### test = compare_lists(a, b)

Compares two lists of values, possibly of different type.

```openscad
// compare_lists — Compares two lists of values, possibly of different type.
// Usage: test = compare_lists(a, b)
```

#### test = compare_vals(a, b);

Compares two values, possibly of different type.

```openscad
// compare_vals — Compares two values, possibly of different type.
// Usage: test = compare_vals(a, b);
```

#### z = complex(list)

Replaces scalars in a list or matrix with complex number 2-vectors.

```openscad
// complex — Replaces scalars in a list or matrix with complex number 2-vectors.
// Usage: z = complex(list)
```

#### vals = constrain(v, minval, maxval);

Limit (clamp) a number or array of numbers to a specified range of values.

```openscad
a = constrain(-5, -1, 1);   // Returns: -1
b = constrain(5, -1, 1);    // Returns: 1
c = constrain(0.3, -1, 1);  // Returns: 0.3
d = constrain(9.1, 0, 9);   // Returns: 9
e = constrain([1,2,3,4,5,6,7,8,9], 3, 7);          // Returns: [3,3,3,4,5,6,7,7,7]
f = constrain([[1,2,3], [4,5,6], [7,8,9]], 3, 7);  // Returns: [[3,3,3], [4,5,6], [7,7,7]]
g = constrain([[1,2,3,4], [5,6,7], [8,9]], 3, 7);  // Returns: [[3,3,3,4], [5,6,7], [7,7]]
```

#### contour(f, isovalue, bounding_box, pixel_size, [pixel_count=], [use_centers=], [smoothing=], [exact_bounds=], [show_stats=], [show_box=], ...) [ATTACHMENTS];

Creates a 2D contour from a function or array of values.

```openscad
field =[
[0,2,2,1,0,0,0,0],
[2,4,1,0,0,0,0,0],
[2,2,2,1,0,0,0,0],
[0,0,1,2,2,2,1,1],
[0,0,2,1,0,3,1,0],
[0,2,0,2,0,3,4,0],
[0,0,0,1,2,3,2,0],
[0,0,0,0,0,1,0,0]
];
isoval=[0.7,INF];
pixsize = 5;
color("lightgreen") zrot(-90)
contour(field, isoval, pixel_size=pixsize,
use_centers=false);
color("blue") down(1)
square((len(field)-1)*pixsize, true);
```

#### conv_hull([keep]) CHILDREN;

Performs a hull operation on the children using tags to determine what happens.

```openscad
conv_hull("keep")
sphere(d=100, $fn=64) {
cuboid([40,90,90]);
tag("keep")xcyl(d=40, l=120);
}
```

#### bool = convex_collision(points1, points2, [eps]);

Check whether the convex hulls of two point lists intersect.

```openscad
pts1 = move([-3,0], p=square(3,center=true));
pts2 = rot(a=45, p=square(2,center=true));
pts3 = [ [2,0], [1,2],[3,2], [3,-2], [1,-2] ];
polygon(pts1);
polygon(pts2);
polygon(pts3);
echo(convex_collision(pts1,pts2)); // Returns: false
echo(convex_collision(pts2,pts3)); // Returns: true
```

#### dist = convex_distance(points1, points2,eps);

Compute distance between convex hull of two point lists.

```openscad
pts1 = move([-3,0], p=square(3,center=true));
pts2 = rot(a=45, p=square(2,center=true));
pts3 = [ [2,0], [1,2],[3,2], [3,-2], [1,-2] ];
polygon(pts1);
polygon(pts2);
polygon(pts3);
echo(convex_distance(pts1,pts2)); // Returns: 0.0857864
echo(convex_distance(pts2,pts3)); // Returns: 0
```

#### convex_offset_extrude(height, [bottom], [top], ...) 2D-CHILDREN;

Make a solid from geometry where offset changes along the object's length.

```openscad
convex_offset_extrude(bottom = os_chamfer(height=-2),
top=os_chamfer(height=1), height=7)
xscale(4)circle(r=6,$fn=64);
```

#### x = convolve(p,q);

Returns the convolution of `p` and `q`.

```openscad
a = convolve([1,1],[1,2,1]); // Returns: [1,3,3,1]
b = convolve([1,2,3],[1,2,1])); // Returns: [1,4,8,8,3]
c = convolve([[1,1],[2,2],[3,1]],[1,2,1])); // Returns: [[1,1],[4,4],[8,6],[8,4],[3,1]]
d = convolve([[1,1],[2,2],[3,1]],[[1,2],[2,1]])); // Returns:  [3,9,11,7]
```

#### PARENT() corner_mask([corners], [except]) CHILDREN;

Attaches a 3d mask shape to the given corners of the parent.

```openscad
diff()
cube(100, center=true)
corner_mask([TOP,FRONT],LEFT+FRONT+TOP)
difference() {
translate(-0.01*[1,1,1]) cube(20);
translate([20,20,20]) sphere(r=20);
}
```

#### PARENT() corner_profile([corners], [except], [r=|d=], [convexity=]) CHILDREN;

Rotationally extrudes a 2d edge profile into corner mask on the given corners of the parent.

```openscad
diff()
cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) {
corner_profile(TOP,r=10)
mask2d_teardrop(r=10, angle=40);
}
```

#### corrugated_wall(h, l, thick, [strut=], [wall=]) [ATTACHMENTS];

Makes a corrugated rectangular wall.

```openscad
corrugated_wall(h=50, l=100);
```

#### a = cosh(x);

Returns the hyperbolic cosine of the given value.

```openscad
// cosh — Returns the hyperbolic cosine of the given value.
// Usage: a = cosh(x);
```

#### list = count(n, [s], [step], [reverse]);

Creates a list of incrementing numbers.

```openscad
nl1 = count(5);  // Returns: [0,1,2,3,4]
nl2 = count(5,3);  // Returns: [3,4,5,6,7]
nl3 = count(4,3,2);  // Returns: [3,5,7,9]
nl4 = count(5,reverse=true);    // Returns: [4,3,2,1,0]
nl5 = count(5,3,reverse=true);  // Returns: [7,6,5,4,3]
```

#### crown_gear(circ_pitch, teeth, backing, face_width, [pressure_angle=], [clearance=], [backlash=], [profile_shift=], [slices=]);

Creates a crown gear that can mesh with a spur gear.

```openscad
crown_gear(mod=1, teeth=40, backing=3, face_width=5, pressure_angle=20);
```

#### cube(size, [center]);

Creates a cube with anchors for attaching children.

```openscad
cube(40);
```

#### cubetruss(extents, [clips=], [bracing=], [size=], [strut=], [clipthick=], ...) [ATTACHMENTS];

Creates a multi-cube straight cubetruss shape.

```openscad
cubetruss(extents=3);
cubetruss(extents=3, clips=FRONT);
cubetruss(extents=3, clips=[FRONT,BACK]);
cubetruss(extents=[2,3]);
cubetruss(extents=[1,4,2]);
cubetruss(extents=[1,4,2], bracing=false);
```

#### cubetruss_clip(extents, [size=], [strut=], [clipthick=]) [ATTACHMENTS];

Creates a clip for the end of a cubetruss to snap-lock it to another cubetruss.

```openscad
cubetruss_clip(extents=2);
cubetruss_clip(extents=1);
cubetruss_clip(clipthick=2.5);
```

#### cubetruss_corner(h, extents, [bracing=], [size=], [strut=], [clipthick=]);

Creates a multi-cube corner cubetruss shape.

```openscad
cubetruss_corner(extents=2);
cubetruss_corner(extents=2, h=2);
cubetruss_corner(extents=[3,3,0,0,2]);
cubetruss_corner(extents=[3,0,3,0,2]);
cubetruss_corner(extents=[3,3,3,3,2]);
```

#### length = cubetruss_dist(cubes, [gaps], [size=], [strut=]);

Returns the length of a cubetruss truss.

```openscad
// cubetruss_dist — Returns the length of a cubetruss truss.
// Usage: length = cubetruss_dist(cubes, [gaps], [size=], [strut=]);
```

#### cubetruss_foot(w, [size=], [strut=], [clipthick=]) [ATTACHMENTS];

Creates a foot that can connect two cubetrusses.

```openscad
cubetruss_foot(w=1);
cubetruss_foot(w=3);
```

#### cubetruss_joiner([w=], [vert=], [size=], [strut=], [clipthick=]) [ATTACHMENTS];

Creates a joiner that can connect two cubetrusses end-to-end.

```openscad
cubetruss_joiner(w=1, vert=false);
cubetruss_joiner(w=1, vert=true);
cubetruss_joiner(w=2, vert=true, anchor=BOT);
```

#### cubetruss_segment([size=], [strut=], [bracing=]);

Creates a single cubetruss cube.

```openscad
cubetruss_segment(bracing=false);
cubetruss_segment(bracing=true);
cubetruss_segment(strut=4);
cubetruss_segment(size=40);
```

#### cubetruss_support([size=], [strut=], [extents=]) [ATTACHMENTS];

Creates a cubetruss support structure shape.

```openscad
cubetruss_support();
```

#### cubetruss_uclip(dual, [size=], [strut=], [clipthick=]) [ATTACHMENTS];

Creates a joiner that can connect two cubetrusses end-to-end.

```openscad
cubetruss_uclip(dual=false);
cubetruss_uclip(dual=true);
```

#### cuboid(size, [anchor=], [spin=], [orient=]);

Creates a cube with chamfering and roundovers.

```openscad
cuboid(40);
```

#### prod_list = cumprod(list, [right]);

Returns the running cumulative product of a list of values.

```openscad
cumprod([1,3,5]);  // returns [1,3,15]
cumprod([2,2,2]);  // returns [2,4,8]
cumprod([[1,2,3], [3,4,5], [5,6,7]]));  // returns [[1, 2, 3], [3, 8, 15], [15, 48, 105]]
```

#### sums = cumsum(v);

Returns the running cumulative sum of a list of values.

```openscad
cumsum([1,1,1]);  // returns [1,2,3]
cumsum([2,2,2]);  // returns [2,4,6]
cumsum([1,2,3]);  // returns [1,3,6]
cumsum([[1,2,3], [3,4,5], [5,6,7]]);  // returns [[1,2,3], [4,6,8], [9,12,15]]
```

#### cyl(l|h|length|height, r, [r2], [center|anchor=], [circum=], [realign=]) [ATTACHMENTS];

Creates an attachable cylinder with roundovers, chamfering or texturing.

```openscad
xdistribute(30) {
cyl(l=40, r=10);
cyl(l=40, r1=10, r2=5);
}
```

#### cylinder(h, r=/d=, [center=]);

Creates an attachable cylinder.

```openscad
xdistribute(30) {
cylinder(h=40, r=10);
cylinder(h=40, r1=10, r2=5);
}
```

#### cylindrical_extrude(ir|id=, or|od=, [size=], [convexity=], [spin=], [orient=]) 2D-CHILDREN;

Extrudes 2D children outwards around a cylinder.

```openscad
cylindrical_extrude(or=50, ir=45)
text(text="Hello World!", size=10, halign="center", valign="center");
```

#### pt = cylindrical_to_xyz(r, theta, z);

Convert cylindrical coordinates to cartesian coordinates.

```openscad
xyz = cylindrical_to_xyz(20,30,40);
xyz = cylindrical_to_xyz([40,60,50]);
```

#### dashed_stroke(path, dashpat, [width=], [closed=]);

Draws a dashed line along a path or region boundary.

```openscad
path = [for (a=[-180:10:180]) [a/3,20*sin(a)]];
dashed_stroke(path, [3,2], width=1);
```

#### debug_bezier(bez, [size], [N=]);

Shows a bezier path and its associated control points.

```openscad
bez = [
[-10,   0],  [-15,  -5],
[ -5, -10],  [  0, -10],  [ 5, -10],
[ 14,  -5],  [ 15,   0],  [16,   5],
[  5,  10],  [  0,  10]
];
debug_bezier(bez, N=3, width=0.5);
```

#### debug_bezier_patches(patches, [size=], [splinesteps=], [showcps=], [showdots=], [showpatch=], [convexity=], [style=]);

Shows a bezier surface patch and its associated control points.

```openscad
patch1 = [
[[15,15,0], [33,  0,  0], [ 67,  0,  0], [ 85, 15,0]],
[[ 0,33,0], [33, 33, 50], [ 67, 33, 50], [100, 33,0]],
[[ 0,67,0], [33, 67, 50], [ 67, 67, 50], [100, 67,0]],
[[15,85,0], [33,100,  0], [ 67,100,  0], [ 85, 85,0]],
];
patch2 = [
[[15,85,0], [33,100,  0], [ 67,100,  0], [ 85, 85,0]],
[[ 0,67,0], [33, 67,-50], [ 67, 67,-50], [100, 67,0]],
[[ 0,33,0], [33, 33,-50], [ 67, 33,-50], [100, 33,0]],
[[15,15,0], [33,  0,  0], [ 67,  0,  0], [ 85, 15,0]],
];
debug_bezier_patches(patches=[patch1, patch2], splinesteps=8, showcps=true);
```

#### debug_nurbs(control, degree, [width], [splinesteps=], [type=], [mult=], [knots=], [size=], [show_weights=], [show_knots=], [show_idx=]);

Shows a NURBS curve and its control points, knots and weights

```openscad
pts = [[5,0],[0,20],[33,43],[37,88],[60,62],[44,22],[77,44],[79,22],[44,3],[22,7]];
debug_nurbs(pts,4,type="clamped",show_knots=true);
```

#### debug_nurbs_interp(points, degree, [splinesteps=], [method=], [closed=], [deriv=], [start_deriv=], [end_deriv=], [curvature=], [start_curvature=], [end_curvature=], [corners=], [extra_pts=], [smooth=], [width=], [size=], [data_size=], [data_index=], [show_control=], [control_index=], [show_knots=], [show_deriv=], [show_curvature=]);

Interpolates a NURBS using {{nurbs_interp()}} and displays the curve with informative overlays.

```openscad
data = [[0,0], [20,30], [35,120], [50,30], [70,0]];
method = ["length", "centripetal", "dynamic", "foley", "fang"];
color = ["blue","lime","yellow","orange","red"]; 
for (i = [0:4]) {
color(color[i]) {
debug_nurbs_interp(data, 3, closed = true, method = method[i], size = 5, data_size = 3);
move([80,100-i*15]) text(method[i]);
}
}
```

#### debug_polygon(points, paths, [vertices=], [edges=], [convexity=], [size=]);

Draws an annotated polygon.

```openscad
debug_polygon(
points=concat(
regular_ngon(or=10, n=8),
regular_ngon(or=8, n=8)
),
paths=[
[for (i=[0:7]) i],
[for (i=[15:-1:8]) i]
]
);
```

#### debug_region(region, [vertices=], [edges=], [convexity=], [size=]);

Draws an annotated {{region}}.

```openscad
region = make_region([square(15), move([5,5],square(15))]);
debug_region(region,size=1);
```

#### debug_vnf(vnfs, [faces=], [vertices=], [opacity=], [size=], [convexity=], [filter=]);

A replacement for `vnf_polyhedron()` to help with debugging.

```openscad
verts = [for (z=[-10,10], a=[0:120:359.9]) [10*cos(a),10*sin(a),z]];
faces = [[0,1,2], [5,4,3], [0,3,4], [0,4,1], [1,4,5], [1,5,2], [2,5,3], [2,3,0]];
debug_vnf([verts,faces], size=2);
```

#### list = deduplicate(list, [closed], [eps]);

Returns a list with all consecutive duplicate values removed.

```openscad
a = deduplicate([8,3,4,4,4,8,2,3,3,8,8]);  // Returns: [8,3,4,8,2,3,8]
b = deduplicate(closed=true, [8,3,4,4,4,8,2,3,3,8,8]);  // Returns: [8,3,4,8,2,3]
c = deduplicate("Hello");  // Returns: "Helo"
d = deduplicate([[3,4],[7,2],[7,1.99],[1,4]],eps=0.1);  // Returns: [[3,4],[7,2],[1,4]]
e = deduplicate([[7,undef],[7,undef],[1,4],[1,4+1e-12]],eps=0);    // Returns: [[7,undef],[1,4],[1,4+1e-12]]
```

#### new_idxs = deduplicate_indexed(list, indices, [closed], [eps]);

Takes a list of indices into a list of values, and returns a list of indices whose values are not consecutively the same.

```openscad
a = deduplicate_indexed([8,6,4,6,3], [1,4,3,1,2,2,0,1]);  // Returns: [1,4,3,2,0,1]
b = deduplicate_indexed([8,6,4,6,3], [1,4,3,1,2,2,0,1], closed=true);  // Returns: [1,4,3,2,0]
c = deduplicate_indexed([[7,undef],[7,undef],[1,4],[1,4],[1,4+1e-12]],eps=0);    // Returns: [0,2,4]
```

#### val = default(val, dflt);

Returns a default value if the argument is 'undef', else returns the argument.

```openscad
// default — Returns a default value if the argument is 'undef', else returns the argument.
// Usage: val = default(val, dflt);
```

#### PARENT() default_tag(tag, [do_tag]) CHILDREN;

Conditionally set a default tag for all children.

```openscad
module thing() { tag("remove") cuboid(10);}
diff()
cuboid(20){
position(TOP) thing();
position(RIGHT) tag("keep_it") thing();
}
```

#### part = define_part(name, geom, [inside=], [T=]);

Creates an attachable part data structure.

```openscad
module twocyl(d, sep, h, ang=20) 
{
parts = [
define_part("left", attach_geom(r=d/2,h=h), T=left(sep/2)*yrot(-ang)),
define_part("right", attach_geom(r=d/2,h=h), T=right(sep/2)*yrot(ang)),
];
attachable(size=[sep+d,d,h], parts=parts){
union(){
left(sep/2) yrot(-ang) cyl(d=d,h=h);
right(sep/2) yrot(ang) cyl(d=d,h=h);
}
children();
}  
}
twocyl(d=10,sep=30,h=10){
attach(TOP,TOP) cuboid(3);
color("pink")attach_part("left")attach(TOP,BOT) cuboid(3);
color("green")attach_part("right")attach(TOP,BOT) cuboid(3);    
}
```

#### delts = deltas(v,[wrap]);

Returns the deltas between a list of values.

```openscad
deltas([2,5,9,17]);  // returns [3,4,8].
deltas([[1,2,3], [3,6,8], [4,8,11]]);  // returns [[2,4,5], [1,2,3]]
```

#### deprecate(new_name);

Display a console note that a module is deprecated and suggest a replacement.

```openscad
// deprecate — Display a console note that a module is deprecated and suggest a replacement.
// Usage: deprecate(new_name);
```

#### x = deriv(data, [h], [closed])

Returns the first derivative estimate of a list of data.

```openscad
// deriv — Returns the first derivative estimate of a list of data.
// Usage: x = deriv(data, [h], [closed])
```

#### x = deriv2(data, [h], [closed])

Returns the second derivative estimate of a list of data.

```openscad
// deriv2 — Returns the second derivative estimate of a list of data.
// Usage: x = deriv2(data, [h], [closed])
```

#### x = deriv3(data, [h], [closed])

Returns the third derivative estimate of a list of data.

```openscad
// deriv3 — Returns the third derivative estimate of a list of data.
// Usage: x = deriv3(data, [h], [closed])
```

#### desc_copies(transforms) CHILDREN;

Places copies according to a list of transformation matrices and supplies descriptions for the copies.

```openscad
// desc_copies — Places copies according to a list of transformation matrices and supplies descriptions for the copies.
// Usage: desc_copies(transforms) CHILDREN;
```

#### dir = desc_anchor(desc,[dir], [anchor]);

Computes the direction in the current context of a direction or anchor in a description's context

```openscad
prismoid(20,10,h=15)
attach(RIGHT,BOT) cuboid([4,4,15])
position(TOP) cyl(d=12,h=5,orient=desc_dir(),anchor=BACK);
```

#### dist = desc_dist(desc1,anchor1,desc2,anchor2);

Computes the distance between two points specified by attachable descriptions

```openscad
cuboid(10) let(desc=parent()) {
color("red")attach(TOP+LEFT+FWD) sphere(r=0.75,$fn=12);
right(15) cuboid(10) {
color("red") attach(TOP+RIGHT+BACK) sphere(r=0.75,$fn=12);
echo(desc_dist(parent(),TOP+RIGHT+BACK, desc, TOP+LEFT+FWD));  // Prints 26.9258
}
}
```

#### point = desc_point(desc,[p],[anchor]);

Computes the location in the current context of an anchor point from an attachable description

```openscad
cuboid(10) let(desc=parent())
right(12) up(27)
union(){
pt1 = desc_point(desc,anchor=TOP+BACK+LEFT);
pt2 = desc_point(desc,anchor=TOP+FWD+RIGHT);
stroke([pt1,pt2,CENTER], closed=true, width=.5,color="red");
}
```

#### d = det2(M);

Compute determinant of 2x2 matrix.

```openscad
M = [ [6,-2], [1,8] ];
det = det2(M);  // Returns: 50
```

#### d = det3(M);

Compute determinant of 3x3 matrix.

```openscad
M = [ [6,4,-2], [1,-2,8], [1,5,7] ];
det = det3(M);  // Returns: -334
```

#### d = det4(M);

Compute determinant of 4x4 matrix.

```openscad
M = [ [6,4,-2,1], [1,-2,8,-3], [1,5,7,4], [2,3,4,7] ];
det = det4(M);  // Returns: -1773
```

#### d = determinant(M);

compute determinant of an arbitrary square matrix.

```openscad
M = [ [6,4,-2,9], [1,-2,8,3], [1,5,7,6], [4,2,5,1] ];
det = determinant(M);  // Returns: 2267
```

#### mat = diagonal_matrix(diag, [offdiag]);

Make a diagonal matrix.

```openscad
// diagonal_matrix — Make a diagonal matrix.
// Usage: mat = diagonal_matrix(diag, [offdiag]);
```

#### dp = diametral_pitch(circ_pitch);

Returns tooth density expressed as "diametral pitch".

```openscad
diam_pitch1 = diametral_pitch(mod=2);
diam_pitch2 = diametral_pitch(circ_pitch=8);
diam_pitch3 = diametral_pitch(diam_pitch=16);
```

#### diff([remove], [keep]) PARENT() CHILDREN;

Performs a differencing operation using tags rather than hierarchy to control what happens.

```openscad
diff()
cuboid(50) {
tag("remove") attach(TOP) sphere(d=40);
tag("keep") attach(CTR) cylinder(h=40, d=10);
}
```

#### difference() CHILDREN;

Performs a Boolean difference operation.

```openscad
shape1 = move([-8,-8,0], p=circle(d=50));
shape2 = move([ 8, 8,0], p=circle(d=50));
for (shape = [shape1,shape2])
stroke(shape, width=0.5, color="red", closed=true);
color("green") region(difference(shape1,shape2));
```

#### distribute(spacing, sizes, dir) CHILDREN;

Distributes each child, individually, out along an arbitrary line.

```openscad
distribute(sizes=[100, 30, 50], dir=UP) {
sphere(r=50);
cube([10,20,30], center=true);
cylinder(d=30, h=50, center=true);
}
```

#### dovetail(gender, w=|width, h=|height, slide|thickness=, [slope=|angle=], [taper=|back_width=], [chamfer=], [r=|radius=], [round=], [extra=], [entry_slot_length=], [$slop=])

Creates a possibly tapered dovetail shape.

```openscad
dovetail("male", width=15, height=8, slide=30);
right(20) dovetail("female", width=15, height=8, slide=30);
```

#### down(z) CHILDREN;

Translates children downward (Z-).

```openscad
#sphere(d=10);
down(20) sphere(d=10);
```

#### newstr = downcase(str);

Lowercases all characters in a string.

```openscad
s=downcase("ABCdef");   // Returns "abcdef"
```

#### echo_matrix(M, [description], [sig], [sep], [eps]);

Print a matrix neatly to the console.

```openscad
// echo_matrix — Print a matrix neatly to the console.
// Usage: echo_matrix(M, [description], [sig], [sep], [eps]);
```

#### echo_struct(struct, [name]);

Echoes the struct to the console in a formatted manner.

```openscad
// echo_struct — Echoes the struct to the console in a formatted manner.
// Usage: echo_struct(struct, [name]);
```

#### echo_viewport();

Display the current viewport parameters.

```openscad
// echo_viewport — Display the current viewport parameters.
// Usage: echo_viewport();
```

#### EDGE(i)

Named edge anchor constants

```openscad
// EDGE — Named edge anchor constants
// Usage: EDGE(i)
```

#### PARENT() edge_mask([edges], [except]) CHILDREN;

Attaches a 3D mask shape to the given edges of the parent.

```openscad
diff()
cube([50,60,70],center=true)
edge_mask([TOP,"Z"],except=[BACK,TOP+LEFT])
rounding_edge_mask(l=71,r=10);
```

#### PARENT() edge_profile([edges], [except], [convexity]) CHILDREN;

Extrudes a 2d edge profile into a mask on edges of the parent cuboid, prismoid or cone.

```openscad
diff()
cube([50,60,70],center=true)
edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
mask2d_roundover(r=10, inset=2);
```

#### PARENT() edge_profile([edges], [except], [convexity=], [flip=], [corner_type=]) CHILDREN;

Extrudes an asymmetric 2D profile into a mask on edges and corners of the parent cuboid.

```openscad
ogee = [
"xstep",1,  "ystep",1,  // Starting shoulder.
"fillet",5, "round",5,  // S-curve.
"ystep",1,  "xstep",1   // Ending shoulder.
];
diff()
cuboid(50) {
edge_profile_asym(FRONT)
mask2d_ogee(ogee);
}
```

#### egg(length, r1|d1=, r2|d2=, R|D=) [ATTACHMENTS];

Creates an egg-shaped 2d object.

```openscad
$fn=100;
color("red") stroke(egg(78,25,12, 60),closed=true);
stroke([left(14,circle(25)),
right(27,circle(12))]);
```

#### ellipse(r|d=, [realign=], [circum=], [uniform=], ...) [ATTACHMENTS];

Creates the approximation of an ellipse or a circle.

```openscad
ellipse(r=25);
```

#### bool = ends_with(str,pattern);

Returns true if the string ends with a given substring.

```openscad
b1=ends_with("abcdef","def");  // Returns true
b2=ends_with("abcdef","de");   // Returns false
b3=ends_with("abcdef","");     // Returns true
```

#### enveloping_worm(circ_pitch, mate_teeth, d, [left_handed=], [starts=], [arc=], [pressure_angle=]);

Creates a double-enveloping worm that will mate with a worm gear.

```openscad
enveloping_worm(circ_pitch=8, mate_teeth=45, d=30, $fn=72);
```

#### exclusive_or() CHILDREN;

Performs a Boolean exclusive-or operation.

```openscad
shape1 = move([-8,-8,0], p=circle(d=50));
shape2 = move([ 8, 8,0], p=circle(d=50));
for (shape = [shape1,shape2])
color("red") stroke(shape, width=0.5, closed=true);
color("green") region(exclusive_or(shape1,shape2));
```

#### arr = exponential_rands([n], [lambda], [seed])

Returns a list of random numbers with an exponential distribution.

```openscad
// exponential_rands — Returns a list of random numbers with an exponential distribution.
// Usage: arr = exponential_rands([n], [lambda], [seed])
```

#### expose_anchors(opacity) {child1() show_anchors(); child2() show_anchors(); ...}

Used to show a transparent object with solid color anchor arrows.

```openscad
expose_anchors() cube(50, center=true) show_anchors();
```

#### extrude_from_to(pt1, pt2, [convexity=], [twist=], [scale=], [slices=]) 2D-CHILDREN;

Extrudes 2D children between two points in 3D space.

```openscad
extrude_from_to([0,0,0], [10,20,30], convexity=4, twist=360, scale=3.0, slices=40) {
xcopies(3) circle(3, $fn=32);
}
```

#### fn = f_1arg(func);

Creates a factory for a 2-arg function literal, where you can optionally pre-fill the arg.

```openscad
f_str = f_1arg(function(a) str(a));
fn_str = f_str();   // = function(a) str(a);
fn_str3 = f_str(3); // = function() str(3);
```

#### fn = f_2arg(target_func);

Creates a factory for a 2-arg function literal, where you can optionally pre-fill the args.

```openscad
f_lt = f_2arg(function(a,b) a<b);
fn_lt = f_lt();      // = function(a,b) a<b;
fn_3lt = f_lt(3);    // = function(b) 3<b;
fn_3lt = f_lt(a=3);  // = function(b) 3<b;
fn_lt3 = f_lt(b=3);  // = function(a) a<3;
fn_3lt4 = f_lt(3,4); // = function() 3<4;
```

#### fn = f_2arg_simple(target_func);

Creates a factory for a 2-arg function literal, where you can optionally pre-fill the args.

```openscad
f_lt = f_2arg_simple(function(a,b) a<b);
fn_lt = f_lt();       // = function(a,b) a<b;
fn_lt3 = f_lt(3);     // = function(a) a<3;
fn_3lt4 = f_lt(3,4);  // = function() 3<4;
```

#### fn = f_3arg(target_func);

Creates a factory for a 3-arg function literal, where you can optionally pre-fill the args.

```openscad
p1 = [10,4]; p2 = [3,7];
f_va = f_3arg(function(a,b,c) vector_angle(a,b,c));
fn_va = f_va();       // = function(a,b,c) vector_angle(a,b,c);
fn_va2 = f_lt(c=p1);  // = function(a,b) vector_angle(a,b,p1);
fn_va3 = f_lt(a=p2);  // = function(a,c) vector_angle(a,p2,c);
fn_va4 = f_lt(a=p1,c=p2); // = function() vector_angle(p1,b,p2);
```

#### fn = f_abs();

Returns a function to calculate the absolute value of a given number.

```openscad
// f_abs — Returns a function to calculate the absolute value of a given number.
// Usage: fn = f_abs();
```

#### fn = f_acos();

Returns a function to calculate the arccosine of a value.

```openscad
// f_acos — Returns a function to calculate the arccosine of a value.
// Usage: fn = f_acos();
```

#### fn = f_add();

Returns a function to add `a` and `b`.

```openscad
// f_add — Returns a function to add `a` and `b`.
// Usage: fn = f_add();
```

#### fn = f_and();

Returns a function to check if both `a` and `b` are true.

```openscad
// f_and — Returns a function to check if both `a` and `b` are true.
// Usage: fn = f_and();
```

#### fn = f_approx();

Returns a function to compare if `a` is approximately equal to `b`.

```openscad
fn_approx = f_approx();       // = function(a,b) approx(a,b);
fn_approx3 = f_approx(3);     // = function(a) approx(a,3);
fn_3approx4 = f_approx(3,4);  // = function() approx(3,4);
```

#### fn = f_asin();

Returns a function to calculate the arcsine of a value.

```openscad
// f_asin — Returns a function to calculate the arcsine of a value.
// Usage: fn = f_asin();
```

#### fn = f_atan();

Returns a function to calculate the arctangent of a value.

```openscad
// f_atan — Returns a function to calculate the arctangent of a value.
// Usage: fn = f_atan();
```

#### fn = f_atan2();

Returns a function to calculate the arctangent of `y` and `x`

```openscad
// f_atan2 — Returns a function to calculate the arctangent of `y` and `x`
// Usage: fn = f_atan2();
```

#### fn = f_ceil();

Returns a function to calculate the integer ceiling of a given number.

```openscad
// f_ceil — Returns a function to calculate the integer ceiling of a given number.
// Usage: fn = f_ceil();
```

#### fn = f_chr();

Returns a function to get a string character from its ordinal number.

```openscad
// f_chr — Returns a function to get a string character from its ordinal number.
// Usage: fn = f_chr();
```

#### fn = f_cmp();

Returns a function to compare values.

```openscad
fn_cmp = f_cmp();       // = function(a,b) a==b?0: a>b?1: -1;
fn_cmp3 = f_cmp(3);     // = function(a) a==3?0: a>3?1: -1;
fn_3cmp4 = f_cmp(3,4);  // = function() 3==4?0: 3>4?1: -1;
```

#### fn = f_cos();

Returns a function to calculate the cosine of a value.

```openscad
// f_cos — Returns a function to calculate the cosine of a value.
// Usage: fn = f_cos();
```

#### fn = f_cross();

Returns a function to calculate the norm of a given vector.

```openscad
// f_cross — Returns a function to calculate the norm of a given vector.
// Usage: fn = f_cross();
```

#### fn = f_div();

Returns a function to divide `a` by `b`.

```openscad
// f_div — Returns a function to divide `a` by `b`.
// Usage: fn = f_div();
```

#### fn = f_eq();

Returns a function to compare if `a` is exactly equal to `b`.

```openscad
fn_eq = f_eq();       // = function(a,b) a==b;
fn_eq3 = f_eq(3);     // = function(a) a==3;
fn_3eq4 = f_eq(3,4);  // = function() 3==4;
```

#### fn = f_even();

Returns a function to check if `a` is an even number.

```openscad
l2 = filter(f_even(), [3,4,5,6,7,8]);  // Returns: [4,6,8]
```

#### fn = f_exp();

Returns a function to calculate the natural exponent of a given number.

```openscad
// f_exp — Returns a function to calculate the natural exponent of a given number.
// Usage: fn = f_exp();
```

#### fn = f_floor();

Returns a function to calculate the integer floor of a given number.

```openscad
// f_floor — Returns a function to calculate the integer floor of a given number.
// Usage: fn = f_floor();
```

#### fn = f_gt();

Returns a function to compare if `a` is greater than `b`.

```openscad
fn_gt = f_gt();       // = function(a,b) a>b;
fn_gt3 = f_gt(3);     // = function(a) a>3;
fn_3gt4 = f_gt(3,4);  // = function() 3>4;
```

#### fn = f_gte();

Returns a function to compare if `a` is greater than or equal to `b`.

```openscad
fn_gte = f_gte();       // = function(a,b) a>=b;
fn_gte3 = f_gte(3);     // = function(a) a>=3;
fn_3gte4 = f_gte(3,4);  // = function() 3>=4;
```

#### fn = f_is_bezier_patch();

Returns a function to determine if a value is a Bezier Patch structure.

```openscad
// f_is_bezier_patch — Returns a function to determine if a value is a Bezier Patch structure.
// Usage: fn = f_is_bezier_patch();
```

#### fn = f_is_bool();

Returns a function to determine if a value is a boolean.

```openscad
// f_is_bool — Returns a function to determine if a value is a boolean.
// Usage: fn = f_is_bool();
```

#### fn = f_is_def();

Returns a function to determine if a value is not `undef`.

```openscad
// f_is_def — Returns a function to determine if a value is not `undef`.
// Usage: fn = f_is_def();
```

#### fn = f_is_finite();

Returns a function to determine if a value is a number type that is finite.

```openscad
// f_is_finite — Returns a function to determine if a value is a number type that is finite.
// Usage: fn = f_is_finite();
```

#### fn = f_is_function();

Returns a function to determine if a value is a function literal.

```openscad
// f_is_function — Returns a function to determine if a value is a function literal.
// Usage: fn = f_is_function();
```

#### fn = f_is_int();

Returns a function to determine if a value is an integer number.

```openscad
// f_is_int — Returns a function to determine if a value is an integer number.
// Usage: fn = f_is_int();
```

#### fn = f_is_list();

Returns a function to determine if a value is a list.

```openscad
// f_is_list — Returns a function to determine if a value is a list.
// Usage: fn = f_is_list();
```

#### fn = f_is_nan();

Returns a function to determine if a value is a number type that is Not a Number (NaN).

```openscad
// f_is_nan — Returns a function to determine if a value is a number type that is Not a Number (NaN).
// Usage: fn = f_is_nan();
```

#### fn = f_is_num();

Returns a function to determine if a value is a number.

```openscad
// f_is_num — Returns a function to determine if a value is a number.
// Usage: fn = f_is_num();
```

#### fn = f_is_path();

Returns a function to determine if a value is a Path (a list of points).

```openscad
// f_is_path — Returns a function to determine if a value is a Path (a list of points).
// Usage: fn = f_is_path();
```

#### fn = f_is_range();

Returns a function to determine if a value is a range.

```openscad
// f_is_range — Returns a function to determine if a value is a range.
// Usage: fn = f_is_range();
```

#### fn = f_is_region();

Returns a function to determine if a value is a Region (a list of Paths).

```openscad
// f_is_region — Returns a function to determine if a value is a Region (a list of Paths).
// Usage: fn = f_is_region();
```

#### fn = f_is_string();

Returns a function to determine if a value is a string.

```openscad
// f_is_string — Returns a function to determine if a value is a string.
// Usage: fn = f_is_string();
```

#### fn = f_is_undef();

Returns a function to determine if a value is `undef`.

```openscad
// f_is_undef — Returns a function to determine if a value is `undef`.
// Usage: fn = f_is_undef();
```

#### fn = f_is_vector();

Returns a function to determine if a value is a list of numbers.

```openscad
// f_is_vector — Returns a function to determine if a value is a list of numbers.
// Usage: fn = f_is_vector();
```

#### fn = f_is_vnf();

Returns a function to determine if a value is a VNF structure.

```openscad
// f_is_vnf — Returns a function to determine if a value is a VNF structure.
// Usage: fn = f_is_vnf();
```

#### fn = f_len();

Returns a function to calculate the length of a string or list.

```openscad
// f_len — Returns a function to calculate the length of a string or list.
// Usage: fn = f_len();
```

#### fn = f_ln();

Returns a function to calculate the natural logarithm of a given number.

```openscad
// f_ln — Returns a function to calculate the natural logarithm of a given number.
// Usage: fn = f_ln();
```

#### fn = f_log();

Returns a function to calculate the base 10 logarithm of a given number.

```openscad
// f_log — Returns a function to calculate the base 10 logarithm of a given number.
// Usage: fn = f_log();
```

#### fn = f_lt();

Returns a function to compare if `a` is less than `b`.

```openscad
fn_lt = f_lt();       // = function(a,b) a<b;
fn_lt3 = f_lt(3);     // = function(a) a<3;
fn_3lt4 = f_lt(3,4);  // = function() 3<4;
```

#### fn = f_lte();

Returns a function to compare if `a` is less than or equal to `b`.

```openscad
fn_lte = f_lte();       // = function(a,b) a<=b;
fn_lte3 = f_lte(3);     // = function(a) a<=3;
fn_3lte4 = f_lte(3,4);  // = function() 3<=4;
```

#### fn = f_max();

Returns a function to calculate the maximum value of a list.

```openscad
// f_max — Returns a function to calculate the maximum value of a list.
// Usage: fn = f_max();
```

#### fn = f_max2();

Returns a function to calculate the maximum of two values.

```openscad
// f_max2 — Returns a function to calculate the maximum of two values.
// Usage: fn = f_max2();
```

#### fn = f_max3();

Returns a function to calculate the maximum of three values.

```openscad
// f_max3 — Returns a function to calculate the maximum of three values.
// Usage: fn = f_max3();
```

#### fn = f_min();

Returns a function to calculate the minimum value of a list.

```openscad
// f_min — Returns a function to calculate the minimum value of a list.
// Usage: fn = f_min();
```

#### fn = f_min2();

Returns a function to calculate the minimum of two values.

```openscad
// f_min2 — Returns a function to calculate the minimum of two values.
// Usage: fn = f_min2();
```

#### fn = f_min3();

Returns a function to calculate the minimum of three values.

```openscad
// f_min3 — Returns a function to calculate the minimum of three values.
// Usage: fn = f_min3();
```

#### fn = f_mod();

Returns a function to calculate the modulo of `a` divided by `b`.

```openscad
// f_mod — Returns a function to calculate the modulo of `a` divided by `b`.
// Usage: fn = f_mod();
```

#### fn = f_mul();

Returns a function to multiply `a` by `b`.

```openscad
// f_mul — Returns a function to multiply `a` by `b`.
// Usage: fn = f_mul();
```

#### fn = f_nand();

Returns a function to check if `a` and `b` are not both true.

```openscad
// f_nand — Returns a function to check if `a` and `b` are not both true.
// Usage: fn = f_nand();
```

#### fn = f_napprox();

Returns a function to compare if `a` is not approximately equal to `b`.

```openscad
fn_napprox = f_napprox();       // = function(a,b) napprox(a,b);
fn_napprox3 = f_napprox(3);     // = function(a) napprox(a,3);
fn_3napprox4 = f_napprox(3,4);  // = function() napprox(3,4);
```

#### fn = f_neg();

Returns a function to calculate `-a`

```openscad
// f_neg — Returns a function to calculate `-a`
// Usage: fn = f_neg();
```

#### fn = f_neq();

Returns a function to compare if `a` is not exactly equal to `b`.

```openscad
fn_neq = f_neq();       // = function(a,b) a!=b;
fn_neq3 = f_neq(3);     // = function(a) a!=3;
fn_3neq4 = f_neq(3,4);  // = function() 3!=4;
```

#### fn = f_nor();

Returns a function to check if neither `a` nor `b` are true.

```openscad
// f_nor — Returns a function to check if neither `a` nor `b` are true.
// Usage: fn = f_nor();
```

#### fn = f_norm();

Returns a function to calculate the norm of a given vector.

```openscad
// f_norm — Returns a function to calculate the norm of a given vector.
// Usage: fn = f_norm();
```

#### fn = f_not();

Returns a function to check if `a` is not true.

```openscad
// f_not — Returns a function to check if `a` is not true.
// Usage: fn = f_not();
```

#### fn = f_odd();

Returns a function to check if `a` is an odd number.

```openscad
l2 = filter(f_odd(), [3,4,5,6,7,8]);  // Returns: [3,5,7]
```

#### fn = f_or();

Returns a function to check if either `a` or `b` is true.

```openscad
// f_or — Returns a function to check if either `a` or `b` is true.
// Usage: fn = f_or();
```

#### fn = f_ord();

Returns a function to get the ordinal number of a string character.

```openscad
// f_ord — Returns a function to get the ordinal number of a string character.
// Usage: fn = f_ord();
```

#### fn = f_pow();

Returns a function to calculate `a` to the power of `b`.

```openscad
// f_pow — Returns a function to calculate `a` to the power of `b`.
// Usage: fn = f_pow();
```

#### fn = f_round();

Returns a function to calculate the integer rounding of a given number.

```openscad
// f_round — Returns a function to calculate the integer rounding of a given number.
// Usage: fn = f_round();
```

#### fn = f_sign();

Returns a function to calculate the sign of a given number.

```openscad
// f_sign — Returns a function to calculate the sign of a given number.
// Usage: fn = f_sign();
```

#### fn = f_sin();

Returns a function to calculate the sine of a value.

```openscad
// f_sin — Returns a function to calculate the sine of a value.
// Usage: fn = f_sin();
```

#### fn = f_sqr();

Returns a function to calculate the square of a given number.

```openscad
// f_sqr — Returns a function to calculate the square of a given number.
// Usage: fn = f_sqr();
```

#### fn = f_sqrt();

Returns a function to calculate the square root of a given number.

```openscad
// f_sqrt — Returns a function to calculate the square root of a given number.
// Usage: fn = f_sqrt();
```

#### fn = f_str();

Returns a function to get the string representation of an arbitrary value.

```openscad
// f_str — Returns a function to get the string representation of an arbitrary value.
// Usage: fn = f_str();
```

#### fn = f_str2();

Returns a function to concatenate the string representations of two arbitrary values.

```openscad
// f_str2 — Returns a function to concatenate the string representations of two arbitrary values.
// Usage: fn = f_str2();
```

#### fn = f_str3();

Returns a function to concatenate the string representations of three arbitrary values.

```openscad
// f_str3 — Returns a function to concatenate the string representations of three arbitrary values.
// Usage: fn = f_str3();
```

#### fn = f_sub();

Returns a function to subtract `a` from `b`.

```openscad
// f_sub — Returns a function to subtract `a` from `b`.
// Usage: fn = f_sub();
```

#### fn = f_tan();

Returns a function to calculate the tangent of a value.

```openscad
// f_tan — Returns a function to calculate the tangent of a value.
// Usage: fn = f_tan();
```

#### fn = f_xor();

Returns a function to check if either `a` or `b`, but not both, are true.

```openscad
// f_xor — Returns a function to check if either `a` or `b`, but not both, are true.
// Usage: fn = f_xor();
```

#### FACE(i)

Named face anchor constants

```openscad
// FACE — Named face anchor constants
// Usage: FACE(i)
```

#### PARENT() face_mask(faces) CHILDREN;

Ataches a 3d mask shape to the given faces of the parent.

```openscad
diff()
cylinder(r=30, h=60)
face_mask(TOP) {
rounding_cylinder_mask(r=30,rounding=5);
cuboid([5,61,10]);
}
```

#### PARENT() face_profile(faces, r|d=, [convexity=]) CHILDREN;

Extrudes a 2D edge profile into a mask for all edges and corners of the given faces on the parent.

```openscad
diff()
cube([50,60,70],center=true)
face_profile(TOP,r=10)
mask2d_roundover(r=10);
```

#### x = factorial(n, [d]);

Returns the factorial of the given integer.

```openscad
x = factorial(4);  // Returns: 24
y = factorial(6);  // Returns: 720
z = factorial(9);  // Returns: 362880
```

#### filled = fill(region);

Remove holes from a {{region}}

```openscad
$fs=.5;$fa=1;
reg=[circle(r=10),right(5.5,circle(r=1)),
circle(r=8), circle(r=3),
right(5.5,rect([3,4]))];
color("green")region(reg);
right(25)region(fill(reg));
```

#### fillet(l|h=|length=|height=, r|d=, [ang=], [excess=], [rounding=|chamfer=]) [ATTACHMENTS];

Creates a smooth fillet between two faces.

```openscad
union() {
translate([0,2,-4])
cube([20, 4, 24], anchor=BOTTOM);
translate([0,-10,-4])
cube([20, 20, 4], anchor=BOTTOM);
color("green")
fillet(
l=20, r=10,
spin=180, orient=RIGHT
);
}
```

#### lst = filter(func, list);

Returns just the list items which the given function returns true for.

```openscad
func = function(x) x>5;
echo(filter(func, [3,4,5,6,7]));
// ECHO: [6,7]
```

#### indices = find_all(func, list);

Returns the indices of all items in a list that a given function returns true for.

```openscad
func = function(x) x>5;
echo(find_all(func, [3,4,5,6,7]));
// ECHO: [3,4]
```

#### idx = find_approx(val, list, [start=], [eps=]);

Finds the indexes of the item(s) in the given list that are approximately the given value.

```openscad
find_approx(3,[4,5,3.01,2,2.99], eps=0.1);  // Returns 2
find_approx(9,[4,5,3.01,2,2.99], eps=0.1);  // Returns undef
find_approx(3,[4,5,3.01,2,2.99], all=true, eps=0.1);  // Returns [2,4]
find_approx(9,[4,5,3.01,2,2.99], all=true, eps=0.1);  // Returns []
```

#### idx = find_first(func, list, [start=]);

Returns the index of the first item in a list, after `start`, that a given function returns true for.

```openscad
data = [8,5,3,7,4,2,9];
echo(find_first(f_lte(4), data));
// ECHO: 2
```

#### val = first_defined(v, [recursive]);

Returns the first value in the argument list that is not 'undef'.

```openscad
val = first_defined([undef,7,undef,true]);  // Returns: 7
```

#### new_pts = fit_to_box(pts, [x=], [y=], [z=]);

Scale the x, y, and/or z coordinantes of a list of points to span a range.

```openscad
bez = [
[10,60], [-5,30],
[20,60], [50,50], [100,30],
[50,30], [70,20]
];
path = bezpath_curve(bez);
newpath = fit_to_box(path, x=[0,40], y=[0,40]);
stroke(path, width=2, color="red");
stroke(square(40), width=1, closed=true);
stroke(newpath, width=2, color="blue");
```

#### a = fit_to_range(M, minval, maxval);

Scale the values in an array to span a range.

```openscad
a =  [0.0066, 0.194, 0.598, 0.194, 0.0066];
v = fit_to_range(a,5,10);
// Returns: [5, 6.584, 10, 6.584, 5]

b = [ [20,20,0], [40,80,20], [60,40,20] ];
m = fit_to_range(b,-10,10);
// Returns:  [[-5,-5,-10], [0,10,-5], [5,0,-5]]

c = [2,3,4,5,6];
inv = fit_to_range(c, 20, 8); // inverted range!
// Returns:  [20, 17, 14, 11, 8]
```

#### list = flatten(l);

Flattens a list of sublists into a single list.

```openscad
l = flatten([[1,2,3], [4,5,[6,7,8]]]);  // returns [1,2,3,4,5,[6,7,8]]
```

#### x = for_n(n, init, func);

Iteratively calls a work function `n` times, returning the final result.

```openscad
fib = function(n) for_n(
n, [],
function(i,x) x? [x[1], x[0]+x[1]] : [0,1]
)[1];
```

#### list = force_list(value, [n], [fill]);

Coerces non-list values into a list.

```openscad
x = force_list([3,4,5]);  // Returns: [3,4,5]
y = force_list(5);  // Returns: [5]
z = force_list(7, n=3);  // Returns: [7,7,7]
w = force_list(4, n=3, fill=1);  // Returns: [4,1,1]
```

#### outpath = force_path(path, [name])

Checks that path is a region with one component.

```openscad
// force_path — Checks that path is a region with one component.
// Usage: outpath = force_path(path, [name])
```

#### region = force_region(poly)

Given a polygon returns a region.

```openscad
// force_region — Given a polygon returns a region.
// Usage: region = force_region(poly)
```

#### PARENT() force_tag([tag]) CHILDREN;

Assigns a tag to a non-attachable object.

```openscad
diff()
{
polygon(square(10));
move(-[.01,.01])polygon(square(5),$tag="remove");
}
```

#### s = format(fmt, vals);

Formats multiple values into a string with a given format.

```openscad
format("The value of {} is {:.14f}.", ["pi", PI]);  // Returns: "The value of pi is 3.14159265358979."
format("The value {1:f} is known as {0}.", ["pi", PI]);  // Returns: "The value 3.141593 is known as pi."
format("We use a very small value {1:.6g} as {0}.", ["EPSILON", EPSILON]);  // Returns: "We use a very small value 1e-9 as EPSILON."
format("{:-5s}{:i}{:b}", ["foo", 12e3, 5]);  // Returns: "foo  12000true"
format("{:-10s}{:.3f}", ["plecostamus",27.43982]);  // Returns: "plecostamus27.440"
format("{:-10.9s}{:.3f}", ["plecostamus",27.43982]);  // Returns: "plecostam 27.440"
```

#### s = format_fixed(f, [digits]);

Formats a float into a string with a fixed number of decimal places.

```openscad
// format_fixed — Formats a float into a string with a fixed number of decimal places.
// Usage: s = format_fixed(f, [digits]);
```

#### str = format_float(f,[sig]);

Formats a float into a string with a given number of significant digits.

```openscad
format_float(PI,12);  // Returns: "3.14159265359"
format_float([PI,-16.75],12);  // Returns: "[3.14159265359, -16.75]"
```

#### str = format_int(i, [mindigits]);

Formats an integer into a string, with possible leading zeros.

```openscad
str(123456789012345);  // Returns "1.23457e+14"
format_int(123456789012345);  // Returns "123456789012345"
format_int(-123456789012345); // Returns "-123456789012345"
format_int(12,3);             // Returns 012
```

#### frame_map(v1, v2, v3, [reverse=]) CHILDREN;

Rotates and possibly skews children from one frame of reference to another.

```openscad
frame_map(x=[0,1,0], y=[0,0,1]) linear_extrude(height=10) square(3);
```

#### frame_ref(s, opacity);

Shows axis orientation arrows.

```openscad
frame_ref(25);
frame_ref(30, opacity=0.5);
```

#### front_half([s], [y]) CHILDREN;

Masks the back half of an object along the X-Z plane, leaving the front half.

```openscad
front_half() sphere(r=20);
front_half(y=5) sphere(r=20);
```

#### list = full_flatten(l);

Recursively flattens nested sublists into a single list of non-list values.

```openscad
l = full_flatten([[1,2,3], [4,5,[6,7,8]]]);  // returns [1,2,3,4,5,6,7,8]
```

#### index = furthest_point(pt, points);

Finds the furthest point in a list of points.

```openscad
// furthest_point — Finds the furthest point in a list of points.
// Usage: index = furthest_point(pt, points);
```

#### fwd(y) CHILDREN;

Translates children forward (Y-).

```openscad
#sphere(d=10);
fwd(20) sphere(d=10);
```

#### arr = gaussian_rands([n],[mean], [cov], [seed]);

Returns a list of random numbers with a gaussian distribution.

```openscad
// gaussian_rands — Returns a list of random numbers with a gaussian distribution.
// Usage: arr = gaussian_rands([n],[mean], [cov], [seed]);
```

#### x = gcd(a,b)

Returns the Greatest Common Divisor/Factor of two integers.

```openscad
// gcd — Returns the Greatest Common Divisor/Factor of two integers.
// Usage: x = gcd(a,b)
```

#### dist = gear_dist(mod=|diam_pitch=|circ_pitch=, teeth1, teeth2, [helical], [profile_shift1], [profile_shift2], [pressure_angle=], [backlash=]);

Returns the distance between two gear centers for spur gears or parallel axis helical gears.

```openscad
circ_pitch=5; teeth1=7; teeth2=24;
d = gear_dist(circ_pitch=circ_pitch, teeth1, teeth2);
spur_gear2d(circ_pitch, teeth1, gear_spin=-90);
right(d) spur_gear2d(circ_pitch, teeth2, gear_spin=90-180/teeth2);
```

#### dist = gear_dist_skew(mod=|diam_pitch=|circ_pitch=, teeth1, teeth2, helical1, helical2, [profile_shift1], [profile_shift2], [pressure_angle=]

Returns the distance between two helical gear centers with skew axes.

```openscad
circ_pitch=5; teeth1=15; teeth2=24; ha1=45; ha2=30; thick=10;
d = gear_dist_skew(circ_pitch=circ_pitch, teeth1, teeth2, helical1=ha1, helical2=ha2);
left(d/2) spur_gear(circ_pitch, teeth1, helical=ha1, thickness=thick, gear_spin=-90);
right(d/2) xrot(ha1+ha2) spur_gear(circ_pitch, teeth2, helical=ha2, thickness=thick, gear_spin=90-180/teeth2);
```

#### shorten = gear_shorten(teeth1, teeth2, [helical], [profile_shift1], [profile_shift2], [pressure_angle=]);

Returns the tip shortening parameter for profile shifted parallel axis gears.

```openscad
teeth1=25;
teeth2=19;
mod=4;
ps1 = 0.75;
ps2 = 0.75;
d = gear_dist(mod=mod, teeth1,teeth2,0,ps1,ps2);
color("lightblue")
spur_gear2d(mod=mod,teeth=teeth1,profile_shift=ps1,gear_spin=-90);
right(d)
spur_gear2d(mod=mod,teeth=teeth2,profile_shift=ps2,gear_spin=-90);
```

#### shorten = gear_shorten_skew(teeth1, teeth2, helical1, helical2, [profile_shift1], [profile_shift2], [pressure_angle=]);

Returns the tip shortening parameter for profile shifted skew axis helical gears.

```openscad
// gear_shorten_skew — Returns the tip shortening parameter for profile shifted skew axis helical gears.
// Usage: shorten = gear_shorten_skew(teeth1, teeth2, helical1, helical2, [profile_shift1], [profile_shift2], [pressure_angle=]);
```

#### ang = gear_skew_angle(teeth1, teeth2, helical1, helical2, [profile_shift1], [profile_shift2], [pressure_angle=]

Returns corrected skew angle between two profile shifted helical gears.

```openscad
circ_pitch=5; teeth1=12; teeth2=7; ha1=25; ha2=30; thick=10;
d = gear_dist_skew(circ_pitch=circ_pitch, teeth1, teeth2, ha1, ha2);
ang = gear_skew_angle(teeth1, teeth2, helical1=ha1, helical2=ha2);  // Returns 57.7
left(d/2)
spur_gear(circ_pitch, teeth1, helical=ha1, thickness=thick, gear_spin=-90);
right(d/2) color("lightblue")
xrot(ang) spur_gear(circ_pitch, teeth2, helical=ha2, thickness=thick, gear_spin=90-180/teeth2);
```

#### generic_airplane([s]);

Shows a generic airplane shape, useful for viewing orientations.

```openscad
generic_airplane(s=20);
```

#### generic_bottle_cap(wall, [texture], ...) [ATTACHMENTS];

Creates a generic cap for a bottle.

```openscad
generic_bottle_cap(thread_depth=2,neck_od=INCH,height=INCH/2);
generic_bottle_cap(texture="knurled",neck_od=25,thread_od=30,height=10);
generic_bottle_cap(texture="ribbed",thread_depth=3,thread_od=25,height=13);
```

#### generic_bottle_neck([wall], ...) [ATTACHMENTS];

Creates a generic neck for a bottle.

```openscad
generic_bottle_neck();
```

#### generic_screw(screwsize, screwlen, headsize, headlen)

Creates a simple screw model, useful for making screwholes.

```openscad
generic_screw(screwsize=3,screwlen=10,headsize=6,headlen=3, anchor="countersunk");
generic_screw(screwsize=3,screwlen=10,headsize=6,headlen=3, anchor="base");
```

#### generic_threaded_nut(nutwidth, id, h|height|thickness, pitch, profile, [$slop], ...) [ATTACHMENTS];

Creates a generic threaded nut.

```openscad
// generic_threaded_nut — Creates a generic threaded nut.
// Usage: generic_threaded_nut(nutwidth, id, h|height|thickness, pitch, profile, [$slop], ...) [ATTACHMENTS];
```

#### generic_threaded_rod(d, l|length, pitch, profile, [internal=], ...) [ATTACHMENTS];

Creates a generic threaded rod.

```openscad
pitch = 2;
depth = pitch * cos(30) * 5/8;
profile = [
[-7/16, -depth/pitch*1.07],
[-6/16, -depth/pitch],
[-1/16,  0],
[ 1/16,  0],
[ 6/16, -depth/pitch],
[ 7/16, -depth/pitch*1.07]
];
stroke(profile, width=0.02);
```

#### anchr = get_anchor(anchor,center,[uncentered],[dflt]);

Returns the correct anchor from `anchor` and `center`.

```openscad
anchr1 = get_anchor(undef, undef, BOTTOM, TOP);  // Returns: [0, 0, 1] (TOP)
anchr2 = get_anchor(RIGHT, undef, BOTTOM, TOP);  // Returns: [1, 0, 0] (RIGHT)
anchr3 = get_anchor(undef, false, BOTTOM, TOP);  // Returns: [0, 0,-1] (BOTTOM)
anchr4 = get_anchor(RIGHT, false, BOTTOM, TOP);  // Returns: [0, 0,-1] (BOTTOM)
anchr5 = get_anchor(undef, true,  BOTTOM, TOP);  // Returns: [0, 0, 0] (CENTER)
anchr6 = get_anchor(RIGHT, true,  BOTTOM, TOP);  // Returns: [0, 0, 0] (CENTER)
```

#### get_metric_bolt_head_height(...)

Returns the height of a metric bolt head.

```openscad
// get_metric_bolt_head_height — Returns the height of a metric bolt head.
// Usage: get_metric_bolt_head_height(...)
```

#### get_metric_bolt_head_size(...)

Returns the diameter of a metric bolt head.

```openscad
// get_metric_bolt_head_size — Returns the diameter of a metric bolt head.
// Usage: get_metric_bolt_head_size(...)
```

#### get_metric_iso_coarse_thread_pitch(...)

Returns the ISO metric coarse thread pitch for a given bolt size.

```openscad
// get_metric_iso_coarse_thread_pitch — Returns the ISO metric coarse thread pitch for a given bolt size.
// Usage: get_metric_iso_coarse_thread_pitch(...)
```

#### get_metric_iso_fine_thread_pitch(...)

Returns the ISO metric fine thread pitch for a given bolt size.

```openscad
// get_metric_iso_fine_thread_pitch — Returns the ISO metric fine thread pitch for a given bolt size.
// Usage: get_metric_iso_fine_thread_pitch(...)
```

#### get_metric_iso_superfine_thread_pitch(...)

Returns the ISO metric superfine thread pitch for a given bolt size.

```openscad
// get_metric_iso_superfine_thread_pitch — Returns the ISO metric superfine thread pitch for a given bolt size.
// Usage: get_metric_iso_superfine_thread_pitch(...)
```

#### get_metric_jis_thread_pitch(...)

Returns the JIS metric thread pitch for a given bolt size.

```openscad
// get_metric_jis_thread_pitch — Returns the JIS metric thread pitch for a given bolt size.
// Usage: get_metric_jis_thread_pitch(...)
```

#### get_metric_nut_size(...)

Returns the flat-to-flat diameter of a metric nut.

```openscad
// get_metric_nut_size — Returns the flat-to-flat diameter of a metric nut.
// Usage: get_metric_nut_size(...)
```

#### get_metric_nut_thickness(...)

Returns the thickness of a metric nut.

```openscad
// get_metric_nut_thickness — Returns the thickness of a metric nut.
// Usage: get_metric_nut_thickness(...)
```

#### get_metric_socket_cap_diam(...)

Returns the diameter of a metric socket cap bolt head.

```openscad
// get_metric_socket_cap_diam — Returns the diameter of a metric socket cap bolt head.
// Usage: get_metric_socket_cap_diam(...)
```

#### get_metric_socket_cap_height(...)

Returns the height of a metric socket cap bolt head.

```openscad
// get_metric_socket_cap_height — Returns the height of a metric socket cap bolt head.
// Usage: get_metric_socket_cap_height(...)
```

#### get_metric_socket_cap_socket_depth(...)

Returns the hex drive socket depth of a metric socket cap bolt.

```openscad
// get_metric_socket_cap_socket_depth — Returns the hex drive socket depth of a metric socket cap bolt.
// Usage: get_metric_socket_cap_socket_depth(...)
```

#### get_metric_socket_cap_socket_size(...)

Returns the hex drive socket diameter of a metric socket cap bolt.

```openscad
// get_metric_socket_cap_socket_size — Returns the hex drive socket diameter of a metric socket cap bolt.
// Usage: get_metric_socket_cap_socket_size(...)
```

#### total_shift = get_profile_shift(mod=|diam_pitch=|circ_pitch=, desired, teeth1, teeth2, [helical], [pressure_angle=],

Returns total profile shift needed to achieve a desired spacing between two gears

```openscad
teeth1=37;
teeth2=19;
mod=4;
desired=115;
pshift = get_profile_shift(desired,teeth1,teeth2,mod=mod);  // Returns 0.82
ps1 = pshift/2;
ps2 = pshift/2;
shorten=gear_shorten(teeth1,teeth2,0,ps1,ps2);       // Returns 0.07
d = gear_dist(mod=mod, teeth1,teeth2,0,ps1,ps2);
spur_gear2d(mod=mod,teeth=teeth1,profile_shift=ps1,shorten=shorten,gear_spin=-90,shaft_diam=5);
right(d)
spur_gear2d(mod=mod,teeth=teeth2,profile_shift=ps2,shorten=shorten,gear_spin=-90,shaft_diam=5);
stroke([rect([desired,40], anchor=LEFT)],color="red");
```

#### r = get_radius([r1=], [r2=], [r=], [d1=], [d2=], [d=], [dflt=]);

Given various radii and diameters, returns the most specific radius.

```openscad
r = get_radius(r1=undef, r=undef, dflt=undef);  // Returns: undef
r = get_radius(r1=undef, r=undef, dflt=1);      // Returns: 1
r = get_radius(r1=undef, r=6, dflt=1);          // Returns: 6
r = get_radius(r1=7, r=6, dflt=1);              // Returns: 7
r = get_radius(r1=undef, r2=8, r=6, dflt=1);    // Returns: 8
r = get_radius(r1=undef, r2=8, d=6, dflt=1);    // Returns: 8
r = get_radius(r1=undef, d=6, dflt=1);          // Returns: 3
r = get_radius(d1=7, d=6, dflt=1);              // Returns: 3.5
r = get_radius(d1=7, d2=8, d=6, dflt=1);        // Returns: 3.5
r = get_radius(d1=undef, d2=8, d=6, dflt=1);    // Returns: 4
r = get_radius(r1=8, d=6, dflt=1);              // Returns: 8
```

#### slop = get_slop();

Returns the $slop value.

```openscad
// get_slop — Returns the $slop value.
// Usage: slop = get_slop();
```

#### ghost([ghost]) CHILDREN;

Sets % modifier for attachable children and their descendents.

```openscad
ghost() cuboid(10)
ghost(false) cuboid(5);
```

#### ghost_this() CHILDREN;

Apply % modifier to children at a single level.

```openscad
ghost_this() cuboid(10)
cuboid(5);
```

#### glued_circles(r/d=, [spread], [r1=/d1=], [r2=/d2=], [tangent=], [bulge=], [width=], [blendR=/blendD=], anchor=, spin=) [ATTACHMENTS];

Creates a shape of two circles joined by a curved waist.

```openscad
glued_circles(r=15, spread=40, tangent=45);
glued_circles(d=30, spread=30, tangent=30);
glued_circles(d=30, spread=30, tangent=15);
glued_circles(d=30, spread=30, tangent=-30);
```

#### grid_copies(spacing, size=, [stagger=], [scale=], [inside=], [axes=]) CHILDREN;

Places copies of children in an [X,Y] grid.

```openscad
grid_copies(size=50, spacing=10) cylinder(d=10, h=1);
grid_copies(size=50, spacing=[10,15]) cylinder(d=10, h=1);
grid_copies(spacing=10, n=[13,7], stagger=true) cylinder(d=6, h=5);
grid_copies(spacing=10, n=[13,7], stagger="alt") cylinder(d=6, h=5);
grid_copies(size=50, n=11, stagger=true) cylinder(d=5, h=1);
```

#### groupings = group_data(groups, values);

Groups list data by integer group numbers.

```openscad
groups = group_data([1,2,0], ["A","B","C"]);  // Returns [["B"],["C"],["A"]]
```

#### ulist = group_sort(list,[idx]);

Returns a sorted list of groups of values.

```openscad
sorted = group_sort([5,2,8,3,1,3,8,7,5]);  // Returns: [[1],[2],[3,3],[5,5],[7],[8,8]]
// Next example returns: [ [[2,"b"],[2,"e"]], [[3,"d"]], [[5,"a"],[5,"c"]] ]
sorted2 = group_sort([[5,"a"],[2,"b"], [5,"c"], [3,"d"], [2,"e"] ], idx=0);
```

#### half_joiner(l, w, [base=], [ang=], [screwsize=], [$slop=]) [ATTACHMENTS];

Creates a half-joiner shape to mate with a {{half_joiner2()}} shape..

```openscad
half_joiner(screwsize=3);
half_joiner(l=20,w=10,base=10);
```

#### half_joiner_clear(l, w, [ang=], [clearance=], [overlap=]) [ATTACHMENTS];

Creates a mask to clear space for a {{half_joiner()}}.

```openscad
half_joiner_clear();
```

#### half_joiner2(l, w, [base=], [ang=], [screwsize=])

Creates a half_joiner2 shape to mate with a {{half_joiner()}} shape..

```openscad
half_joiner2(screwsize=3);
half_joiner2(w=10,base=10,l=20);
```

#### half_of(v, [cp], [s], [planar]) CHILDREN;

Masks half of an object at a cut plane.

```openscad
half_of(DOWN+BACK, cp=[0,-10,0]) cylinder(h=40, r1=10, r2=0, center=false);
half_of(DOWN+LEFT, s=200) sphere(d=150);
```

#### hm = hashmap([hashsize=]);

Creates a hashmap manipulation function.

```openscad
hm = hashmap(items=[for (i=[0:9999]) [str("foo",i),i]]);
a = hm("foo37");  // Returns: 37
hm2 = hm("Blah", 39);  // Adds entry "Blah" with val 39.
b = hm2("Blah");  // Returns: 39
hm3 = hm2(additems=[["bar",39],["qux",21]]);  // Adds "bar" and "qux"
hm4 = hm3(del="Blah");  // Deletes entry "Blah".
for (kv = hm4()) {  // Iterates over all key/value pairs.
echo(key=kv[0], val=kv[1]);
}
```

#### path = helix(l|h, [turns=], [angle=], r=|r1=|r2=, d=|d1=|d2=);

Creates a 2d spiral or 3d helical path.

```openscad
stroke(helix(turns=2.5, h=100, r=50), dots=true, dots_color="blue");
```

#### hex_drive_mask(size, length, [anchor], [spin], [orient], [$slop]) [ATTACHMENTS];

Creates a mask for a hex drive recess.

```openscad
// hex_drive_mask — Creates a mask for a hex drive recess.
// Usage: hex_drive_mask(size, length, [anchor], [spin], [orient], [$slop]) [ATTACHMENTS];
```

#### hex_panel(shape, wall, spacing, [frame=], [bevel=], [bevel_frame=], [shift=], [h=|height=|l=|length=], [anchor=], [orient=], [spin=])

Create a hexagon braced panel of any shape

```openscad
hex_panel([50, 100, 5], strut=1.5, spacing=10);
hex_panel([50, 100, 5], 1.5, 10, frame = 5);
hex_panel([50, 100, 5], 5, 10.05);
hex_panel([50, 100, 5], 1.5, 20, frame = 5);
hex_panel([50, 100, 5], 1.5, 12, frame = 0);
hex_panel([50, 100, 5], frame = 10, spacing = 20, strut = 4);
hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [LEFT, RIGHT]);
hex_panel([50, 100, 10], 1.5, 10, frame = 5, bevel = [FWD,  BACK]);
hex_panel([50, 100, 10], 1.5, 10, frame = 3, bevel = [LEFT, RIGHT, FWD, BACK]);
hex_panel([50, 100, 10], 1.5, 10, frame = 1, bevel = [LEFT, RIGHT, FWD+BOTTOM, BACK+BOTTOM]);
hex_panel([50, 100, 10], 1.5, 10, frame=2, bevel_frame=0, bevel = [FWD, BACK+BOT, RIGHT, LEFT]);
```

#### hexagon(r/or, [realign=], <align_tip=|align_side=>, [rounding=], ...) [ATTACHMENTS];

Creates a regular hexagon.

```openscad
hexagon(or=30);
hexagon(od=60);
```

#### hide(tags) CHILDREN;

Hides attachable children with the given tags.

```openscad
hide("A")
tag("main") cube(50, anchor=CENTER, $tag="Main") {
tag("A")attach(LEFT, BOTTOM) cylinder(d=30, h=30);
tag("B")attach(RIGHT, BOTTOM) cylinder(d=30, h=30);
}
```

#### hide_this() CHILDREN;

Hides attachable children at the current level

```openscad
$fn=16;
hide_this() cuboid(10)
{
attach(RIGHT,BOT) cyl(r=1,h=5);
attach(LEFT,BOT) cyl(r=1,h=5);
}
```

#### highlight([highlight]) CHILDREN;

Sets # modifier for attachable children and their descendents.

```openscad
highlight() cuboid(10)
highlight(false) attach(RIGHT,BOT)cuboid(5);
```

#### highlight_this() CHILDREN;

Apply # modifier to children at a single level.

```openscad
highlight_this()
cuboid(10)
attach(TOP,BOT)cuboid(5);
```

#### hirth(n, ir|id=, or|od=, tooth_angle, [cone_angle=], [chamfer=], [rounding=], [base=], [crop=], [anchor=], [spin=], [orient=]

Creates a Hirth face spline that locks together two cylinders.

```openscad
hirth(32,20,50);
```

#### hsl(h,[s],[l],[a]) CHILDREN;

Sets the color of children to a specified hue, saturation, lightness and optional alpha channel value.

```openscad
hsl(h=120,s=1,l=0.5) sphere(d=60);
```

#### A = hstack(M1, M2)

Make a new matrix by stacking matrices horizontally.

```openscad
M = ident(3);
v1 = [2,3,4];
v2 = [5,6,7];
v3 = [8,9,10];
a = hstack(v1,v2);     // Returns [[2, 5], [3, 6], [4, 7]]
b = hstack(v1,v2,v3);  // Returns [[2, 5,  8],
//          [3, 6,  9],
//          [4, 7, 10]]
c = hstack([M,v1,M]);  // Returns [[1, 0, 0, 2, 1, 0, 0],
//          [0, 1, 0, 3, 0, 1, 0],
//          [0, 0, 1, 4, 0, 0, 1]]
d = hstack(column(M,0), submatrix(M,idx(M),[1 2]));  // Returns M
strvec = ["one","two"];
strmat = [["three","four"], ["five","six"]];
e = hstack(strvec,strvec); // Returns [["o", "n", "e", "o", "n", "e"],
//          ["t", "w", "o", "t", "w", "o"]]
f = hstack(list_to_matrix(strvec,1), list_to_matrix(strvec,1));
// Returns [["one", "one"],
//          ["two", "two"]]
g = hstack(strmat,strmat); //  Returns: [["three", "four", "three", "four"],
//            [ "five",  "six",  "five",  "six"]]
```

#### hsv(h,[s],[v],[a]) CHILDREN;

Sets the color of children to a hue, saturation, value and optional alpha channel value.

```openscad
hsv(h=120,s=1,v=1) sphere(d=60);
```

#### face_list_or_index_list = hull(points);

Convex hull of a list of 2d or 3d points.

```openscad
// hull — Convex hull of a list of 2d or 3d points.
// Usage: face_list_or_index_list = hull(points);
```

#### hull_points(points, [fast]);

Convex hull of a list of 2d or 3d points.

```openscad
pts = [[-10,-10], [0,10], [10,10], [12,-10]];
hull_points(pts);
```

#### path = hull_region(region);

Compute convex hull of {{region}} or 2D {{path}}

```openscad
data = [star(id=10,od=20,n=9),
right(30, star(id=12,od=25, n=7))];
stroke(data);
stroke([hull_region(data)],color="red");
```

#### index_list = hull2d_path(points,all)

Convex hull of a list of 2d points.

```openscad
pts = [[-10,-10], [0,10], [10,10], [12,-10]];
path = hull2d_path(pts);
move_copies(pts) color("red") circle(1,$fn=12);
polygon(points=pts, paths=[path]);
```

#### faces = hull3d_faces(points)

Convex hull of a list of 3d points.

```openscad
pts = [[-20,-20,0], [20,-20,0], [0,20,5], [0,0,20]];
faces = hull3d_faces(pts);
move_copies(pts) color("red") sphere(1);
%polyhedron(points=pts, faces=faces);
```

#### ang = hyp_adj_to_ang(hyp,adj);

Returns the reference reference angle from the lengths of the hypotenuse and the adjacent side.

```openscad
ang = hyp_adj_to_ang(8,4);  // Returns: 60 degrees
```

#### opp = hyp_adj_to_opp(hyp,adj);

Returns the opposite side length from the lengths of the hypotenuse and the adjacent side.

```openscad
opp = hyp_adj_to_opp(5,4);  // Returns: 3
```

#### adj = hyp_ang_to_adj(hyp,ang);

Returns the adjacent side length from the length of the hypotenuse and the reference reference angle.

```openscad
adj = hyp_ang_to_adj(8,60);  // Returns: 4
```

#### opp = hyp_ang_to_opp(hyp,ang);

Returns the opposite side length from the length of the hypotenuse and the reference reference angle.

```openscad
opp = hyp_ang_to_opp(8,30);  // Returns: 4
```

#### adj = hyp_opp_to_adj(hyp,opp);

Returns the adjacent side length from the lengths of the hypotenuse and the opposite side.

```openscad
adj = hyp_opp_to_adj(5,3);  // Returns: 4
```

#### ang = hyp_opp_to_ang(hyp,opp);

Returns the reference reference angle from the lengths of the hypotenuse and the opposite side.

```openscad
ang = hyp_opp_to_ang(8,4);  // Returns: 30 degrees
```

#### l = hypot(x, y, [z]);

Returns the hypotenuse length of a 2D or 3D triangle.

```openscad
l = hypot(3,4);  // Returns: 5
l = hypot(3,4,5);  // Returns: ~7.0710678119
```

#### mat = ident(n);

Return identity matrix.

```openscad
mat = ident(3);
// Returns:
//   [
//     [1, 0, 0],
//     [0, 1, 0],
//     [0, 0, 1]
//   ]
```

#### range = idx(list, [s=], [e=], [step=]);

Returns a range useful for iterating over a list.

```openscad
colors = ["red", "green", "blue"];
for (i=idx(colors)) right(20*i) color(colors[i]) circle(d=10);
```

#### bool = in_list(val, list, [idx]);

Returns true if a value is in a list.

```openscad
a = in_list("bar", ["foo", "bar", "baz"]);  // Returns true.
b = in_list("bee", ["foo", "bar", "baz"]);  // Returns false.
c = in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1);  // Returns true.
```

#### intersect([intersect], [keep]) PARENT() CHILDREN;

Perform an intersection operation on children using tags rather than hierarchy to control what happens.

```openscad
intersect("mask", keep="axle")
sphere(d=100) {
tag("mask")cuboid([40,100,100]);
tag("axle")xcyl(d=40, l=100);
}
```

#### intersection() CHILDREN;

Performs a Boolean intersection operation.

```openscad
shape1 = move([-8,-8,0], p=circle(d=50));
shape2 = move([ 8, 8,0], p=circle(d=50));
for (shape = [shape1,shape2])
stroke(shape,width=0.5, color="red", closed=true);
color("green") region(intersection(shape1,shape2));
```

#### bool = is_1region(path, [name])

Returns true if {{path}} is a {{region}} with one component.

```openscad
// is_1region — Returns true if {{path}} is a {{region}} with one component.
// Usage: bool = is_1region(path, [name])
```

#### bool = is_bezier_patch(x);

Returns true if the given item is a bezier patch.

```openscad
// is_bezier_patch — Returns true if the given item is a bezier patch.
// Usage: bool = is_bezier_patch(x);
```

#### check = is_bool_list(list,[length])

Returns true if the argument list contains only booleans.

```openscad
// is_bool_list — Returns true if the argument list contains only booleans.
// Usage: check = is_bool_list(list,[length])
```

#### bool = is_collinear(a, [b, c], [eps]);

Determine if points are collinear.

```openscad
// is_collinear — Determine if points are collinear.
// Usage: bool = is_collinear(a, [b, c], [eps]);
```

#### bool = is_consistent(list, [pattern]);

Returns true if the argument is a list with consistent structure and finite numerical data.

```openscad
is_consistent([3,4,5]);              // Returns true
is_consistent([[3,4],[4,5],[6,7]]);  // Returns true
is_consistent([[3,4,5],[3,4]]);      // Returns false
is_consistent([[3,[3,4,[5]]], [5,[2,9,[9]]]]); // Returns true
is_consistent([[3,[3,4,[5]]], [5,[2,9,9]]]);   // Returns false
is_consistent([3,4,5], 0);            // Returns true
is_consistent([3,4,undef], 0);        // Returns false
is_consistent([[3,4],[4,5]], [1,1]);  // Returns true
is_consistent([[3,"a"],[4,true]], [1,undef]);  // Returns true
is_consistent([[3,4], 6, [4,5]], [1,1]);  // Returns false
is_consistent([[1,[3,4]], [4,[5,6]]], [1,[2,3]]);    // Returns true
is_consistent([[1,[3,INF]], [4,[5,6]]], [1,[2,3]]);  // Returns false
is_consistent([], [1,[2,3]]);                        // Returns true
```

#### bool = is_coplanar(points,[eps]);

Check if 3d points are coplanar and not collinear.

```openscad
// is_coplanar — Check if 3d points are coplanar and not collinear.
// Usage: bool = is_coplanar(points,[eps]);
```

#### bool = is_decreasing(list, [strict]);

Returns true if exery item in a list is less than the previous item.

```openscad
a = is_decreasing([1,2,3,4]);  // Returns: false
b = is_decreasing([4,2,3,1]);  // Returns: false
c = is_decreasing([4,3,2,1]);  // Returns: true
```

#### bool = is_def(x);

Returns true if `x` is not `undef`.

```openscad
bool = is_def(undef);  // Returns: false
bool = is_def(false);  // Returns: true
bool = is_def(42);     // Returns: true
bool = is_def("foo");  // Returns: true
```

#### bool = is_description(desc);

Check if its argument is a description

```openscad
// is_description — Check if its argument is a description
// Usage: bool = is_description(desc);
```

#### x = is_digit(s);

Returns true if all characters in the string are decimal digits.

```openscad
// is_digit — Returns true if all characters in the string are decimal digits.
// Usage: x = is_digit(s);
```

#### bool = is_finite(x);

Returns true if the argument is a finite number.

```openscad
bool = is_finite(undef);  // Returns: false
bool = is_finite(false);  // Returns: false
bool = is_finite(42);     // Returns: true
bool = is_finite("foo");  // Returns: false
bool = is_finite(NAN);    // Returns: false
bool = is_finite(INF);    // Returns: false
bool = is_finite(-INF);   // Returns: false
```

#### bool = is_func(x);

Returns true if the argument is a function literal.

```openscad
f = function (a) a==2;
bool = is_func(f);  // Returns: true
```

#### x = is_hexdigit(s);

Returns true if all characters in the string are hexidecimal digits.

```openscad
// is_hexdigit — Returns true if all characters in the string are hexidecimal digits.
// Usage: x = is_hexdigit(s);
```

#### bool = is_homogeneous(list, [depth]);

Returns true if all members of a list are of the same type.

```openscad
a = is_homogeneous([[1,["a"]], [2,["b"]]]);     // Returns true
b = is_homogeneous([[1,["a"]], [2,[true]]]);    // Returns false
c = is_homogeneous([[1,["a"]], [2,[true]]], 1); // Returns true
d = is_homogeneous([[1,["a"]], [2,[true]]], 2); // Returns false
e = is_homogeneous([[1,["a"]], [true,["b"]]]);  // Returns true
```

#### bool = is_increasing(list, [strict]);

Returns true if every item in a list is greater than the previous item.

```openscad
a = is_increasing([1,2,3,4]);  // Returns: true
b = is_increasing([1,3,2,4]);  // Returns: false
c = is_increasing([1,3,3,4]);  // Returns: true
d = is_increasing([1,3,3,4],strict=true);  // Returns: false
e = is_increasing([4,3,2,1]);  // Returns: false
```

#### bool = is_int(n);

Returns true if the argument is an integer.

```openscad
bool = is_int(undef);  // Returns: false
bool = is_int(false);  // Returns: false
bool = is_int(42);     // Returns: true
bool = is_int("foo");  // Returns: false
```

#### x = is_letter(s);

Returns true if all characters in the string are letters.

```openscad
// is_letter — Returns true if all characters in the string are letters.
// Usage: x = is_letter(s);
```

#### x = is_lower(s);

Returns true if all characters in the string are lowercase.

```openscad
// is_lower — Returns true if all characters in the string are lowercase.
// Usage: x = is_lower(s);
```

#### test = is_matrix(A, [m], [n], [square])

Check if input is a numeric matrix, optionally of specified size

```openscad
// is_matrix — Check if input is a numeric matrix, optionally of specified size
// Usage: test = is_matrix(A, [m], [n], [square])
```

#### b = is_matrix_symmetric(A, [eps])

Checks if matrix is symmetric

```openscad
// is_matrix_symmetric — Checks if matrix is symmetric
// Usage: b = is_matrix_symmetric(A, [eps])
```

#### bool = is_nan(x);

Return true if the argument is "not a number".

```openscad
bool = is_nan(undef);  // Returns: false
bool = is_nan(false);  // Returns: false
bool = is_nan(42);     // Returns: false
bool = is_nan("foo");  // Returns: false
bool = is_nan(NAN);    // Returns: true
```

#### bool = is_nurbs_patch(x);

Returns true if the given item looks like a NURBS patch.

```openscad
// is_nurbs_patch — Returns true if the given item looks like a NURBS patch.
// Usage: bool = is_nurbs_patch(x);
```

#### is_path(list, [dim], [fast])

Returns True if 'list' is a {{path}}.

```openscad
bool1 = is_path([[3,4],[5,6]]);    // Returns true
bool2 = is_path([[3,4]]);          // Returns false
bool3 = is_path([[3,4],[4,5]],2);  // Returns true
bool4 = is_path([[3,4,3],[5,4,5]],2);  // Returns false
bool5 = is_path([[3,4,3],[5,4,5]],2);  // Returns false
bool6 = is_path([[3,4,5],undef,[4,5,6]]);  // Returns false
bool7 = is_path([[3,5],[undef,undef],[4,5]]);  // Returns false
bool8 = is_path([[3,4],[5,6],[5,3]]);     // Returns true
bool9 = is_path([3,4,5,6,7,8]);           // Returns false
bool10 = is_path([[3,4],[5,6]], dim=[2,3]);// Returns true
bool11 = is_path([[3,4],[5,6]], dim=[1,3]);// Returns false
bool12 = is_path([[3,4],"hello"], fast=true); // Returns true
bool13 = is_path([[3,4],[3,4,5]]);            // Returns false
bool14 = is_path([[1,2,3,4],[2,3,4,5]]);      // Returns false
bool15 = is_path([[1,2,3,4],[2,3,4,5]],undef);// Returns true
```

#### bool = is_path_simple(path, [closed], [eps]);

Returns true if a {{path}} has no self intersections.

```openscad
// is_path_simple — Returns true if a {{path}} has no self intersections.
// Usage: bool = is_path_simple(path, [closed], [eps]);
```

#### pt = is_point_on_line(point, line, [bounded], [eps]);

Determine if a point is on a line, ray or segment.

```openscad
// is_point_on_line — Determine if a point is on a line, ray or segment.
// Usage: pt = is_point_on_line(point, line, [bounded], [eps]);
```

#### bool = is_polygon_clockwise(poly);

Determine if a 2d polygon winds clockwise.

```openscad
// is_polygon_clockwise — Determine if a 2d polygon winds clockwise.
// Usage: bool = is_polygon_clockwise(poly);
```

#### bool = is_polygon_convex(poly, [eps]);

Check if a polygon is convex.

```openscad
test1 = is_polygon_convex(circle(d=50));                                 // Returns: true
test2 = is_polygon_convex(rot([50,120,30], p=path3d(circle(1,$fn=50)))); // Returns: true
spiral = [for (i=[0:36]) let(a=-i*10) (10+i)*[cos(a),sin(a)]];
test = is_polygon_convex(spiral);                                        // Returns: false
```

#### bool = is_range(x);

Returns true if the argument is a range.

```openscad
bool = is_range(undef);   // Returns: false
bool = is_range(false);   // Returns: false
bool = is_range(42);      // Returns: false
bool = is_range([3,4,5]); // Returns: false
bool = is_range("foo");   // Returns: false
bool = is_range([3:5]);   // Returns: true
```

#### bool = is_region(x);

Returns true if the input appears to be a {{region}}.

```openscad
// is_region — Returns true if the input appears to be a {{region}}.
// Usage: bool = is_region(x);
```

#### bool = is_region_simple(region, [eps]);

Returns true if the input is a region with no corner contact.

```openscad
region = [move([-2,-2],square(14)), [[0,0],[10,0],[5,5]], [[5,5],[0,10],[10,10]]];
rainbow(region)stroke($item, width=.2,closed=true);
move([-1,13])text(is_region_simple(region) ? "simple" : "not-simple", size=2);
```

#### b = is_rotation(A, [dim], [centered])

Check if a transformation matrix represents a rotation.

```openscad
// is_rotation — Check if a transformation matrix represents a rotation.
// Usage: b = is_rotation(A, [dim], [centered])
```

#### bool = is_str(x);

Returns true if the argument is a string.

```openscad
bool = is_str(undef);  // Returns: false
bool = is_str(false);  // Returns: false
bool = is_str(42);     // Returns: false
bool = is_str("foo");  // Returns: true
```

#### bool = is_struct(struct);

Returns true if the value is a struct.

```openscad
// is_struct — Returns true if the value is a struct.
// Usage: bool = is_struct(struct);
```

#### bool = is_type(x, types);

Returns true if the type of 'x' is one of those in the list `types`.

```openscad
is_str_or_list = is_type("foo", ["string","list"]);   // Returns: true
is_str_or_list2 = is_type([1,2,3], ["string","list"]);  // Returns: true
is_str_or_list3 = is_type(2, ["string","list"]);  // Returns: false
is_str = is_type("foo", "string");  // Returns: true
is_str2 = is_type([3,4], "string");  // Returns: false
is_str3 = is_type(["foo"], "string");  // Returns: false
is_str4 = is_type(3, "string");  // Returns: false
```

#### x = is_upper(s);

Returns true if all characters in the string are uppercase.

```openscad
// is_upper — Returns true if all characters in the string are uppercase.
// Usage: x = is_upper(s);
```

#### bool = is_valid_region(region, [eps]);

Returns true if the input is a valid {{region}}.

```openscad
region = [square(10), right(11,square(8))];
rainbow(region)stroke($item, width=.2,closed=true);
back(11)text(is_valid_region(region) ? "region" : "non-region", size=2);
```

#### bool = is_vector(v, [length], [zero=], [all_nonzero=], [eps=]);

Returns true if the given value is a vector.

```openscad
is_vector(4);                          // Returns false
is_vector([4,true,false]);             // Returns false
is_vector([3,4,INF,5]);                // Returns false
is_vector([3,4,5,6]);                  // Returns true
is_vector([3,4,undef,5]);              // Returns false
is_vector([3,4,5],3);                  // Returns true
is_vector([3,4,5],4);                  // Returns true
is_vector([]);                         // Returns false
is_vector([0,4,0],3,zero=false);       // Returns true
is_vector([0,0,0],zero=false);         // Returns false
is_vector([0,0,1e-12],zero=false);     // Returns false
is_vector([0,1,0],all_nonzero=false);  // Returns false
is_vector([1,1,1],all_nonzero=false);  // Returns true
is_vector([],zero=false);              // Returns false
```

#### bool = is_vnf(x);

Returns true given a VNF-like structure.

```openscad
// is_vnf — Returns true given a VNF-like structure.
// Usage: bool = is_vnf(x);
```

#### is_vnf_list(...)

Returns true given a list of VNF-like structures.

```openscad
// is_vnf_list — Returns true given a list of VNF-like structures.
// Usage: is_vnf_list(...)
```

#### isosurface(f, isovalue, bounding_box, voxel_size, [voxel_count=], [reverse=], [closed=], [exact_bounds=], [show_stats=], ...) [ATTACHMENTS];

Creates a 3D isosurface (a 3D contour) from a function or array of values.

```openscad
isovalue = [-INF,10];
bbox = [[-11,-11,-11], [0,11,11]];
isosurface(function (x,y,z) norm([x,y,z]),
isovalue, bbox, voxel_size = 1);
```

#### newfunc = ival(func);

Generates a function with signature `(i,x)` that calls `func(i)`

```openscad
x = while(0, ival(f_lt(5)), xval(f_add(1)));
```

#### jittered_poly(path, [dist]);

Creates a polygon with extra points for smoother twisted extrusions.

```openscad
d = 100; h = 75; quadsize = 5;
path = pentagon(d=d);
spath = subdivide_path(path, maxlen=quadsize, closed=true);
linear_extrude(height=h, twist=72, slices=h/quadsize)
jittered_poly(spath);
```

#### join_prism(polygon, base, length=|height=|l=|h=, fillet=, [base_T=], [scale=], [prism_end_T=], [short=], ...) [ATTACHMENTS];

Join an arbitrary prism to a plane, sphere, cylinder or another arbitrary prism with a fillet.

```openscad
join_prism(circle(r=15,$fn=60),base="plane",
length=18, fillet=3, n=12);
cube([50,50,5],anchor=TOP);
```

#### joiner(l, w, base, [ang=], [screwsize=], [$slop=]) [ATTACHMENTS];

Creates a joiner shape that can mate with another rotated joiner shape.

```openscad
joiner(screwsize=3);
joiner(l=40, w=10, base=10);
```

#### joiner_clear(l, w, [ang=], [clearance=], [overlap=]) [ATTACHMENTS];

Creates a mask to clear space for a {{joiner()}} shape.

```openscad
joiner_clear();
```

#### keyhole(l/length=, r1/d1=, r2/d2=, [shoulder_r=], ...) [ATTACHMENTS];

Creates a 2D keyhole shape.

```openscad
keyhole(40, 10, 30);
keyhole(l=60, r1=20, r2=40);
```

#### knuckle_hinge(length, offset, segs, [inner], [arm_height=], [arm_angle=], [fill=], [clear_top=], [gap=], [round_top=], [round_bot=], [knuckle_diam=], [pin_diam=], [pin_fn=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];

Creates a knuckle-hinge shape.

```openscad
$fn=32;
ydistribute(30){
knuckle_hinge(length=35, segs=5, offset=3, arm_height=1);
knuckle_hinge(length=35, segs=5, offset=3, arm_height=1,inner=true);
}
```

#### item = last(list);

Returns the last item of a list.

```openscad
l = [3,4,5,6,7,8,9];
x = last(l);  // Returns 9.
```

#### C = law_of_cosines(a, b, c);

Applies the Law of Cosines for an arbitrary triangle.

```openscad
// law_of_cosines — Applies the Law of Cosines for an arbitrary triangle.
// Usage: C = law_of_cosines(a, b, c);
```

#### B = law_of_sines(a, A, b);

Applies the Law of Sines for an arbitrary triangle.

```openscad
// law_of_sines — Applies the Law of Sines for an arbitrary triangle.
// Usage: B = law_of_sines(a, A, b);
```

#### div = lcm(a, b);

Returns the Least Common Multiple of two or more integers.

```openscad
// lcm — Returns the Least Common Multiple of two or more integers.
// Usage: div = lcm(a, b);
```

#### left(x) CHILDREN;

Translates children leftward (X-).

```openscad
#sphere(d=10);
left(20) sphere(d=10);
```

#### left_half([s], [x]) CHILDREN;

Masks the right half of an object along the Y-Z plane, leaving the left half.

```openscad
left_half() sphere(r=20);
left_half(x=-8) sphere(r=20);
```

#### x = lerp(a, b, u);

Linearly interpolates between two values.

```openscad
x = lerp(0,20,0.3);  // Returns: 6
x = lerp(0,20,0.8);  // Returns: 16
x = lerp(0,20,-0.1); // Returns: -2
x = lerp(0,20,1.1);  // Returns: 22
p = lerp([0,0],[20,10],0.25);  // Returns [5,2.5]
l = lerp(0,20,[0.4,0.6]);  // Returns: [8,12]
l = lerp(0,20,[0.25:0.25:0.75]);  // Returns: [5,10,15]
```

#### x = lerpn(a, b, n);

Returns exactly `n` values, linearly interpolated between `a` and `b`.

```openscad
l = lerpn(-4,4,9);        // Returns: [-4,-3,-2,-1,0,1,2,3,4]
l = lerpn(-4,4,8,false);  // Returns: [-4,-3,-2,-1,0,1,2,3]
l = lerpn(0,1,6);         // Returns: [0, 0.2, 0.4, 0.6, 0.8, 1]
l = lerpn(0,1,5,false);   // Returns: [0, 0.2, 0.4, 0.6, 0.8]
```

#### xyz = lift_plane(plane, p);

Map a list of 2D points onto a plane in 3D.

```openscad
// lift_plane — Map a list of 2D points onto a plane in 3D.
// Usage: xyz = lift_plane(plane, p);
```

#### pt = line_closest_point(line, pt, [bounded]);

Find point on given line, segment or ray that is closest to a given point.

```openscad
line = [[-30,0],[30,30]];
pt = [-32,-10];
p2 = line_closest_point(line,pt);
stroke(line, endcaps="arrow2");
color("blue") translate(pt) circle(r=1,$fn=12);
color("red") translate(p2) circle(r=1,$fn=12);
```

#### line_copies(spacing, [n], [p1=]) CHILDREN;

Places copies of children along an arbitrary line.

```openscad
line_copies(10) sphere(d=1.5);
line_copies(10, n=5) sphere(d=3);
line_copies([10,5], n=5) sphere(d=3);
line_copies(spacing=10, n=6) sphere(d=3);
line_copies(spacing=[10,5], n=6) sphere(d=3);
line_copies(spacing=10, l=50) sphere(d=3);
line_copies(spacing=10, l=[50,30]) sphere(d=3);
line_copies(spacing=[10,5], l=50) sphere(d=3);
line_copies(l=50, n=4) sphere(d=3);
line_copies(l=[50,-30], n=4) sphere(d=3);
```

#### line = line_from_points(points, [check_collinear], [eps]);

Given a list of collinear points, return the line they define.

```openscad
points = rot(45, v=[-0.5,1,0],
p=random_points(100,3,scale=[5,5,50],seed=47));
line = line_from_points(points);
stroke(line, color="#06f");
%move_copies(points) sphere(d=2, $fn=12);
```

#### pt = line_intersection(line1, line2, [bounded1], [bounded2], [bounded=], [eps=]);

Compute intersection of two lines, segments or rays.

```openscad
line1 = 10*[[9, 4], [5, 7]];
line2 = 10*[[2, 3], [6, 5]];
stroke(line1, endcaps="arrow2");
stroke(line2, endcaps="arrow2");
isect = line_intersection(line1, line2);
color("red") translate(isect) circle(r=1,$fn=12);
```

#### vec = line_normal([P1,P2])

Return normal vector to given line.

```openscad
p1 = [10,10];
p2 = [50,30];
n = line_normal(p1,p2);
stroke([p1,p2], endcap2="arrow2");
color("green") stroke([p1,p1+10*n], endcap2="arrow2");
color("blue") move_copies([p1,p2]) circle(d=2, $fn=12);
```

#### linear_bearing(l, od, id, length) [ATTACHMENTS];

Creates a generic linear bearing cartridge.

```openscad
linear_bearing(l=24, od=15, id=8);
```

#### linear_bearing_housing(d, l, tab, gap, wall, tabwall, screwsize) [ATTACHMENTS];

Creates a generic linear bearing mount clamp.

```openscad
linear_bearing_housing(d=19, l=29, wall=2, tab=8, screwsize=2.5);
```

#### solv = linear_solve(A,b,[pivot])

Solve Ax=b or, for overdetermined case, solve the least square problem.

```openscad
// linear_solve — Solve Ax=b or, for overdetermined case, solve the least square problem.
// Usage: solv = linear_solve(A,b,[pivot])
```

#### x = linear_solve3(A,b)

Fast solution to Ax=b where A is 3x3.

```openscad
// linear_solve3 — Fast solution to Ax=b where A is 3x3.
// Usage: x = linear_solve3(A,b)
```

#### linear_sweep(region, [height], [center=], [slices=], [twist=], [scale=], [style=], [caps=], [convexity=]) [ATTACHMENTS];

Create a linear extrusion from a path, with optional texturing.

```openscad
rgn1 = [for (d=[10:10:60]) circle(d=d,$fn=8)];
rgn2 = [square(30,center=false)];
rgn3 = [for (size=[10:10:20]) move([15,15],p=square(size=size, center=true))];
mrgn = union(rgn1,rgn2);
orgn = difference(mrgn,rgn3);
linear_sweep(orgn,height=20,convexity=16);
```

#### list = list(l)

Expands a range into a full list.

```openscad
l1 = list([3:2:9]);  // Returns: [3,5,7,9]
l2 = list([3,4,5]);  // Returns: [3,4,5]
l3 = list("Foo");    // Returns: ["F","o","o"]
l4 = list(23);       // Returns: [23]
```

#### arr = list_bset(indexset, valuelist, [dflt]);

Returns a list where values are spread to locations indicated by a boolean index list.

```openscad
a = list_bset([false,true,false,true,false], [3,4]);  // Returns: [0,3,0,4,0]
b = list_bset([false,true,false,true,false], [3,4], dflt=1);  // Returns: [1,3,1,4,1]
```

#### list = list_head(list, [to]);

Returns the elements at the beginning of a list.

```openscad
hlist1 = list_head(["foo", "bar", "baz"]);  // Returns: ["foo", "bar"]
hlist2 = list_head(["foo", "bar", "baz"], -3); // Returns: ["foo"]
hlist3 = list_head(["foo", "bar", "baz"], 1);  // Returns: ["foo","bar"]
hlist4 = list_head(["foo", "bar", "baz"], -5); // Returns: []
hlist5 = list_head(["foo", "bar", "baz"], 5);  // Returns: ["foo","bar","baz"]
```

#### list = list_insert(list, indices, values);

Inserts values into the middle of a list.

```openscad
a = list_insert([3,6,9,12],1,5);  // Returns [3,5,6,9,12]
b = list_insert([3,6,9,12],[1,3],[5,11]);  // Returns [3,5,6,9,11,12]
```

#### big = list_biggest(list, k)

Returns the `k` largest values in the list, in arbitrary order.

```openscad
// list_largest — Returns the `k` largest values in the list, in arbitrary order.
// Usage: big = list_biggest(list, k)
```

#### newlist = list_pad(list, minlen, [fill]);

Extend list to specified length.

```openscad
list = [3,4,5];
nlist = list_pad(list,5,23);  // Returns: [3,4,5,23,23]
```

#### list = list_remove(list, ind);

Removes items by index from a list.

```openscad
a = list_remove([3,6,9,12],1);      // Returns: [3,9,12]
b = list_remove([3,6,9,12],[1,3]);  // Returns: [3,9]
c = list_remove([3,6],3);           // Returns: [3,6]
```

#### list = list_remove_values(list, values, [all]);

Removes items by value from a list.

```openscad
test = [3,4,[5,6],7,5,[5,6],4,[6,5],7,[4,4]];
a=list_remove_values(test,4); // Returns: [3, [5, 6], 7, 5, [5, 6], 4, [6, 5], 7, [4, 4]]
b=list_remove_values(test,[4,4]); // Returns: [3, [5, 6], 7, 5, [5, 6], [6, 5], 7, [4, 4]]
c=list_remove_values(test,[4,7]); // Returns: [3, [5, 6], 5, [5, 6], 4, [6, 5], 7, [4, 4]]
d=list_remove_values(test,[5,6]); // Returns: [3, 4, [5, 6], 7, [5, 6], 4, [6, 5], 7, [4, 4]]
e=list_remove_values(test,[[5,6]]); // Returns: [3,4,7,5,[5,6],4,[6,5],7,[4,4]]
f=list_remove_values(test,[[5,6]],all=true); // Returns: [3,4,7,5,4,[6,5],7,[4,4]]
animals = ["bat", "cat", "rat", "dog", "bat", "rat"];
animals2 = list_remove_values(animals, "rat");   // Returns: ["bat","cat","dog","bat","rat"]
nonflying = list_remove_values(animals, "bat", all=true);  // Returns: ["cat","rat","dog","rat"]
animals3 = list_remove_values(animals, ["bat","rat"]);  // Returns: ["cat","dog","bat","rat"]
domestic = list_remove_values(animals, ["bat","rat"], all=true);  // Returns: ["cat","dog"]
animals4 = list_remove_values(animals, ["tucan","rat"], all=true);  // Returns: ["bat","cat","dog","bat"]
```

#### rlist = list_rotate(list, [n]);

Rotates the ordering of a list.

```openscad
l1 = list_rotate([1,2,3,4,5],-2); // Returns: [4,5,1,2,3]
l2 = list_rotate([1,2,3,4,5],-1); // Returns: [5,1,2,3,4]
l3 = list_rotate([1,2,3,4,5],0);  // Returns: [1,2,3,4,5]
l4 = list_rotate([1,2,3,4,5],1);  // Returns: [2,3,4,5,1]
l5 = list_rotate([1,2,3,4,5],2);  // Returns: [3,4,5,1,2]
l6 = list_rotate([1,2,3,4,5],3);  // Returns: [4,5,1,2,3]
l7 = list_rotate([1,2,3,4,5],4);  // Returns: [5,1,2,3,4]
l8 = list_rotate([1,2,3,4,5],5);  // Returns: [1,2,3,4,5]
l9 = list_rotate([1,2,3,4,5],6);  // Returns: [2,3,4,5,1]
```

#### list = list_set(list, indices, values, [dflt], [minlen]);

Sets the value of specific list items.

```openscad
a = list_set([2,3,4,5], 2, 21);  // Returns: [2,3,21,5]
b = list_set([2,3,4,5], [1,3], [81,47]);  // Returns: [2,81,4,47]
```

#### dims = list_shape(v, [depth]);

Returns the dimensions of an array.

```openscad
a = list_shape([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]);     // Returns [2,2,3]
b = list_shape([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 0);  // Returns 2
c = list_shape([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 2);  // Returns 3
d = list_shape([[[1,2,3],[4,5,6]],[[7,8,9]]]);                // Returns [2,undef,3]
```

#### small = list_smallest(list, k)

Returns the `k` smallest values in the list, in arbitrary order.

```openscad
// list_smallest — Returns the `k` smallest values in the list, in arbitrary order.
// Usage: small = list_smallest(list, k)
```

#### list = list_swap(list, i, j);

Swaps two items in a list by index

```openscad
a = list_swap([2,3,4,5], 0, 2);  // Returns: [4,3,2,5]
```

#### list = list_tail(list, [from]);

Returns the elements at the end of a list.

```openscad
tlist1 = list_tail(["foo", "bar", "baz"]);  // Returns: ["bar", "baz"]
tlist2 = list_tail(["foo", "bar", "baz"], -1); // Returns: ["baz"]
tlist3 = list_tail(["foo", "bar", "baz"], 2);  // Returns: ["baz"]
tlist4 = list_tail(["foo", "bar", "baz"], -5); // Returns: ["foo","bar","baz"]
tlist5 = list_tail(["foo", "bar", "baz"], 5);  // Returns: []
```

#### groups = list_to_matrix(v, cnt, [dflt]);

Groups items in a list into sublists.

```openscad
v = [1,2,3,4,5,6];
a = list_to_matrix(v,2)  // returns [[1,2], [3,4], [5,6]]
b = list_to_matrix(v,3)  // returns [[1,2,3], [4,5,6]]
c = list_to_matrix(v,4,0)  // returns [[1,2,3,4], [5,6,0,0]]
```

#### list_unwrap(list, [eps]);

Removes the last item of a list if its first and last values are equal.

```openscad
// list_unwrap — Removes the last item of a list if its first and last values are equal.
// Usage: list_unwrap(list, [eps]);
```

#### list_wrap(path, [eps]);

Returns a list whose last value is the same as the first.

```openscad
// list_wrap — Returns a list whose last value is the same as the first.
// Usage: list_wrap(path, [eps]);
```

#### living_hinge_mask(l, thick, [layerheight=], [foldangle=], [hingegap=], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];

Creates a mask to make a folding "living" hinge.

```openscad
living_hinge_mask(l=100, thick=3, foldangle=60);
```

#### lmXuu_bearing(size) [ATTACHMENTS];

Creates a standardized LM*UU linear bearing cartridge.

```openscad
lmXuu_bearing(size=10);
```

#### lmXuu_housing(size, tab, gap, wall, tabwall, screwsize) [ATTACHMENTS];

Creates a standardized LM*UU linear bearing mount clamp.

```openscad
lmXuu_housing(size=10, wall=2, tab=6, screwsize=2.5);
```

#### diam_len = lmXuu_info(size);

Returns the sizes of a standard LM*UU linear bearing cartridge.

```openscad
// lmXuu_info — Returns the sizes of a standard LM*UU linear bearing cartridge.
// Usage: diam_len = lmXuu_info(size);
```

#### val = log2(x);

Returns the log base 2 of the given value.

```openscad
log2(0.125);  // Returns: -3
log2(16);     // Returns: 4
log2(256);    // Returns: 8
```

#### bool = loop_done(state);

Returns true if the argument indicates the current C-style loop is finishing.

```openscad
// loop_done — Returns true if the argument indicates the current C-style loop is finishing.
// Usage: bool = loop_done(state);
```

#### state = loop_while(state, continue);

Returns true if both arguments indicate the current C-style loop should continue.

```openscad
// loop_while — Returns true if both arguments indicate the current C-style loop should continue.
// Usage: state = loop_while(state, continue);
```

#### bool = looping(state);

Returns true if the argument indicates the current C-style loop should continue.

```openscad
// looping — Returns true if the argument indicates the current C-style loop should continue.
// Usage: bool = looping(state);
```

#### lt_u_p = lu_factor(A);

Compute LU factorization of a matrix with pivoting.

```openscad
// lu_factor — Compute LU factorization of a matrix with pivoting.
// Usage: lt_u_p = lu_factor(A);
```

#### region = make_region(polys, [nonzero], [eps]);

Converts lists of intersecting {{polygons}} into valid {{regions}}.

```openscad
pentagram = turtle(["move",100,"left",144], repeat=4);
region = make_region(pentagram);
rainbow(region)stroke($item, width=1,closed=true);
```

#### manfrotto_rc2_plate([chamfer],[anchor],[orient],[spin]) [ATTACHMENTS];

Creates a Manfrotto RC2 tripod quick release mount plate.

```openscad
manfrotto_rc2_plate();
manfrotto_rc2_plate("bot");
```

#### lst = map(func, list);

Applies a function to each item in a list.

```openscad
func = function(x) x*x;
echo(map(func, [1,2,3,4]));
// ECHO: [1,4,9,16]
```

#### mask2d_chamfer(edge, [angle], [inset], [excess]) [ATTACHMENTS];

Produces a 2D chamfer mask shape.

```openscad
mask2d_chamfer(x=10);
```

#### mask2d_cove(r|d=|h=|height=, [inset], [mask_angle], [excess], [bulge=], [flat_top=], [quarter_round=]) [ATTACHMENTS];

Creates a 2D cove (quarter-round) mask shape.

```openscad
mask2d_cove(r=10);
```

#### mask2d_dovetail(edge, angle, [inset], [shelf], [excess], ...) [ATTACHMENTS];

Creates a 2D dovetail mask shape.

```openscad
mask2d_dovetail(width=10,angle=14);
```

#### mask2d_ogee(pattern, [excess], ...) [ATTACHMENTS];

Creates a 2D ogee mask shape.

```openscad
mask2d_ogee([
"xstep",1,  "ystep",1,  // Starting shoulder.
"fillet",5, "round",5,  // S-curve.
"ystep",1,  "xstep",1   // Ending shoulder.
]);
```

#### mask2d_rabbet(size, [mask_angle], [excess]) [ATTACHMENTS];

Creates a rabbet mask shape.

```openscad
mask2d_rabbet(size=10);
```

#### mask2d_roundover(r|d=|h=|height=|cut=|joint=, [inset], [mask_angle], [excess], [flat_top=], [quarter_round=], [clip_angle=]) [ATTACHMENTS];

Creates a circular mask shape for rounding edges or beading.

```openscad
mask2d_roundover(r=10);
```

#### mask2d_smooth([mask_angle], [cut=], [joint=], [inset=], [excess=], [flat_top=], [anchor=], [spin=]) [ATTACHMENTS];

Creates a continuous curvature mask for rounding edges.

```openscad
mask2d_smooth(cut=3);
```

#### mask2d_teardrop(r|d=, [angle], [inset] [mask_angle], [excess], [cut=], [joint=], [h=|height=]) [ATTACHMENTS];

Creates a 2D teardrop shape with specified max angle from vertical.

```openscad
mask2d_teardrop(r=10,$fn=64);
```

#### mat = matrix_inverse(A)

General matrix inverse.

```openscad
// matrix_inverse — General matrix inverse.
// Usage: mat = matrix_inverse(A)
```

#### matrix_trace(M)

Compute the trace of a square matrix.

```openscad
// matrix_trace — Compute the trace of a square matrix.
// Usage: matrix_trace(M)
```

#### idx = max_index(vals);

Returns the index of the maximum value in the given list.

```openscad
max_index([5,3,9,6,2,7,8,9,1]); // Returns: 2
max_index([5,3,9,6,2,7,8,9,1],all=true); // Returns: [2,7]
```

#### llen = max_length(list);

Given a list of sublists, returns the length of the longest sublist.

```openscad
llen = max_length([[3,4,5],[6,7,8,9]]);  // Returns: 4
```

#### x = mean(v);

Returns the mean value of a list of values.

```openscad
mean([2,3,4]);  // returns 3.
mean([[1,2,3], [3,4,5], [5,6,7]]);  // returns [3, 4, 5]
```

#### half_ang = mean_angle(angle1,angle2);

Returns the mean angle of two angles

```openscad
// mean_angle — Returns the mean angle of two angles
// Usage: half_ang = mean_angle(angle1,angle2);
```

#### middle = median(v)

Returns the median value of a list of values.

```openscad
// median — Returns the median value of a list of values.
// Usage: middle = median(v)
```

#### metaballs(spec, bounding_box, voxel_size, [isovalue=], [closed=], [exact_bounds=], [convexity=], [show_stats=], [show_box=], [debug=] ...) [ATTACHMENTS];

Creates a group of 3D metaballs (smoothly connected blobs).

```openscad
spec = [
left(9), mb_sphere(5),
right(9), mb_sphere(5)
];
metaballs(spec, voxel_size=0.5,
bounding_box=[[-16,-7,-7], [16,7,7]]);
```

#### metaballs2d(spec, bounding_box, pixel_size, [isovalue=], [use_centers=], [smoothing=], [exact_bounds=], [show_stats=], [show_box=], [debug=] ...) [ATTACHMENTS];

Creates a group of 2D metaballs (smoothly connected blobs).

```openscad
spec = [
left(9), mb_circle(5),
right(9), mb_circle(5)
];
metaballs2d(spec, pixel_size=1,
bounding_box=[[-16,-7], [16,7]]);
```

#### metric_bolt(...)



```openscad
ydistribute(40) {
xdistribute(30) {
// Front Row, Left to Right
metric_bolt(headtype="pan", size=10, l=15, details=true, phillips="#2");
metric_bolt(headtype="button", size=10, l=15, details=true, phillips="#2");
metric_bolt(headtype="round", size=10, l=15, details=true, phillips="#2");
}
xdistribute(30) {
// Back Row, Left to Right
metric_bolt(headtype="socket", size=10, l=15, details=true);
metric_bolt(headtype="hex", size=10, l=15, details=true, phillips="#2");
metric_bolt(headtype="countersunk", size=10, l=15, details=true, phillips="#2");
metric_bolt(headtype="oval", size=10, l=15, details=true, phillips="#2");
}
}
```

#### metric_nut(...)

Creates a standard metric nut model.

```openscad
metric_nut(size=10, hole=false);
```

#### idx = min_index(vals);

Returns the index of the minimum value in the given list.

```openscad
a = min_index([5,3,9,6,2,7,8,2,1]); // Returns: 8
b = min_index([5,3,9,6,2,7,8,2,7],all=true); // Returns: [4,7]
```

#### llen = min_length(list);

Given a list of sublists, returns the length of the shortest sublist.

```openscad
slen = min_length([[3,4,5],[6,7,8,9]]);  // Returns: 3
```

#### minkowski_difference() { BASE; DIFF1; DIFF2; ... }

Removes diff shapes from base shape surface.

```openscad
minkowski_difference() {
union() {
cube([120,70,70], center=true);
cube([70,120,70], center=true);
cube([70,70,120], center=true);
}
sphere(r=10);
}
```

#### mirror(v) CHILDREN;

Reflects children across an arbitrary plane.

```openscad
n = [1,0,0];
module obj() right(20) rotate([0,15,-15]) cube([40,30,20]);
obj();
mirror(n) obj();
rot(a=atan2(n.y,n.x),from=UP,to=n) {
color("red") anchor_arrow(s=20, flag=false);
color("#7777") cube([75,75,0.1], center=true);
}
```

#### mirror_copy(v, [cp], [offset]) CHILDREN;

Makes a copy of children mirrored across a given plane.

```openscad
mirror_copy([1,-1,0]) zrot(-45) yrot(90) cylinder(d1=10, d2=0, h=20);
color("blue",0.25) zrot(-45) cube([0.01,15,15], center=true);
```

#### ang = modang(x);

Returns an angle normalized to between -180º and 180º.

```openscad
a1 = modang(-700);  // Returns: 20
a2 = modang(-270);  // Returns: 90
a3 = modang(-120);  // Returns: -120
a4 = modang(120);   // Returns: 120
a5 = modang(270);   // Returns: -90
a6 = modang(700);   // Returns: -20
```

#### modular_hose(size, type, [clearance], [waist_len], [anchor], [spin], [orient]) [ATTACHMENTS];

Creates modular hose parts.

```openscad
modular_hose(1/4,"segment");
right(25)modular_hose(1/2,"segment");
right(60)modular_hose(3/4,"segment");
```

#### r = modular_hose_radius(size, [outer]);

Returns the waist radius of the given modular hose size.

```openscad
$fn=64;
back_half()
diff("remove")
cuboid(50){
attach(TOP) modular_hose(1/2, "ball");
up(0.01)position(TOP+RIGHT)tag("remove")
rot(180)
xrot(-90)
rotate_extrude(angle=135)
right(25)
circle(r=modular_hose_radius(1/2));
}
```

#### mod = module_value(circ_pitch);

Returns tooth density expressed as "module"

```openscad
mod1 = module_value(circ_pitch=8);
mod2 = module_value(mod=2);
mod3 = module_value(diam_pitch=16);
```

#### move(v) CHILDREN;

Translates children in an arbitrary direction.

```openscad
#sphere(d=10);
move([0,20,30]) sphere(d=10);
```

#### move_copies(a) CHILDREN;

Translates copies of all children.

```openscad
#sphere(r=10);
move_copies([[-25,-25,0], [25,-25,0], [0,0,50], [0,25,0]]) sphere(r=10);
```

#### a = named_anchor(name, pos, [orient], [spin]);

Creates an anchor data structure.

```openscad
// named_anchor — Creates an anchor data structure.
// Usage: a = named_anchor(name, pos, [orient], [spin]);
```

#### narrowing_strut(w, l, wall, [ang=]) [ATTACHMENTS];

Makes a strut like an extruded baseball home plate.

```openscad
narrowing_strut(w=10, l=100, wall=5, ang=30);
```

#### info = nema_motor_info(size);

Returns dimension info for a given NEMA motor size.

```openscad
// nema_motor_info — Returns dimension info for a given NEMA motor size.
// Usage: info = nema_motor_info(size);
```

#### nema_mount_mask(size, depth, l, [$slop], ...);

Creates a standard NEMA mount holes mask.

```openscad
nema_mount_mask(size=14, depth=5, l=5);
nema_mount_mask(size=17, depth=5, l=5);
nema_mount_mask(size=17, depth=5, l=0);
```

#### nema_stepper_motor(size, h, shaft_len, [$slop=], ...) [ATTACHMENTS];

Creates a NEMA standard stepper motor model.

```openscad
nema_stepper_motor(size=8, h=24, shaft_len=15);
nema_stepper_motor(size=11, h=24, shaft_len=20);
nema_stepper_motor(size=17, h=40, shaft_len=30);
nema_stepper_motor(size=23, h=50, shaft_len=40);
nema_stepper_motor(size=23, h=50, shaft_len=40, details=false);
```

#### no_children($children);

Assert that the calling module does not support children.

```openscad
module foo() {
no_children($children);
}
```

#### dummy = no_function(name)

Assert that the argument exists only as a module and not as a function.

```openscad
x = no_function("foo");
```

#### no_module();

Assert that the argument exists only as a function and not as a module.

```openscad
module foo() { no_module(); }
```

#### norm_fro(A)

Compute Frobenius norm of a matrix

```openscad
// norm_fro — Compute Frobenius norm of a matrix
// Usage: norm_fro(A)
```

#### npt_threaded_rod(size, [internal=], ...) [ATTACHMENTS];

Creates NPT pipe threading.

```openscad
projection(cut=true) npt_threaded_rod(size=1/4, orient=BACK);
right(.533*INCH/2) color("gray") rect([2,0.5946*INCH],anchor=LEFT);
```

#### x = null_space(A)

Return basis for the null space of A.

```openscad
// null_space — Return basis for the null space of A.
// Usage: x = null_space(A)
```

#### cnt = num_defined(v);

Returns the number of defined values in the the argument list.

```openscad
cnt = num_defined([3,7,undef,2,undef,undef,1]);  // Returns: 4
```

#### seq = num_true(l);

Returns the number of true entries in the arguemnt list.

```openscad
num1 = num_true([0,false,undef]);  // Returns 0.
num2 = num_true([1,false,undef]);  // Returns 1.
num3 = num_true([1,5,false]);      // Returns 2.
num4 = num_true([1,5,true]);       // Returns 3.
num5 = num_true([[0,0], [0,0]]);   // Returns 2.
num6 = num_true([[], [1,0]]);      // Returns 1.
```

#### pts = nurbs_curve(control, degree, splinesteps, [mult=], [weights=], [type=], [knots=]);

Computes one or more points on a NURBS curve.

```openscad
control = [[5,0],[0,20],[33,43],[37,88],[60,62],[44,22],[77,44],[79,22],[44,3],[22,7]];
curve = nurbs_curve(control,2,splinesteps=16);
pts = nurbs_curve(control,2,u=[0.4,0.8]);
stroke(curve);
color("red")move_copies(pts) circle(r=1.5,$fn=16);
```

#### result = nurbs_elevate_degree(control, degree, [knots=], [mult=], [type=], [times=], [weights=]);

Raises the degree of a closed or open NURBS.

```openscad
// nurbs_elevate_degree — Raises the degree of a closed or open NURBS.
// Usage: result = nurbs_elevate_degree(control, degree, [knots=], [mult=], [type=], [times=], [weights=]);
```

#### nurbs_param = nurbs_interp(points, degree, [method=], [closed=], [start_deriv=], [end_deriv=], [curvature=], [start_curvature=], [end_curvature=], [corners=], [deriv=], [extra_pts=], [smooth=]);

Finds a NURBS curve passing through a point list with optional derivative constraints.

```openscad
data = [[0,0], [10,30], [25,15], [40,35], [60,10], [80,25]];
path = nurbs_curve(nurbs_interp(data, 3));
stroke(path);
```

#### nurbs_param = nurbs_interp_surface(points, degree, [method=], [row_wrap=], [col_wrap=], [normal1=], [normal2=], [flat_edges=], [flat_end1=], [flat_end2=], [row_edges=], [col_edges=], [extra_pts=], [smooth=], [first_row_deriv=], [last_row_deriv=], [first_col_deriv=], [last_col_deriv=]);

Returns a NURBS surface that passes through a grid of 3D data points.

```openscad
// A 4x5 grid of 3D data points produces a smooth interpolating surface.
data = [
[[-50, 50,  0], [-16, 50,  20], [ 16, 50,  10], [50, 50,  0], [80, 50,  5]],
[[-50, 16, 20], [-16, 16,  40], [ 16, 16,  30], [50, 16, 20], [80, 16, 10]],
[[-50,-16, 20], [-16,-16,  35], [ 16,-16,  40], [50,-16, 15], [80,-16, 25]],
[[-50,-50,  0], [-16,-50,  10], [ 16,-50,  20], [50,-50,  0], [80,-50,  5]],
];
nurbs_interp_surface(data, 3, splinesteps=8);
```

#### pointgrid = nurbs_patch_points(patch, degree, [splinesteps], [u=], [v=], [weights=], [type=], [mult=], [knots=]);

Computes specified point(s) on a NURBS surface patch

```openscad
patch = [
[[-50, 50,  0], [-16, 50,  20], [ 16, 50,  20], [50, 50,  0]],
[[-50, 16, 20], [-16, 16,  40], [ 16, 16,  40], [50, 16, 20]],
[[-50,-16, 20], [-16,-16,  40], [ 16,-16,  40], [50,-16, 20]],
[[-50,-50,  0], [-16,-50,  20], [ 16,-50,  20], [50,-50,  0]],
];
pts = nurbs_patch_points(patch, 3, u=[0:.1:1], v=[0:.3:1]);
move_copies(flatten(pts)) sphere(r=2,$fn=16);
```

#### vnf = nurbs_vnf(patch, degree, [splinesteps], [mult=], [knots=], [weights=], [type=], [style=], [reverse=], [triangulate=], [caps=], [caps1=], [caps2=]);

Generates a (possibly non-manifold) VNF for a single NURBS surface patch.

```openscad
patch = [
[[-50, 50,  0], [-16, 50,  20], [ 16, 50,  20], [50, 50,  0]],
[[-50, 16, 20], [-16, 16,  40], [ 16, 16,  40], [50, 16, 20]],
[[-50,-16, 20], [-16,-16,  40], [ 16,-16,  40], [50,-16, 20]],
[[-50,-50,  0], [-16,-50,  20], [ 16,-50,  20], [50,-50,  0]],
];
vnf = nurbs_vnf(patch, 2);
vnf_polyhedron(vnf);
```

#### nut([spec], [shape], [thickness], [nutwidth], [thread=], [tolerance=], [hole_oversize=], [bevel=], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];

Creates a standard nut.

```openscad
xdistribute(spacing=0.75*INCH){
nut("3/8",thickness="thin");
nut("3/8",thickness="normal");
nut("3/8",thickness="thick");
nut("3/8",shape="square");
}
```

#### nut_spec = nut_info(name, [shape], [thickness=], [thread=], [width=], [hole_oversize=]);

Returns the dimensions and other info for the given nut.

```openscad
// nut_info — Returns the dimensions and other info for the given nut.
// Usage: nut_spec = nut_info(name, [shape], [thickness=], [thread=], [width=], [hole_oversize=]);
```

#### nut_trap_inline(length|l|heigth|h, [spec], [shape], [$slop=], [anchor=], [orient=], [spin=]) [ATTACHMENTS];

Creates an inline nut trap mask.

```openscad
nut_trap_inline(10, "#8");
```

#### nut_trap_side(trap_width, [spec], [shape], [thickness], [nutwidth=], [poke_len=], [poke_diam=], [$slop=], [anchor=], [orient=], [spin=]) [ATTACHMENTS];

Creates a side nut trap mask.

```openscad
nut_trap_side(10, "#8");
```

#### octagon(r/or, [realign=], [align_tip=|align_side=], [rounding=], ...) [ATTACHMENTS];

Creates a regular octagon.

```openscad
octagon(or=30);
octagon(od=60);
```

#### octahedron(size, ...) [ATTACHMENTS];

Creates an octahedron with axis-aligned points.

```openscad
octahedron(size=40);
```

#### offsetpath = offset(path, [r=|delta=], [chamfer=], [closed=], [check_valid=], [quality=], [error=], [same_length=])

Takes a 2D {{path}}, {{polygon}} or {{region}} and returns a path offset by an amount.

```openscad
star = star(5, r=100, ir=30);
stroke(closed=true, star, width=3, color="red");
stroke(closed=true, width=3,
offset(star, delta=10, closed=true));
```

#### offset_stroke(path, [width], [rounded=], [chamfer=], [start=], [end=], [check_valid=], [quality=], [closed=],...) [ATTACHMENTS];

Draws a line along a path with options to specify angles and roundings at the ends.

```openscad
arc = arc(points=[[1,1],[3,4],[6,3]],n=50);
path = [[0,0],[6,2],[9,7],[8,10]];
xdistribute(spacing=10){
offset_stroke(path, width = 2);
offset_stroke(path, start="round", end="round", width = 2, $fn=32);
offset_stroke(path, start="pointed", end="pointed", width = 2);
}
fwd(10) xdistribute(spacing=10){
offset_stroke(arc, width = 2);
offset_stroke(arc, start="round", end="round", width = 2, $fn=32);
offset_stroke(arc, start="pointed", end="pointed", width = 2);
}
```

#### offset_sweep(path, [height|length=|h=|l=], [bottom], [top], [offset=], [convexity=],...) [ATTACHMENTS];

Make a solid from a polygon with offset that changes along its length.

```openscad
star = star(5, r=22, ir=13);
rounded_star = round_corners(star, cut=flatten(repeat([.5,0],5)), $fn=24);
offset_sweep(rounded_star, height=20, bottom=os_circle(r=4), top=os_circle(r=1), steps=15);
```

#### offset3d(r, [size], [convexity]) CHILDREN;

Expands or contracts the surface of a 3D object.

```openscad
// offset3d — Expands or contracts the surface of a 3D object.
// Usage: offset3d(r, [size], [convexity]) CHILDREN;
```

#### val = one_defined(vals, names, [dflt])

Returns the defined value in the argument list if only a single value is defined.

```openscad
length1 = one_defined([length,L,l], ["length","L","l"]);
length2 = one_defined([length,L,l], "length,L,l", dflt=1);
```

#### onion(r|d=, [ang=], [cap_h=], [circum=], [realign=], ...) [ATTACHMENTS];

Creates an attachable onion-like shape.

```openscad
onion(r=30, ang=30);
```

#### ang = opp_adj_to_ang(opp,adj);

Returns the reference reference angle from the lengths of the opposite and adjacent sides.

```openscad
ang = opp_adj_to_ang(0.5,sqrt(3)/2);  // Returns: 30 degrees
```

#### hyp = opp_adj_to_hyp(opp,adj);

Returns the hypotenuse length from the lengths of the opposite and adjacent sides.

```openscad
hyp = opp_adj_to_hyp(4,3);  // Returns: 5
```

#### adj = opp_ang_to_adj(opp,ang);

Returns the adjacent side length from the length of the opposite side and the reference reference angle.

```openscad
adj = opp_ang_to_adj(8,45);  // Returns: 8
```

#### hyp = opp_ang_to_hyp(opp,ang);

Returns the hypotenuse length from the length of the opposite side and the reference reference angle.

```openscad
hyp = opp_ang_to_hyp(4,30);  // Returns: 8
```

#### adj = opp_hyp_to_adj(opp,hyp);

Returns the adjacent side length from the lengths of the opposite side and the hypotenuse.

```openscad
adj = opp_hyp_to_adj(3,5);  // Returns: 4
```

#### ang = opp_hyp_to_ang(opp,hyp);

Returns the reference reference angle from the lengths of the opposite side and the hypotenuse.

```openscad
ang = opp_hyp_to_ang(4,8);  // Returns: 30 degrees
```

#### PARENT() orient(anchor, [spin]) CHILDREN;

Orients children's tops in the directon of the specified anchor.

```openscad
prismoid([50,50],[30,30],h=40) {
position(TOP+RIGHT)
orient(RIGHT)
prismoid([30,30],[0,5],h=20,anchor=BOT+LEFT);
}
```

#### x = outer_product(u,v);

Compute the outer product of two vectors.

```openscad
// outer_product — Compute the outer product of two vectors.
// Usage: x = outer_product(u,v);
```

#### or = outer_radius(circ_pitch, teeth, [helical=], [clearance=], [internal=], [profile_shift=], [shorten=]);

Returns the outer radius for a gear.

```openscad
or = outer_radius(circ_pitch=5, teeth=20);
or = outer_radius(circ_pitch=5, teeth=20, helical=30);
or = outer_radius(diam_pitch=10, teeth=17);
or = outer_radius(mod=2, teeth=16);
```

#### p = pair(list, [wrap]);

Returns a list of overlapping consecutive pairs in a list.

```openscad
for (p = pair(circle(d=40, $fn=12)))
stroke(p, endcap2="arrow2");
```

#### PARENT() let( desc = parent() ) CHILDREN;

Returns a description (transformation state and attachment geometry) of the parent

```openscad
// parent — Returns a description (transformation state and attachment geometry) of the parent
// Usage: PARENT() let( desc = parent() ) CHILDREN;
```

#### PARENT() let( desc = parent_part(name, [ind]) ) CHILDREN;

Returns a description (transformation state and attachment geometry) of a part defined by the parent

```openscad
$fn=48;
module twocyl(d, sep, h, ang=20) 
{
parts = [
define_part("left", attach_geom(r=d/2,h=h),
T=left(sep/2)*yrot(-ang)),
define_part("right", attach_geom(r=d/2,h=h),
T=right(sep/2)*yrot(ang)),
];
attachable(size=[sep+d,d,h], parts=parts){
union(){
left(sep/2) yrot(-ang) cyl(d=d,h=h);
right(sep/2) yrot(ang) cyl(d=d,h=h);
}
children();
}  
}
twocyl(d=10,sep=20,h=10) 
prism_connector(circle(r=2,$fn=32),
parent_part("left"), RIGHT,
parent_part("right"), LEFT,
fillet=1);
```

#### num = parse_float(str);

Parse a float from a string.

```openscad
parse_float("44");       // Returns 44
parse_float("3.4");      // Returns 3.4
parse_float("-99.3332"); // Returns -99.3332
parse_float("3.483e2");  // Returns 348.3
parse_float("-44.9E2");  // Returns -4490
parse_float("7.342e-4"); // Returns 0.0007342
parse_float("");         // Returns 0
```

#### num = parse_frac(str,[mixed=],[improper=],[signed=]);

Parse a float from a fraction string.

```openscad
parse_frac("3/4");     // Returns 0.75
parse_frac("-77/9");   // Returns -8.55556
parse_frac("+1/3");    // Returns 0.33333
parse_frac("19");      // Returns 19
parse_frac("2 3/4");   // Returns 2.75
parse_frac("-2 12/4"); // Returns -5
parse_frac("");        // Returns 0
parse_frac("3/0");     // Returns inf
parse_frac("0/0");     // Returns nan
parse_frac("-77/9",improper=false);   // Returns nan
parse_frac("-2 12/4",improper=false); // Returns nan
parse_frac("-2 12/4",signed=false);   // Returns nan
parse_frac("-2 12/4",mixed=false);    // Returns nan
parse_frac("2 1/4",mixed=false);      // Returns nan
```

#### num = parse_int(str, [base])

Parse an integer from a string.

```openscad
parse_int("349");        // Returns 349
parse_int("-37");        // Returns -37
parse_int("+97");        // Returns 97
parse_int("43.9");       // Returns nan
parse_int("1011010",2);  // Returns 90
parse_int("13",2);       // Returns nan
parse_int("dead",16);    // Returns 57005
parse_int("CEDE", 16);   // Returns 52958
parse_int("");           // Returns 0
```

#### num = parse_num(str);

Parse a float from a decimal or fraction string.

```openscad
parse_num("3/4");    // Returns 0.75
parse_num("3.4e-2"); // Returns 0.034
```

#### partition(size, [spread], [cutsize], [cutpath], [gap], [spin], [$slop=]) CHILDREN;

Cuts an object in two with matched joining edges, then separates the parts.

```openscad
partition(spread=12, cutpath="dovetail") cylinder(h=50, d=80, center=false);
partition(spread=12, gap=10, cutpath="dovetail") cylinder(h=50, d=80, center=false);
partition(spread=12, gap=10, cutpath="dovetail", cutpath_centered=false) cylinder(h=50, d=80, center=false);
partition(spread=20, gap=10, cutsize=15, cutpath="dovetail") cylinder(h=50, d=80, center=false);
partition(spread=25, gap=10, cutsize=[20,20], cutpath="dovetail") cylinder(h=50, d=80, center=false);
```

#### partition_cut_mask(l, [cutsize], [cutpath], [gap], [inverse], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];

Creates a mask to cut an object into two subparts that can be reassembled.

```openscad
partition_cut_mask(gap=0, cutpath="dovetail");
partition_cut_mask(gap=10, cutpath="dovetail");
partition_cut_mask(gap=10, cutsize=15, cutpath="dovetail");
partition_cut_mask(gap=10, cutsize=[15,15], cutpath="dovetail");
partition_cut_mask(gap=10, cutsize=[15,15], cutpath="dovetail", cutpath_centered=false);
```

#### partition_mask(l, w, h, [cutsize], [cutpath], [gap], [inverse], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];

Creates a mask to remove half an object with the remaining half suitable for reassembly.

```openscad
partition_mask(w=50, gap=0, cutpath="jigsaw", $fn=12);
partition_mask(w=50, gap=10, cutpath="jigsaw", $fn=12);
partition_mask(w=50, gap=10, cutpath="jigsaw", inverse=true, $fn=12);
partition_mask(w=50, gap=10, cutsize=4, cutpath="jigsaw", $fn=12);
partition_mask(w=50, gap=10, cutsize=4, cutpath="jigsaw", cutpath_centered=false, $fn=12);
partition_mask(w=50, gap=10, cutsize=[4,20], cutpath="jigsaw", $fn=12);
```

#### path = partition_path(pathdesc, [repeat=], [y=], [altpath=], [seglen=], [segwidth=]);

Creates a partition path from a path description.

```openscad
stroke(partition_path(["flat"]), width=3);
stroke(partition_path(["sawtooth"]), width=3);
stroke(partition_path(["square"]), width=3);
stroke(partition_path(["triangle"]), width=3);
stroke(partition_path(["halfsine"], $fn=24), width=3);
stroke(partition_path(["semicircle"], $fn=24), width=3);
stroke(partition_path(["comb"]), width=3);
stroke(partition_path(["finger"]), width=3);
stroke(partition_path(["dovetail"]), width=3);
stroke(partition_path(["hammerhead"]), width=3);
stroke(partition_path(["jigsaw"], $fn=24), width=3);
```

#### index_pt = path_closest_point(path, pt);

Returns the closest place on a {{path}} to a given {{point}}.

```openscad
path = circle(d=100,$fn=6);
pt = [20,10];
closest = path_closest_point(path, pt);
stroke(path, closed=true);
color("blue") translate(pt) circle(d=3, $fn=12);
color("red") translate(closest[1]) circle(d=3, $fn=12);
```

#### path_copies(path, [n], [spacing], [sp], [rotate_children], [closed=]) CHILDREN;

Uniformly distributes copies of children along a path.

```openscad
spiral = [for(theta=[0:360*8]) theta * [cos(theta), sin(theta)]]/100;
stroke(spiral,width=.25);
color("red") path_copies(spiral, n=100) circle(r=1);
```

#### curvs = path_curvature(path, [closed]);

Returns the estimated numerical curvature of the {{path}}.

```openscad
// path_curvature — Returns the estimated numerical curvature of the {{path}}.
// Usage: curvs = path_curvature(path, [closed]);
```

#### path_list = path_cut(path, cutdist, [closed]);

Cuts a {{path}} into subpaths at various {{points}}.

```openscad
path = circle(d=100);
segs = path_cut(path, [50, 200], closed=true);
rainbow(segs) stroke($item, endcaps="butt", width=3);
```

#### cuts = path_cut_points(path, cutdist, [closed=], [direction=]);

Returns a list of cut {{points}} at a list of distances from the first point in a {{path}}.

```openscad
square=[[0,0],[1,0],[1,1],[0,1]];
path_cut_points(square, [.5,1.5,2.5]);   // Returns [[[0.5, 0], 1], [[1, 0.5], 2], [[0.5, 1], 3]]
path_cut_points(square, [0,1,2,3]);      // Returns [[[0, 0], 1], [[1, 0], 2], [[1, 1], 3], [[0, 1], 4]]
path_cut_points(square, [0,0.8,1.6,2.4,3.2], closed=true);  // Returns [[[0, 0], 1], [[0.8, 0], 1], [[1, 0.6], 2], [[0.6, 1], 3], [[0, 0.8], 4]]
path_cut_points(square, [0,0.8,1.6,2.4,3.2]);               // Returns [[[0, 0], 1], [[0.8, 0], 1], [[1, 0.6], 2], [[0.6, 1], 3], undef]
```

#### path_extrude(path, [convexity], [clipsize]) 2D-CHILDREN;

Extrudes 2D children along a 3D path.

```openscad
path = [ [0, 0, 0], [33, 33, 33], [66, 33, 40], [100, 0, 0], [150,0,0] ];
path_extrude(path) circle(r=10, $fn=6);
```

#### path_extrude2d(path, [caps=], [closed=], [s=], [convexity=]) 2D-CHILDREN;

Extrudes 2D children along a 2D path.

```openscad
path = [
each right(50, p=arc(d=100,angle=[90,180])),
each left(50, p=arc(d=100,angle=[0,-90])),
];
path_extrude2d(path,caps=false) {
fwd(2.5) square([5,6],center=true);
fwd(6) square([10,5],center=true);
}
```

#### joined_path = path_join(paths, [joint], [k=], [relocate=], [closed=]);

Join paths end to end with optional rounding.

```openscad
horiz = [[0,0],[10,0]];
vert = [[0,0],[0,10]];
stroke(path_join([horiz, vert, -horiz]));
```

#### path_length(path,[closed])

Returns the path length.

```openscad
path = [[0,0], [5,35], [60,-25], [80,0]];
echo(path_length(path));
```

#### fracs = path_length_fractions(path, [closed]);

Returns the fractional distance of each point along the length of a path.

```openscad
// path_length_fractions — Returns the fractional distance of each point along the length of a path.
// Usage: fracs = path_length_fractions(path, [closed]);
```

#### newpath = path_merge_collinear(path, [eps]);

Removes unnecessary collinear points from a path.

```openscad
// path_merge_collinear — Removes unnecessary collinear points from a path.
// Usage: newpath = path_merge_collinear(path, [eps]);
```

#### ind = path_merge_collinear(path, [eps]);

Removes unnecessary collinear points from a path specified as an index list into a list

```openscad
// path_merge_collinear_indexed — Removes unnecessary collinear points from a path specified as an index list into a list
// Usage: ind = path_merge_collinear(path, [eps]);
```

#### norms = path_normals(path, [tangents], [closed]);

Returns normal vectors for each point along a path.

```openscad
// path_normals — Returns normal vectors for each point along a path.
// Usage: norms = path_normals(path, [tangents], [closed]);
```

#### path_segment_lengths(path,[closed])

Returns a list of the lengths of segments in a {{path}}.

```openscad
// path_segment_lengths — Returns a list of the lengths of segments in a {{path}}.
// Usage: path_segment_lengths(path,[closed])
```

#### path_sweep(shape, path, [method], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [scale=], [scale_by_length=], [last_normal=], [tangent=], [uniform=], [relaxed=], [caps=], [style=], [convexity=], [anchor=], [cp=], [spin=], [orient=], [atype=]) [ATTACHMENTS];

Sweep a 2d polygon path along a 2d or 3d path.

```openscad
path = [for(theta=[-180:5:180]) [theta/10, 10*sin(theta)]];
sq = square(6,center=true);
path_sweep(sq,path);
```

#### path_sweep2d(shape, path, [closed], [caps], [quality], [style], [convexity=], [anchor=], [spin=], [orient=], [atype=], [cp=]) [ATTACHMENTS];

Sweep a 2d polygon path along a 2d path allowing self-intersection.

```openscad
sinewave = [for(i=[-30:10:360*2+30]) [i/40,3*sin(i)]];
path_sweep2d(circle(r=3,$fn=15), sinewave);
```

#### tangs = path_tangents(path, [closed], [uniform]);

Returns tangent vectors for each point along a path.

```openscad
rect = square([10,3]);
tangents = path_tangents(rect,closed=true);
stroke(rect,closed=true, width=0.25);
color("purple")
for(i=[0:len(tangents)-1])
stroke([rect[i]-tangents[i], rect[i]+tangents[i]],width=.25, endcap2="arrow2");
```

#### path_text(path, text, [size], [thickness], [font], [lettersize=], [offset=], [reverse=], [normal=], [top=], [textmetrics=], [kern=])

Creates 2d or 3d text placed along a path.

```openscad
path = path3d(arc(100, r=25, angle=[245, 370]));
color("red")stroke(path, width=.3);
path_text(path, "Example text", font="Liberation Mono", size=5, lettersize = 5/1.2);
```

#### bezpath = path_to_bezpath(path, [closed], [tangents], [uniform], [size=]|[relsize=]);

Generates a bezier path that passes through all points in a given linear path.

```openscad
// path_to_bezpath — Generates a bezier path that passes through all points in a given linear path.
// Usage: bezpath = path_to_bezpath(path, [closed], [tangents], [uniform], [size=]|[relsize=]);
```

#### torsions = path_torsion(path, [closed]);

Returns the estimated numerical torsion of the {{path}}.

```openscad
// path_torsion — Returns the estimated numerical torsion of the {{path}}.
// Usage: torsions = path_torsion(path, [closed]);
```

#### pts = path2d(points);

Convert a path to 2D.

```openscad
// path2d — Convert a path to 2D.
// Usage: pts = path2d(points);
```

#### pts = path3d(points, [fill]);

Convert a path to 3D.

```openscad
// path3d — Convert a path to 3D.
// Usage: pts = path3d(points, [fill]);
```

#### pt = path4d(points, [fill]);

Convert a path to 4d.

```openscad
// path4d — Convert a path to 4d.
// Usage: pt = path4d(points, [fill]);
```

#### pco1810_cap([h], [r|d], [wall], [texture]) [ATTACHMENTS];

Creates a cap for a PCO1810 standard bottle.

```openscad
pco1810_cap();
pco1810_cap(texture="knurled");
pco1810_cap(texture="ribbed");
```

#### pco1810_neck([wall]) [ATTACHMENTS];

Creates a neck for a PCO1810 standard bottle.

```openscad
pco1810_neck();
```

#### pco1881_cap(wall, [texture]) [ATTACHMENTS];

Creates a cap for a PCO1881 standard bottle.

```openscad
pco1881_cap();
pco1881_cap(texture="knurled");
pco1881_cap(texture="ribbed");
```

#### pco1881_neck([wall]) [ATTACHMENTS];

Creates a neck for a PCO1881 standard bottle.

```openscad
pco1881_neck();
```

#### pentagon(or|od=, [realign=], [align_tip=|align_side=]) [ATTACHMENTS];

Creates a regular pentagon.

```openscad
pentagon(or=30);
pentagon(od=60);
```

#### list = permutations(l, [n]);

Returns a list of all permutations of the list entries.

```openscad
pairs = permutations([3,4,5,6]);  // // Returns: [[3,4],[3,5],[3,6],[4,3],[4,5],[4,6],[5,3],[5,4],[5,6],[6,3],[6,4],[6,5]]
```

#### depth = phillips_depth(size, d);

Returns the depth a phillips recess needs to be for a given diameter.

```openscad
// phillips_depth — Returns the depth a phillips recess needs to be for a given diameter.
// Usage: depth = phillips_depth(size, d);
```

#### diam = phillips_diam(size, depth);

Returns the diameter of a phillips recess of a given depth.

```openscad
// phillips_diam — Returns the diameter of a phillips recess of a given depth.
// Usage: diam = phillips_diam(size, depth);
```

#### phillips_mask(size) [ATTACHMENTS];

Creates a mask for a Philips screw drive.

```openscad
xdistribute(10) {
phillips_mask(size="#1");
phillips_mask(size="#2");
phillips_mask(size=3);
phillips_mask(size=4);
}
```

#### pie_slice(l|h=|height=|length=, r, ang, [center]);

Creates a pie slice shape.

```openscad
pie_slice(ang=45, l=20, r=30);
```

#### pr = pitch_radius(pitch, teeth, [helical]);

Returns the pitch radius for a gear.

```openscad
pr = pitch_radius(circ_pitch=5, teeth=11);
pr = pitch_radius(circ_pitch=5, teeth=11, helical=30);
pr = pitch_radius(diam_pitch=10, teeth=11);
pr = pitch_radius(mod=2, teeth=20);
pr = pitch_radius(mod=2, teeth=20, helical=30);
```

#### pts = plane_closest_point(plane, points);

Returns the orthogonal projection of points onto a plane.

```openscad
points = move([10,20,30], p=yrot(25, p=path3d(circle(d=100, $fn=36))));
plane = plane_from_normal([1,0,1]);
proj = plane_closest_point(plane,points);
color("red") move_copies(points) sphere(d=4,$fn=12);
color("blue") move_copies(proj) sphere(d=4,$fn=12);
move(centroid(proj)) {
rot(from=UP,to=plane_normal(plane)) {
anchor_arrow(50);
%cube([120,150,0.1],center=true);
}
}
```

#### plane = plane_from_normal(normal, [pt])

Return plane defined by normal vector and a point.

```openscad
plane_from_normal([0,0,1], [2,2,2]);  // Returns the xy plane passing through the point (2,2,2)
```

#### plane = plane_from_points(points, [check_coplanar], [eps]);

Return plane defined by a set of 3D points, with arbitrary normal direction.

```openscad
points = rot(45, v=[-0.3,1,0],
p=random_points(100,3,scale=[50,50,15],seed=47));
plane = plane_from_points(points);
move_copies(points) sphere(d=3, $fn=12);
cp = mean(points);
move(cp) rot(from=UP,to=plane_normal(plane)) {
color("#06f") anchor_arrow(50, flag=false);
%linear_extrude(0.1) square(100, center=true);
}
```

#### plane = plane_from_polygon(points, [check_coplanar], [eps]);

Given a 3d planar polygon, returns directed plane.

```openscad
xyzpath = rot(45, v=[0,1,0], p=path3d(star(n=5,step=2,d=100), 70));
plane = plane_from_polygon(xyzpath);
#stroke(xyzpath,closed=true,width=3);
cp = centroid(xyzpath);
move(cp) rot(from=UP,to=plane_normal(plane)) anchor_arrow(45);
```

#### line = plane_intersection(plane1, plane2)

Returns the intersection of two or three planes.

```openscad
// plane_intersection — Returns the intersection of two or three planes.
// Usage: line = plane_intersection(plane1, plane2)
```

#### angle = plane_line_angle(plane,line);

Returns the angle between a plane and a 3d line.

```openscad
// plane_line_angle — Returns the angle between a plane and a 3d line.
// Usage: angle = plane_line_angle(plane,line);
```

#### pt = plane_line_intersection(plane, line, [bounded], [eps]);

Returns the intersection of a plane and 3d line, segment or ray.

```openscad
// plane_line_intersection — Returns the intersection of a plane and 3d line, segment or ray.
// Usage: pt = plane_line_intersection(plane, line, [bounded], [eps]);
```

#### vec = plane_normal(plane);

Returns the normal vector to a plane.

```openscad
// plane_normal — Returns the normal vector to a plane.
// Usage: vec = plane_normal(plane);
```

#### d = plane_offset(plane);

Returns the signed offset of the plane from the origin.

```openscad
// plane_offset — Returns the signed offset of the plane from the origin.
// Usage: d = plane_offset(plane);
```

#### plane = plane3pt(p1, p2, p3);

Return a plane from 3 points.

```openscad
// plane3pt — Return a plane from 3 points.
// Usage: plane = plane3pt(p1, p2, p3);
```

#### plane = plane3pt_indexed(points, i1, i2, i3);

Given list of 3d points and 3 indices, return the plane they define.

```openscad
// plane3pt_indexed — Given list of 3d points and 3 indices, return the plane they define.
// Usage: plane = plane3pt_indexed(points, i1, i2, i3);
```

#### gear_data = planetary_gears(mod=|circ_pitch=|diam_pitch=, n, max_teeth, ring_carrier=|carrier_ring=|sun_carrier=|carrier_sun=|sun_ring=|ring_sun=, [helical=], [gear_spin=]);

Calculate teeth counts and angles for planetary gear assembly with specified ratio.

```openscad
mod=1;
gear_data = planetary_gears(mod=mod, n=3, max_teeth=28, ring_carrier=1.341, gear_spin=4/3*360/3*$t);
ring_gear2d(mod=mod, teeth=gear_data[1][1], profile_shift=gear_data[1][2], gear_spin=gear_data[1][3],backing=2);
stroke(gear_data[2][4],closed=true,color="blue",width=2);
spur_gear2d(mod=mod, teeth=gear_data[0][1], profile_shift=gear_data[0][2], gear_spin=gear_data[0][3]);  //sun
color("red")move_copies(gear_data[2][4])
spur_gear2d(mod=mod, teeth=gear_data[2][1], profile_shift=gear_data[2][2], gear_spin=gear_data[2][3][$idx]);
```

#### plot_revolution(f, angle, z, [r=/d=] [r1=/d1], [r2=/d2=], [rclip=], [rspan=], [horiz=], [style=], [convexity=], ...) [ATTACHMENTS];

Generates a surface by evaluating a of z and theta and putting the result on a surface of revolution

```openscad
f = function (x,y) 5*cos(5*norm([x*180/50,y*180/50]))+5;
plot_revolution(f, arclength=[-50:1:50], z=[-50:1:50], r=30);
```

#### plot3d(f, x, y, [zclip=], [zspan=], [base=], [convexity=], [style=]) [ATTACHMENTS];

Generates a surface by evaluating a function on a 2D grid

```openscad
func = function (x,y) 35*cos(3*norm([x,y]));
plot3d(func, [-180:4:180], [-180:4:180]);
```

#### bool = point_in_polygon(point, poly, [nonzero], [eps])

Checks if a 2d point is inside or on the boundary of a 2d polygon.

```openscad
a=20*2/3;
b=30*2/3;
ofs = 17*2/3;
curve = [for(theta=[0:10:140])  [a * theta/360*2*PI - b*sin(theta), a-b*cos(theta)]];
path = deduplicate(concat( reverse(offset(curve,r=ofs,closed=false)),
xflip(offset(curve,r=ofs,closed=false)),
xflip(reverse(curve)),
curve
));
stroke(path,closed=true);
pts = [[0,0],[10,0],[0,20]];
for(p=pts){
color(point_in_polygon(p,path)==1 ? "green" : "red")
move(p)circle(r=1.5, $fn=12);
}
```

#### check = point_in_region(point, region, [eps]);

Tests if a point is inside, outside, or on the border of a {{region}}.

```openscad
region = [for(i=[2:4:10]) hexagon(r=i)];
color("#ff7") region(region);
for(x=[-10:10], y=[-10:10])
if (point_in_region([x,y], region)>=0)
move([x,y]) color("red") circle(0.15, $fn=12);
else
move([x,y]) color("#ddf") circle(0.1, $fn=12);
```

#### dist = point_line_distance(pt, line, [bounded]);

Find shortest distance from point to a line, segment or ray.

```openscad
dist1 = point_line_distance([3,8], [[-10,0], [10,0]]);  // Returns: 8
dist2 = point_line_distance([3,8], [[-10,0], [10,0]],SEGMENT);  // Returns: 8
dist3 = point_line_distance([14,3], [[-10,0], [10,0]],SEGMENT);  // Returns: 5
```

#### dist = point_plane_distance(plane, point)

Determine distance between a point and plane.

```openscad
// point_plane_distance — Determine distance between a point and plane.
// Usage: dist = point_plane_distance(plane, point)
```

#### pt = point2d(p, [fill]);

Convert a vector to 2D.

```openscad
// point2d — Convert a vector to 2D.
// Usage: pt = point2d(p, [fill]);
```

#### pt = point3d(p, [fill]);

Convert a vector to 3D.

```openscad
// point3d — Convert a vector to 3D.
// Usage: pt = point3d(p, [fill]);
```

#### pt = point4d(p, [fill]);

Convert a vector to 4d.

```openscad
// point4d — Convert a vector to 4d.
// Usage: pt = point4d(p, [fill]);
```

#### pt_pair = pointlist_bounds(pts);

Returns the min and max bounding coordinates for the given list of points.

```openscad
// pointlist_bounds — Returns the min and max bounding coordinates for the given list of points.
// Usage: pt_pair = pointlist_bounds(pts);
```

#### pt = polar_to_xy(r, theta);

Convert 2D polar coordinates to cartesian coordinates.

```openscad
xy = polar_to_xy(20,45);    // Returns: ~[14.1421365, 14.1421365]
xy = polar_to_xy(40,30);    // Returns: ~[34.6410162, 15]
xy = polar_to_xy([40,30]);  // Returns: ~[34.6410162, 15]
xy = polar_to_xy([[40,30],[20,120]]);  // Returns: ~[[34.6410162, 15], [-10, 17.3205]]
```

#### sum = poly_add(p,q)

Returns the polynomial sum of adding two polynomials.

```openscad
// poly_add — Returns the polynomial sum of adding two polynomials.
// Usage: sum = poly_add(p,q)
```

#### [quotient,remainder] = poly_div(n,d)

Returns the polynomial quotient and remainder results of dividing two polynomials.

```openscad
// poly_div — Returns the polynomial quotient and remainder results of dividing two polynomials.
// Usage: [quotient,remainder] = poly_div(n,d)
```

#### x = polymult(p,q)

Compute product of two polynomials, returning a polynomial.

```openscad
// poly_mult — Compute product of two polynomials, returning a polynomial.
// Usage: x = polymult(p,q)
```

#### roots = poly_roots(p, [tol]);

Returns all complex valued roots of the given real polynomial.

```openscad
// poly_roots — Returns all complex valued roots of the given real polynomial.
// Usage: roots = poly_roots(p, [tol]);
```

#### area = polygon_area(poly, [signed]);

Calculate area of a 2d or 3d polygon.

```openscad
// polygon_area — Calculate area of a 2d or 3d polygon.
// Usage: area = polygon_area(poly, [signed]);
```

#### polygon_edge_mask(mask, l|h=|length=|height=, [scale=]) [ATTACHMENTS];

Extrudes a 2d mask polygon to an edge mask with a correct corner anchor

```openscad
polygon_edge_mask(mask2d_roundover(r=5, excess=2), length=20);
```

#### pt_or_segments = polygon_line_intersection(poly, line, [bounded], [nonzero], [eps]);

Find intersection between 2d or 3d polygon and a line, segment or ray.

```openscad
hex = zrot(140,p=rot([-45,40,20],p=path3d(hexagon(r=15))));
line = [[5,0,-13],[-3,-5,13]];
isect = polygon_line_intersection(hex,line);
stroke(hex,closed=true);
stroke(line);
color("red")move(isect)sphere(r=1,$fn=12);
```

#### vec = polygon_normal(poly);

Return normal to a polygon.

```openscad
path = rot([0,30,15], p=path3d(star(n=5, d=100, step=2)));
stroke(path, closed=true);
n = polygon_normal(path);
rot(from=UP, to=n)
color("red")
stroke([[0,0,0], [0,0,20]], endcap2="arrow2");
```

#### splitpolys = polygon_parts(poly, [nonzero], [eps]);

Parses a self-intersecting polygon into a list of non-intersecting {{polygons}}.

```openscad
poly = [
[-100,100], [0,-50], [100,100],
[100,-100], [0,50], [-100,-100]
];
splitpolys = polygon_parts(poly);
rainbow(splitpolys) stroke($item, closed=true, width=3);
```

#### triangles = polygon_triangulate(poly, [ind], [error], [eps])

Divide a polygon into triangles.

```openscad
poly = star(id=10, od=15,n=11);
tris =  polygon_triangulate(poly);
color("lightblue") for(tri=tris) polygon(select(poly,tri));
color("blue")    up(1) for(tri=tris) { stroke(select(poly,tri),.15,closed=true); }
color("magenta") up(2) stroke(poly,.25,closed=true); 
color("black")   up(3) debug_vnf([path3d(poly),[]],faces=false,size=1);
```

#### x = polynomial(p, z)

Evaluate a polynomial at a real or complex value.

```openscad
// polynomial — Evaluate a polynomial at a real or complex value.
// Usage: x = polynomial(p, z)
```

#### PARENT() position(at) CHILDREN;

Attaches children to a parent object at an anchor point.

```openscad
spheroid(d=20) {
position(TOP) cyl(l=10, d1=10, d2=5, anchor=BOTTOM);
position(RIGHT) cyl(l=10, d1=10, d2=5, anchor=BOTTOM);
position(FRONT) cyl(l=10, d1=10, d2=5, anchor=BOTTOM);
}
```

#### mod = posmod(x, m)

Returns the positive modulo of a value.

```openscad
a = posmod(-700,360);  // Returns: 340
b = posmod(-270,360);  // Returns: 90
c = posmod(-120,360);  // Returns: 240
d = posmod(120,360);   // Returns: 120
e = posmod(270,360);   // Returns: 270
f = posmod(700,360);   // Returns: 340
g = posmod(3,2.5);     // Returns: 0.5
```

#### prism_connector(profile, desc1, anchor1, desc2, anchor2, [fillet=], [fillet1=], [fillet2=], [spin_align=], [scale=], [shift1=], [shift2]=, [shift=], [n=], [n1=], [n2=], [k=], [k1=], [k2=], [uniform=], [uniform1=], [uniform2=], [overlap=], [overlap1=], [overlap2=], [smooth_normals=], [smooth_normals=], [smooth_normals1]=, [smooth_normals2=], [debug=], [debug_pos=]);

Construct a filleted prism that connects objects

```openscad
circ = circle(r=3, $fn=48);
prismoid(20,13,shift=[-2,1],h=15) let(prism=parent())
right(30) zrot(20)spheroid(r=10,circum=true,$fn=48) let(ball=parent())
prism_connector(circ,prism,RIGHT,ball,LEFT,fillet1=4,fillet2=1);
```

#### prismoid(size1, size2, [h|l|height|length], [shift], [xang=], [yang=], ...) [ATTACHMENTS];

Creates a rectangular prismoid shape with optional roundovers and chamfering.

```openscad
prismoid(size1=[35,50], size2=[20,30], h=20);
```

#### x = product(v);

Returns the multiplicative product of a list of values.

```openscad
product([2,3,4]);  // returns 24.
product([[1,2,3], [3,4,5], [5,6,7]]);  // returns [15, 48, 105]
```

#### xy = project_plane(plane, p);

Project a set of points onto a specified plane, returning 2D points.

```openscad
pt = [5,-5,5];
a=[0,0,0];  b=[10,-10,0];  c=[10,0,10];
xy = project_plane([a,b,c],pt);
```

#### region = projection(vnf, [cut], [z]);

Returns projection or intersection of vnf with XY plane

```openscad
vnf = vnf_join([
xrot(90,torus(id=15,od=24,$fn=5)),
right(12,torus(id=15,od=24,$fn=4)),
up(13,right(15,cube(3,center=true)))
]);
vnf_polyhedron(vnf);
```

#### qr = qr_factor(A,[pivot]);

Compute QR factorization of a matrix.

```openscad
// qr_factor — Compute QR factorization of a matrix.
// Usage: qr = qr_factor(A,[pivot]);
```

#### roots = quadratic_roots(a, b, c, [real])

Computes roots for the quadratic equation.

```openscad
// quadratic_roots — Computes roots for the quadratic equation.
// Usage: roots = quadratic_roots(a, b, c, [real])
```

#### num = quant(x, y);

Returns `x` quantized to the nearest integer multiple of `y`.

```openscad
a = quant(12,4);    // Returns: 12
b = quant(13,4);    // Returns: 12
c = quant(13.1,4);  // Returns: 12
d = quant(14,4);    // Returns: 16
e = quant(14.1,4);  // Returns: 16
f = quant(15,4);    // Returns: 16
g = quant(16,4);    // Returns: 16
h = quant(9,3);     // Returns: 9
i = quant(10,3);    // Returns: 9
j = quant(10.4,3);  // Returns: 9
k = quant(10.5,3);  // Returns: 12
l = quant(11,3);    // Returns: 12
m = quant(12,3);    // Returns: 12
n = quant(11,2.5);  // Returns: 10
o = quant(12,2.5);  // Returns: 12.5
p = quant([12,13,13.1,14,14.1,15,16],4);  // Returns: [12,12,12,16,16,16,16]
q = quant([9,10,10.4,10.5,11,12],3);      // Returns: [9,9,9,12,12,12]
r = quant([[9,10,10.4],[10.5,11,12]],3);  // Returns: [[9,9,9],[12,12,12]]
```

#### num = quantdn(x, y);

Returns `x` quantized down to an integer multiple of `y`.

```openscad
a = quantdn(12,4);    // Returns: 12
b = quantdn(13,4);    // Returns: 12
c = quantdn(13.1,4);  // Returns: 12
d = quantdn(14,4);    // Returns: 12
e = quantdn(14.1,4);  // Returns: 12
f = quantdn(15,4);    // Returns: 12
g = quantdn(16,4);    // Returns: 16
h = quantdn(9,3);     // Returns: 9
i = quantdn(10,3);    // Returns: 9
j = quantdn(10.4,3);  // Returns: 9
k = quantdn(10.5,3);  // Returns: 9
l = quantdn(11,3);    // Returns: 9
m = quantdn(12,3);    // Returns: 12
n = quantdn(11,2.5);  // Returns: 10
o = quantdn(12,2.5);  // Returns: 10
p = quantdn([12,13,13.1,14,14.1,15,16],4);  // Returns: [12,12,12,12,12,12,16]
q = quantdn([9,10,10.4,10.5,11,12],3);      // Returns: [9,9,9,9,9,12]
r = quantdn([[9,10,10.4],[10.5,11,12]],3);  // Returns: [[9,9,9],[9,9,12]]
```

#### num = quantup(x, y);

Returns `x` quantized uo to an integer multiple of `y`.

```openscad
a = quantup(12,4);    // Returns: 12
b = quantup(13,4);    // Returns: 16
c = quantup(13.1,4);  // Returns: 16
d = quantup(14,4);    // Returns: 16
e = quantup(14.1,4);  // Returns: 16
f = quantup(15,4);    // Returns: 16
g = quantup(16,4);    // Returns: 16
h = quantup(9,3);     // Returns: 9
i = quantup(10,3);    // Returns: 12
j = quantup(10.4,3);  // Returns: 12
k = quantup(10.5,3);  // Returns: 12
l = quantup(11,3);    // Returns: 12
m = quantup(12,3);    // Returns: 12
n = quantdn(11,2.5);  // Returns: 12.5
o = quantdn(12,2.5);  // Returns: 12.5
p = quantup([12,13,13.1,14,14.1,15,16],4);  // Returns: [12,16,16,16,16,16,16]
q = quantup([9,10,10.4,10.5,11,12],3);      // Returns: [9,12,12,12,12,12]
r = quantup([[9,10,10.4],[10.5,11,12]],3);  // Returns: [[9,12,12],[12,12,12]]
```

#### rabbit_clip(type, length, width, snap, thickness, depth, [compression=], [clearance=], [lock=], [lock_clearance=], [splineteps=], [anchor=], [orient=], [spin=]) [ATTACHMENTS];

Creates a rabbit-eared clip that can snap into a slot.

```openscad
module test_pair(length, width, snap, thickness, compression, lock=false)
{
depth = 5;
extra_depth = 10;// Change this to 0.4 for closed sockets
cuboid([max(width+5,12),12, depth], chamfer=.5, edges=[FRONT,"Y"], anchor=BOTTOM)
attach(BACK)
rabbit_clip(type="pin",length=length, width=width,snap=snap,thickness=thickness,depth=depth,
compression=compression,lock=lock);
right(width+13)
diff("remove")
cuboid([width+8,max(12,length+2),depth+3], chamfer=.5, edges=[FRONT,"Y"], anchor=BOTTOM)
tag("remove")
attach(BACK)
rabbit_clip(type="socket",length=length, width=width,snap=snap,thickness=thickness,
depth=depth+extra_depth, lock=lock,compression=0);
}
left(37)ydistribute(spacing=28){
test_pair(length=6, width=7, snap=0.25, thickness=0.8, compression=0.1);
test_pair(length=3.5, width=7, snap=0.1, thickness=0.8, compression=0.1);  // snap = 0.2 gives a firmer connection
test_pair(length=3.5, width=5, snap=0.1, thickness=0.8, compression=0.1);  // hard to take apart
}
right(17)ydistribute(spacing=28){
test_pair(length=12, width=10, snap=1, thickness=1.2, compression=0.2);
test_pair(length=8, width=7, snap=0.75, thickness=0.8, compression=0.2, lock=true); // With lock, very firm and irreversible
}
```

#### rack(pitch, teeth, thickness, [base|bottom=|width=], [helical=], [pressure_angle=], [backlash=], [clearance=]) [ATTACHMENTS];

Creates a straight or helical gear rack.

```openscad
rack(pitch=5, teeth=10, thickness=5);
```

#### rack2d(pitch, teeth, [base|bottom=|width=], [pressure_angle=], [backlash=], [clearance=]) [ATTACHMENTS];

Creates a 2D gear rack.

```openscad
rack2d(pitch=5, teeth=10);
```

#### rail(l, w, h, [chamfer=], [ang=]) [ATTACHMENTS];

Creates a V-groove rail.

```openscad
rail(l=100, w=10, h=10);
```

#### rainbow(list,[stride],[maxhues],[shuffle],[seed]) CHILDREN;

Iterates through a list, displaying children in different colors.

```openscad
rainbow(["Foo","Bar","Baz"]) fwd($idx*10) text(text=$item,size=8,halign="center",valign="center");
```

#### rand_int(minval, maxval, n, [seed]);

Returns a random integer.

```openscad
ints = rand_int(0,100,3);
int = rand_int(-10,10,1)[0];
```

#### str = rand_str(n, [charset], [seed]);

Create a randomized string.

```openscad
// rand_str — Create a randomized string.
// Usage: str = rand_str(n, [charset], [seed]);
```

#### points = random_points(n, dim, [scale], [seed]);

Returns a list of random points.

```openscad
// random_points — Returns a list of random points.
// Usage: points = random_points(n, dim, [scale], [seed]);
```

#### points = random_polygon([n], [size], [angle_sep], [seed]);

Returns the clockwise path of a simple random polygon.

```openscad
polygon(random_polygon(17, [10,20], seed=888));
```

#### roots = real_roots(p, [eps], [tol])

Returns all real roots of the given real polynomial.

```openscad
// real_roots — Returns all real roots of the given real polynomial.
// Usage: roots = real_roots(p, [eps], [tol])
```

#### recolor([c]) CHILDREN;

Sets the color for attachable children and their descendants.

```openscad
cuboid([10,10,5])
recolor("green")attach(TOP,BOT) cuboid([9,9,4.5])
attach(TOP,BOT) cuboid([8,8,4])
recolor("purple") attach(TOP,BOT) cuboid([7,7,3.5])
attach(TOP,BOT) cuboid([6,6,3])
recolor("cyan")attach(TOP,BOT) cuboid([5,5,2.5])
attach(TOP,BOT) cuboid([4,4,2]);
```

#### rect(size, [rounding], [chamfer], ...) [ATTACHMENTS];

Creates a 2d rectangle with optional corner rounding.

```openscad
rect(40);
```

#### rect_tube(h, size, isize, [center|anchor=], [shift]);

Creates a rectangular tube.

```openscad
rect_tube(size=50, wall=5, h=30);
rect_tube(size=[100,60], wall=5, h=30);
rect_tube(isize=[60,80], wall=5, h=30);
rect_tube(size=[100,60], isize=[90,50], h=30);
rect_tube(size1=[100,60], size2=[70,40], wall=5, h=30);
```

#### res = reduce(func, list, [init]);

Applies a 2-arg function cumulatively to the items of a list, returning the final result.

```openscad
x = reduce(f_add(),[3,4,5]);  // Returns: 12
```

#### region(r, [anchor], [spin=], [cp=], [atype=]) [ATTACHMENTS];

Creates the 2D {{polygons}} described by the given {{region}} or list of polygons.

```openscad
region([circle(d=50), square(25,center=true)]);
```

#### area = region_area(region);

Computes the area of the specified valid {{region}}.

```openscad
area = region_area([square(10), right(20,square(8))]);  // Returns 164
```

#### rgns = region_parts(region);

Splits a {{region}} into a list of connected regions.

```openscad
R = [for(i=[1:7]) square(i,center=true)];
region_list = region_parts(R);
rainbow(region_list) region($item);
```

#### regular_ngon(n, r|d=|or=|od=, [realign=]) [ATTACHMENTS];

Creates a regular N-sided polygon.

```openscad
regular_ngon(n=5, or=30);
regular_ngon(n=5, od=60);
```

#### regular_polyhedron([name],[index=],[type=],[faces=],[facetype=],[hasfaces=],...) [CHILDREN];

Creates a regular polyhedron with optional rounding.

```openscad
regular_polyhedron("tetrahedron", facedown=false);
regular_polyhedron("cube", facedown=false);
regular_polyhedron("octahedron", facedown=false);
regular_polyhedron("dodecahedron", facedown=false);
regular_polyhedron("icosahedron", facedown=false);
regular_polyhedron("truncated tetrahedron", facedown=false);
regular_polyhedron("truncated octahedron", facedown=false);
regular_polyhedron("truncated cube", facedown=false);
regular_polyhedron("truncated icosahedron", facedown=false);
regular_polyhedron("truncated dodecahedron", facedown=false);
regular_polyhedron("cuboctahedron", facedown=false);
regular_polyhedron("icosidodecahedron", facedown=false);
regular_polyhedron("rhombicuboctahedron", facedown=false);
regular_polyhedron("rhombicosidodecahedron", facedown=false);
regular_polyhedron("truncated cuboctahedron", facedown=false);
regular_polyhedron("truncated icosidodecahedron", facedown=false);
regular_polyhedron("snub cube", facedown=false);
regular_polyhedron("snub dodecahedron", facedown=false);
regular_polyhedron("triakis tetrahedron", facedown=false);
regular_polyhedron("tetrakis hexahedron", facedown=false);
regular_polyhedron("triakis octahedron", facedown=false);
regular_polyhedron("pentakis dodecahedron", facedown=false);
regular_polyhedron("triakis icosahedron", facedown=false);
regular_polyhedron("rhombic dodecahedron", facedown=false);
regular_polyhedron("rhombic triacontahedron", facedown=false);
regular_polyhedron("deltoidal icositetrahedron", facedown=false);
regular_polyhedron("deltoidal hexecontahedron", facedown=false);
regular_polyhedron("disdyakis dodecahedron", facedown=false);
regular_polyhedron("disdyakis triacontahedron", facedown=false);
regular_polyhedron("pentagonal icositetrahedron", facedown=false);
regular_polyhedron("pentagonal hexecontahedron", facedown=false);
regular_polyhedron("trapezohedron",faces=10, side=1, longside=2.25, facedown=false);
regular_polyhedron("great dodecahedron");
regular_polyhedron("small stellated dodecahedron");
regular_polyhedron("great stellated dodecahedron");
regular_polyhedron("small triambic icosahedron");
```

#### info = regular_polyhedron_info(info, ...);

Returns info used to create a regular polyhedron.

```openscad
// regular_polyhedron_info — Returns info used to create a regular polyhedron.
// Usage: info = regular_polyhedron_info(info, ...);
```

#### regular_prism(n, h|l=|height=|length=, r, [center=|anchor=], [realign=]) [ATTACHMENTS];

Creates a regular prism with roundovers and chamfering

```openscad
regular_prism(5,r=10,h=25);
```

#### newpoly = reindex_polygon(reference, poly);

Adjust point indexing of polygon to minimize pointwise distance to a reference polygon.

```openscad
pent = subdivide_path([for(i=[0:4])[sin(72*i),cos(72*i)]],30);
circ = circle($fn=30,r=2.2);
reindexed = reindex_polygon(circ,pent);
move_copies(concat(circ,pent)) circle(r=.1,$fn=32);
color("red") move_copies([pent[0],circ[0]]) circle(r=.1,$fn=32);
color("blue") translate(reindexed[0])circle(r=.1,$fn=32);
```

#### mat = reorient(anchor, spin, [orient], two_d=true, size=, [size2=], [shift=], ...);

Calculates the transformation matrix needed to reorient an object.

```openscad
// reorient — Calculates the transformation matrix needed to reorient an object.
// Usage: mat = reorient(anchor, spin, [orient], two_d=true, size=, [size2=], [shift=], ...);
```

#### list = repeat(val, n);

Returns a list of repeated copies of a value.

```openscad
a = repeat(1, 4);        // Returns [1,1,1,1]
b = repeat(8, [2,3]);    // Returns [[8,8,8], [8,8,8]]
c = repeat(0, [2,2,3]);  // Returns [[[0,0,0],[0,0,0]], [[0,0,0],[0,0,0]]]
d = repeat([1,2,3],3);   // Returns [[1,2,3], [1,2,3], [1,2,3]]
e = repeat(4, -1);       // Returns []
```

#### newlist = repeat_entries(list, N, [exact]);

Repeats list entries (as uniformly as possible) to make list of specified length.

```openscad
list = [0,1,2,3];
a = repeat_entries(list, 6);  // Returns: [0,0,1,2,2,3]
b = repeat_entries(list, 6, exact=false);  // Returns: [0,0,1,1,2,2,3,3]
c = repeat_entries(list, [1,1,2,1], exact=false);  // Returns: [0,1,2,2,3]
```

#### req_children($children);

Assert that the calling module requires children.

```openscad
module foo() {
req_children($children);
}
```

#### newpath = resample_path(path, n|spacing=, [closed=]);

Returns an equidistant set of points along a path.

```openscad
path = xscale(2,circle($fn=250, r=10));
sampled = resample_path(path, 16);
stroke(path);
color("red")move_copies(sampled) circle($fn=16);
```

#### restore([desc]) CHILDREN;

Restores transformation state and attachment geometry from a description

```openscad
left(5) cuboid(10)
let(save_pt = parent())
attach(RIGHT,BOT) recolor("green") cuboid(3)
restore(save_pt)
attach(FWD,BOT) recolor("pink") cuboid(3);
```

#### reuleaux_polygon(n, r|d=, ...) [ATTACHMENTS];

Creates a constant-width shape that is not circular.

```openscad
reuleaux_polygon(n=3, r=50);
reuleaux_polygon(n=5, d=100);
```

#### rlist = reverse(list);

Reverses the elements of a list.

```openscad
reverse([3,4,5,6]);  // Returns [6,5,4,3]
```

#### newpoly = reverse_polygon(poly)

Reverse winding direction of polygon.

```openscad
// reverse_polygon — Reverse winding direction of polygon.
// Usage: newpoly = reverse_polygon(poly)
```

#### right(x) CHILDREN;

Translates children rightward (X+).

```openscad
#sphere(d=10);
right(20) sphere(d=10);
```

#### right_half([s=], [x=]) CHILDREN;

Masks the left half of an object along the Y-Z plane, leaving the right half.

```openscad
right_half() sphere(r=20);
right_half(x=-5) sphere(r=20);
```

#### right_triangle(size, [center], ...) [ATTACHMENTS];

Creates a right triangle.

```openscad
right_triangle([40,30]);
```

#### region=ring(n, r1=|d1=, r2=|d2=, [full=], [angle=], [start=]);

Draws a 2D ring or partial ring or returns a region or path

```openscad
ring(r1=5,r2=7, n=32);
ring(r=5,ring_width=-1, n=32);
ring(r=7, n=5, ring_width=-4);
ring(points=[[0,0],[3,3],[5,2]], ring_width=2, n=32);
ring(points=[[0,0],[3,3],[5,2]], r=1, n=32);
ring(cp=[3,3], points=[[4,4],[1,3]], ring_width=1);
ring(corner=[[0,0],[4,4],[7,3]], r2=2, r1=1.5,n=22,full=false);
ring(r1=5,r2=7, angle=[33,110], n=32);
ring(r1=5,r2=7, angle=[0,360], n=32);  // full circle
ring(r=5, points=[[0,0],[3,3],[5,2]], full=false, n=32);
ring(32,-2, cp=[1,1], points=[[4,4],[-3,6]], full=false);
```

#### ring_gear(circ_pitch, teeth, thickness, [backing|od=|or=|width=], [pressure_angle=], [helical=], [herringbone=], [profile_shift=], [clearance=], [backlash=]) [ATTACHMENTS];

Creates a 3D ring gear.

```openscad
ring_gear(circ_pitch=5, teeth=48, thickness=10);
```

#### ring_gear2d(circ_pitch, teeth, [backing|od=|or=|width=], [pressure_angle=], [helical=], [profile_shift=], [clearance=], [backlash=]) [ATTACHMENTS];

Creates a 2D ring gear.

```openscad
circ_pitch=5; teeth1=50; teeth2=18;
dist = gear_dist(circ_pitch=circ_pitch, teeth1, teeth2, internal1=true);
ring_gear2d(circ_pitch=circ_pitch, teeth=teeth1);
color("lightblue")back(dist)
spur_gear2d(circ_pitch=circ_pitch, teeth=teeth2);
```

#### ring_hook(base_size, hole_z, or, od=, [ir=], [hole=], [rounding=], [fillet=], [hole_rounding=], [anchor=], [spin=], [orient=])

A hook with a circular hole or attached cylinder

```openscad
ring_hook([50, 10], 25, 25, ir=20);
```

#### robertson_mask(size, [extra], [ang], [$slop=]);

Creates a mask for a Robertson/Square drive recess.

```openscad
robertson_mask(size=2);
```

#### x = root_find(f, x0, x1, [tol])

Finds a root of the given continuous function.

```openscad
f = function (x) x*sin(x)-4;
root=root_find(f, 0,25); // root = 15.2284
// Graphical verification
stroke([for(x=[0:25]) [x,f(x)]],width=.2);
color("red")move([root,f(root)])
circle(r=.25,$fn=16);
```

#### rr = outer_radius(mod=|circ_pitch=|diam_pitch=, teeth, [helical], [pressure_angle=], [clearance=], [internal=], [profile_shift=], [backlash=]);

Returns the radius of the roots of the teeth

```openscad
teeth=5;
mod=5;
rr = root_radius(mod=mod, teeth);
spur_gear2d(mod=mod, teeth=teeth, hide=floor(teeth/2));
circle(r=rr, $fn=64);
```

#### rot(a, [cp=], [reverse=]) CHILDREN;

Rotates children in various ways.

```openscad
#cube([2,4,9]);
rot([30,60,0], cp=[0,0,9]) cube([2,4,9]);
```

#### rot_copies(rots, [cp=], [sa=], [delta=], [subrot=]) CHILDREN;

Rotates copies of children.

```openscad
#cylinder(h=20, r1=5, r2=0);
rot_copies([[45,0,0],[0,45,90],[90,-45,270]]) cylinder(h=20, r1=5, r2=0);
```

#### info = rot_decode(rotation,[long]); // Returns: [angle,axis,cp,translation]

Extract axis and rotation angle from a rotation matrix.

```openscad
info = rot_decode(rot(45));
// Returns: [45, [0,0,1], [0,0,0], [0,0,0]]
info = rot_decode(rot(a=37, v=[1,2,3], cp=[4,3,-7])));
// Returns: [37, [0.26, 0.53, 0.80], [4.8, 4.6, -4.6], [0,0,0]]
info = rot_decode(left(12)*xrot(-33));
// Returns: [33, [-1,0,0], [0,0,0], [-12,0,0]]
info = rot_decode(translate([3,4,5]));
// Returns: [0, [0,0,1], [0,0,0], [3,4,5]]
```

#### B = rot_inverse(A)

Invert 2d or 3d rotation transformations.

```openscad
// rot_inverse — Invert 2d or 3d rotation transformations.
// Usage: B = rot_inverse(A)
```

#### rlist = rot_resample(rotlist, n, [method=], [twist=], [scale=], [smoothlen=], [long=], [turns=], [closed=])

Resample a list of rotation operators.

```openscad
tran = rot_resample([ident(4), back(5)*up(4)*xrot(-10)*zrot(-20)*yrot(117,cp=[10,0,0])], n=25);
sweep(circle(r=1,$fn=3), tran);
```

#### vnf = rotate_sweep(shape, [angle], ...);

Create a surface of revolution from a path with optional texturing.

```openscad
rgn = right(30,
union([for (a = [0, 90])
zrot(a, rect([15,5]))]));
rotate_sweep(rgn);
```

#### rounded_path = round_corners(path, [method], [radius=], [cut=], [joint=], [closed=], [verbose=]);

Round or chamfer the corners of a path (clipping them off).

```openscad
* `method="circle", radius=2`:
Rounds every point with circular, radius 2 roundover
* `method="smooth", cut=2`:
Rounds every point with continuous curvature rounding with a cut of 2, and a default 0.5 smoothing parameter
* `method="smooth", cut=2, k=0.3`:
Rounds every point with continuous curvature rounding with a cut of 2, and a gentle 0.3 smoothness setting
.
The number of segments used for roundovers is determined by `$fa`, `$fs` and `$fn` as usual for
circular roundovers.  For continuous curvature roundovers `$fs` and `$fn` are used and `$fa` is
ignored.  Note that $fn is interpreted as the number of points on the roundover curve, which is
not equivalent to its meaning for rounding circles because roundovers are usually small fractions
of a circular arc.  As usual, $fn overrides $fs.  When doing continuous curvature rounding, be sure to use lots of segments or the effect
would be hidden by the discretization.  Note that if you use $fn with "smooth" then $fn points are added at each corner.
This guarantees a specific output length.  It also means that if
you set `joint` nonzero on a flat "corner", with collinear points, you get `$fn` points at that "corner."
If you have two roundovers that fully consume a segment, then they share a point where they meet in the segment, which means the output
point count decreases by one.
```

#### round2d(r) [ATTACHMENTS];

Rounds the corners of 2d objects.

```openscad
round2d(r=10) {square([40,100], center=true); square([100,40], center=true);}
round2d(or=10) {square([40,100], center=true); square([100,40], center=true);}
round2d(ir=10) {square([40,100], center=true); square([100,40], center=true);}
round2d(or=16,ir=8) {square([40,100], center=true); square([100,40], center=true);}
```

#### round3d(r) CHILDREN;

Rounds arbitrary 3d objects.

```openscad
// round3d — Rounds arbitrary 3d objects.
// Usage: round3d(r) CHILDREN;
```

#### rounded_prism(bottom, [top], [height=|h=|length=|l=], [joint_top=], [joint_bot=], [joint_sides=], [k=], [k_top=], [k_bot=], [k_sides=], [splinesteps=], [debug=], [convexity=],...) [ATTACHMENTS];

Make a rounded 3d object by connecting two polygons with the same vertex count.

```openscad
rounded_prism(pentagon(3), height=3,
joint_top=0.5, joint_bot=0.5, joint_sides=0.5) position(FWD) cube(1);
```

#### rounding_corner_mask(r|d, [ang], [excess=], [style=]) [ATTACHMENTS];

Creates a shape to round 90° corners.

```openscad
rounding_corner_mask(r=20);
```

#### rounding_cylinder_mask(r|d=, rounding);

Creates a shape to round the end of a cylinder.

```openscad
difference() {
cylinder(r=50, h=50, center=false);
up(50) #rounding_cylinder_mask(r=50, rounding=10);
}
```

#### rounding_edge_mask(l|h=|length=|height=, r|d=, [ang], [excess=], [rounding=|chamfer=], ) [ATTACHMENTS];

Creates a shape to round an arbitrary 3d edge.

```openscad
rounding_edge_mask(l=50, r=15);
```

#### rounding_hole_mask(r|d, rounding, [excess]) [ATTACHMENTS];

Creates a shape to round the edge of a round hole.

```openscad
rounding_hole_mask(r=40, rounding=20, $fa=2, $fs=2);
```

#### ruler(length, width, [thickness=], [depth=], [labels=], [pipscale=], [maxscale=], [colors=], [alpha=], [unit=], [inch=]) [ATTACHMENTS];

Creates a ruler.

```openscad
ruler(100,depth=3);
ruler(100,depth=3,labels=true);
ruler(27);
ruler(27,maxscale=0);
ruler(100,pipscale=3/4,depth=2);
ruler(100,width=2,depth=2);
```

#### bool = same_shape(a,b);

Returns true if the argument lists are numeric and of the same shape.

```openscad
same_shape([3,[4,5]],[7,[3,4]]);   // Returns true
same_shape([3,4,5], [7,[3,4]]);    // Returns false
```

#### vec = scalar_vec3(v, [dflt]);

Expands a scalar or a list with length less than 3 to a length 3 vector.

```openscad
vec = scalar_vec3(undef);      // Returns: undef
vec = scalar_vec3(10);         // Returns: [10,10,10]
vec = scalar_vec3(10,1);       // Returns: [10,1,1]
vec = scalar_vec3([10,10],1);  // Returns: [10,10,1]
vec = scalar_vec3([10,10]);    // Returns: [10,10,0]
vec = scalar_vec3([10]);       // Returns: [10,0,0]
```

#### scale(SCALAR) CHILDREN;

Scales children arbitrarily.

```openscad
pt1 = scale(3, [3,1,4]);        // Returns: [9,3,12]
pt2 = scale([2,3,4], [3,1,4]);  // Returns: [6,3,16]
pt3 = scale([2,3,4], [[1,2,3],[4,5,6]]);  // Returns: [[2,6,12], [8,15,24]]
mat2d = scale([2,3]);    // Returns: [[2,0,0],[0,3,0],[0,0,1]]
mat3d = scale([2,3,4]);  // Returns: [[2,0,0,0],[0,3,0,0],[0,0,4,0],[0,0,0,1]]
```

#### screw([spec], [head], [drive], [thread=], [drive_size=], [length=|l=], [thread_len=], [undersize=], [shaft_undersize=], [head_undersize=], [tolerance=], [blunt_start=], [details=], [anchor=], [atype=], [orient=], [spin=]) [ATTACHMENTS];

Creates a standard screw with optional tolerances.

```openscad
$fn=32;
xdistribute(spacing=8){
screw("#6", length=12);
screw("#6-32", head="button", drive="torx",length=12);
screw("#6-32,3/4", head="hex");
screw("#6", thread="fine", head="fillister",length=12, drive="phillips");
screw("#6", head="flat small",length=12,drive="slot");
screw("#6-32", head="flat large", length=12, drive="torx");
screw("#6-32", head="flat undercut",length=12);
screw("#6-24", head="socket",length=12);          // Non-standard threading
screw("#6-32", drive="hex", drive_size=1.5, length=12);
}
```

#### screw_head(screw_info, [details],[counterbore],[flat_height],[teardrop],[internal])

Creates a screw head.

```openscad
// screw_head — Creates a screw head.
// Usage: screw_head(screw_info, [details],[counterbore],[flat_height],[teardrop],[internal])
```

#### screw_hole([spec], [head], [thread=], [length=|l=], [oversize=], [hole_oversize=], [teardrop=], [head_oversize], [tolerance=], [$slop=], [blunt_start=], [anchor=], [atype=], [orient=], [spin=]) [ATTACHMENTS];

Creates a screw hole.

```openscad
diff()
cuboid(20)
attach(TOP)
screw_hole("1/4-20,.5",head="socket",counterbore=5,anchor=TOP);
```

#### info = screw_info(name, [head], [drive], [thread=], [drive_size=], [oversize=], [head_oversize=])

Returns the dimensions and other info for the given screw.

```openscad
// screw_info — Returns the dimensions and other info for the given screw.
// Usage: info = screw_info(name, [head], [drive], [thread=], [drive_size=], [oversize=], [head_oversize=])
```

#### dist = segment_distance(seg1, seg2, [eps]);

Find smallest distance between two line semgnets.

```openscad
dist = segment_distance([[-14,3], [-15,9]], [[-10,0], [10,0]]);  // Returns: 5
dist2 = segment_distance([[-5,5], [5,-5]], [[-10,3], [10,-3]]);  // Returns: 0
```

#### sides = segs(r, [angle]);

Returns the number of sides for a circle given `$fn`, `$fa`, and `$fs`.

```openscad
$fn=12; sides=segs(10);           // Returns: 12
$fa=2; $fs=3; sides=segs(10);     // Returns: 21
$fa=2; $fs=3; sides=segs(10,180); // Returns: 11
```

#### item = select(list, start);

Returns one or more items from a list, with wrapping.

```openscad
l = [3,4,5,6,7,8,9];
a = select(l, 5, 6);   // Returns [8,9]
b = select(l, 5, 8);   // Returns [8,9,3,4]
c = select(l, 5, 2);   // Returns [8,9,3,4,5]
d = select(l, -3, -1); // Returns [7,8,9]
e = select(l, 3, 3);   // Returns [6]
f = select(l, 4);      // Returns 7
g = select(l, -2);     // Returns 8
h = select(l, [1:3]);  // Returns [4,5,6]
i = select(l, [3,1]);  // Returns [6,4]
```

#### s = set_difference(a, b);

Returns a list of unique items that are in list A, but not in list B.

```openscad
set_a = [2,3,5,7,11];
set_b = [1,2,3,5,8];
set_d = set_difference(set_a, set_b);
// set_d now equals [7,11]
```

#### s = set_intersection(a, b);

Returns a list of unique items that are in both given lists.

```openscad
set_a = [2,3,5,7,11];
set_b = [1,2,3,5,8];
set_i = set_intersection(set_a, set_b);
// set_i now equals [2,3,5]
```

#### s = set_union(a, b, [get_indices]);

Merges two lists, returning a list of unique items.

```openscad
set_a = [2,3,5,7,11];
set_b = [1,2,3,5,8];
set_u = set_union(set_a, set_b);
// set_u now equals [2,3,5,7,11,1,8]
set_v = set_union(set_a, set_b, get_indices=true);
// set_v now equals [[5,0,1,2,6], [2,3,5,7,11,1,8]]
```

#### shape_compare([eps]) {TEST_SHAPE; EXPECTED_SHAPE;}

Compares two child shapes.

```openscad
$fn=36;
shape_compare() {
sphere(d=100);
rotate_extrude() right_half(planar=true) circle(d=100);
}
```

#### shell2d(thickness, [or], [ir])

Creates a shell from 2D children.

```openscad
shell2d(10) {square([40,100], center=true); square([100,40], center=true);}
shell2d(-10) {square([40,100], center=true); square([100,40], center=true);}
shell2d([-10,10]) {square([40,100], center=true); square([100,40], center=true);}
shell2d(10,or=10) {square([40,100], center=true); square([100,40], center=true);}
shell2d(10,ir=10) {square([40,100], center=true); square([100,40], center=true);}
shell2d(10,or=[10,0]) {square([40,100], center=true); square([100,40], center=true);}
shell2d(10,or=[0,10]) {square([40,100], center=true); square([100,40], center=true);}
shell2d(10,ir=[10,0]) {square([40,100], center=true); square([100,40], center=true);}
shell2d(10,ir=[0,10]) {square([40,100], center=true); square([100,40], center=true);}
shell2d(8,or=[16,8],ir=[16,8]) {square([40,100], center=true); square([100,40], center=true);}
```

#### shoulder_screw(s, d, length, [head=], [thread_len=], [tolerance=], [head_size=], [drive=], [drive_size=], [thread=], [undersize=], [shaft_undersize=], [head_undersize=], [shoulder_undersize=],[atype=],[anchor=],[orient=],[spin=]) [ATTACHMENTS];

Creates a shoulder screw.

```openscad
shoulder_screw("iso",10,length=20);
```

#### show_all(...)

Shows all children and clears tags.

```openscad
// show_all — Shows all children and clears tags.
// Usage: show_all(...)
```

#### PARENT() show_anchors([s], [std=], [custom=]);

Shows anchors for the parent object.

```openscad
cube(50, center=true) show_anchors();
```

#### show_int(tags) CHILDREN;

Shows children with the listed tags that were already shown in the parent context.

```openscad
// show_int — Shows children with the listed tags that were already shown in the parent context.
// Usage: show_int(tags) CHILDREN;
```

#### show_only(tags) CHILDREN;

Show only the children with the listed tags.

```openscad
show_only("visible")
cube(50, anchor=CENTER)
tag("visible"){
attach(LEFT, BOTTOM) cylinder(d=30, h=30);
attach(RIGHT, BOTTOM) cylinder(d=30, h=30);
}
```

#### show_plane(plane, size, [offset]) [ATTACHMENTS];

Display (part of) a plane

```openscad
sphere(r=15,$fn=48);
plane = plane_from_normal([2,-3,9],[4,-5,12]);
%show_plane(plane, [35,25], [4,-7]);
```

#### show_transform_list(tlist, [s]);

Shows a list of transforms and how they connect.

```openscad
tlist = [
zrot(90),
zrot(90) * fwd(30) * zrot(30),
zrot(90) * fwd(30) * zrot(30) *
fwd(35) * xrot(-30),
zrot(90) * fwd(30) * zrot(30) *
fwd(35) * xrot(-30) * fwd(40) * yrot(15),
];
show_transform_list(tlist, s=20);
```

#### shuffled = shuffle(list, [seed]);

Randomizes the order of a list.

```openscad
//        Spades   Hearts    Diamonds  Clubs
suits = ["\u2660", "\u2661", "\u2662", "\u2663"];
ranks = [2,3,4,5,6,7,8,9,10,"J","Q","K","A"];
cards = [for (suit=suits, rank=ranks) str(rank,suit)];
deck = shuffle(cards);
```

#### hx = simple_hash(x);

Returns an integer hash of a given value.

```openscad
x = simple_hash("Foobar");
x = simple_hash([[10,20],[-5,3]]);
```

#### newpath = simplify_path(path, maxerr, [closed=]);

Removes points from an irregular path, preserving dominant features.

```openscad
calif = [
[225,12681], [199,12544], [180,12490], [221,12435], [300,12342], [310,12315], [320,12263], [350,12154],
[374,11968], [350,11820], [328,11707], [291,11586], [259,11553], [275,11499], [304,11420], [312,11321],
[273,11189], [233,11066], [200,10995], [160,10942], [104,10820], [0,10568], [25,10510], [50,10420],
[65,10312], [271,10108], [368,10004], [438,9909], [517,9809], [569,9741], [600,9666], [615,9600],
[630,9567], [649,9526], [679,9385], [670,9245], [650,9187], [635,9113], [644,8985], [673,8938], [694,8846],
[740,8745], [770,8678], [780,8635], [771,8528], [745,8449], [738,8403], [807,8364], [872,8298], [894,8264],
[1090,8076], [1270,7877], [1366,7798], [1440,7679], [1495,7596], [1543,7541], [1560,7487], [1575,7447],
[1576,7350], [1536,7234], [1521,7168], [1587,7184], [1761,7129], [1838,7050], [1893,7050], [1995,6995],
[2081,6940], [2109,7006], [2100,7045], [2100,7090], [2109,7155], [2115,7210], [2100,7269], [2124,7334],
[2179,7365], [2209,7391], [2242,7362], [2308,7311], [2280,7215], [2220,7164], [2210,7150], [2200,7095],
[2200,7040], [2234,7040], [2274,6932], [2415,6775], [2459,6691], [2483,6578], [2558,6497], [2610,6449],
[2598,6430], [2490,6475], [2444,6500], [2410,6515], [2406,6530], [2375,6570], [2305,6610], [2224,6638],
[2225,6806], [2211,6867], [2159,6913], [2109,6912], [2075,6810], [2074,6583], [2068,6521], [2104,6503],
[2140,6454], [2153,6417], [2184,6336], [2187,6243], [2173,6158], [2213,6065], [2250,6005], [2283,5970],
[2343,5928], [2370,5875], [2428,5822], [2485,5779], [2606,5782], [2728,5785], [2772,5725], [2850,5561],
[2839,5472], [2820,5391], [2797,5322], [2734,5321], [2676,5330], [2640,5289], [2656,5236], [2661,5205],
[2671,5144], [2712,5083], [2720,4973], [2738,4882], [2806,4819], [2891,4780], [2966,4737], [3004,4662],
[3043,4604], [3080,4542], [3128,4491], [3170,4453], [3294,4262], [3370,4150], [3384,4090], [3402,4057],
[3442,4029], [3602,3909], [3753,3777], [3855,3600], [3830,3521], [3900,3425], [3957,3394], [4000,3390],
[4045,3393], [4109,3315], [4121,3235], [4089,3125], [4074,3085], [4081,3019], [4098,2923], [4116,2848],
[4160,2774], [4135,2734], [4116,2697], [4100,2645], [4123,2585], [4208,2558], [4272,2478], [4367,2441],
[4453,2461], [4533,2485], [4635,2488], [4795,2472], [4875,2450], [4896,2425], [4933,2402], [4988,2404],
[5036,2409], [5124,2401], [5388,2338], [5479,2252], [5552,2199], [5610,2121], [5658,2051], [5802,1986],
[5858,1955], [5994,1930], [6110,1905], [6122,1880], [6174,1895], [6309,1910], [6447,1875], [6510,1797],
[6532,1730], [6525,1594], [6531,1521], [6615,1490], [6697,1525], [6729,1555], [6812,1543], [6901,1506],
[7084,1351], [7193,1305], [7250,1205], [7255,1155], [7305,1152], [7420,1087], [7522,995], [7751,608],
[7771,455], [7780,278], [7856,218], [7914,192], [7928,143], [7860,170], [7910,80], [7931,39], [7944,-2],
[8009,4], [8490,46], [10165,189], [10542,220], [10575,240], [10654,239], [10701,218], [10735,242],
[10776,308], [10811,372], [10834,527], [10781,598], [10685,625], [10595,763], [10588,913], [10553,965],
[10572,995], [10585,1062], [10617,1120], [10672,1153], [10755,1340], [10781,1436], [10811,1637],
[10791,1768], [10771,1807], [10824,1852], [10927,2015], [10995,2073], [11184,2212], [11204,2270],
[11174,2312], [11045,2430], [10931,2585], [10881,2678], [10806,2818], [10739,2936], [10670,3102],
[10670,3166], [4823,8540], [4804,12775], [4798,12800], [2515,12800], [232,12800]
];
newpoly = simplify_path(calif, 120, closed=true);
left(4000) polygon(calif);
right(4000) color("lightgreen") polygon(newpoly);
```

#### a = sinh(x);

Returns the hyperbolic sine of the given value.

```openscad
// sinh — Returns the hyperbolic sine of the given value.
// Usage: a = sinh(x);
```

#### skew([sxy=]|[axy=], [sxz=]|[axz=], [syx=]|[ayx=], [syz=]|[ayz=], [szx=]|[azx=], [szy=]|[azy=]) CHILDREN;

Skews (or shears) children along various axes.

```openscad
skew(sxy=0.5) square(40, center=true);
```

#### skin(profiles, slices, [z=], [refine=], [method=], [sampling=], [caps=], [closed=], [style=], [convexity=], [anchor=],[cp=],[spin=],[orient=],[atype=]) [ATTACHMENTS];

Connect a sequence of arbitrary polygons into a 3D object.

```openscad
skin([octagon(4), circle($fn=70,r=2)], z=[0,3], slices=10);
```

#### interp_vector = slerp(v1, v2, u);

Spherical lerp(), great-circle interpolation on a unit sphere.

```openscad
// slerp — Spherical lerp(), great-circle interpolation on a unit sphere.
// Usage: interp_vector = slerp(v1, v2, u);
```

#### vec_list = slerpn(v1, v2, n);

Spherical lerpn(), returns exactly `n` vectors interpolated on a great circle.

```openscad
radius = 40;
interps = slerpn([-1,-1,0],[1,0.5,1], 7);
stroke(interps*radius, dots=true, width=2);
%sphere(radius);
```

#### list = slice(list, s, e);

Returns part of a list without wrapping.

```openscad
a = slice([3,4,5,6,7,8,9], 3, 5);   // Returns [6,7,8]
b = slice([3,4,5,6,7,8,9], 2, -1);  // Returns [5,6,7,8,9]
c = slice([3,4,5,6,7,8,9], 1, 1);   // Returns [4]
d = slice([3,4,5,6,7,8,9], 5);      // Returns [8,9]
e = slice([3,4,5,6,7,8,9], 2, -2);  // Returns [5,6,7,8]
f = slice([3,4,5,6,7,8,9], 4, 3;    // Returns []
g = slice([3,4,5], 1, 5;            // Returns [4,5]
h = slice([3,4,5], 5, 7);           // Returns []
```

#### profs = slice_profiles(profiles, slices, [closed]);

Linearly interpolates between path profiles.

```openscad
// slice_profiles — Linearly interpolates between path profiles.
// Usage: profs = slice_profiles(profiles, slices, [closed]);
```

#### slider(l, w, h, [base=], [wall=], [ang=], [$slop=]) [ATTACHMENTS];

Creates a V-groove slider.

```openscad
slider(l=30, base=10, wall=4, $slop=0.2);
```

#### smoothed = smooth_path(path, [tangents], [size=|relsize=], [method="edges"], [splinesteps=], [closed=], [uniform=]);

Create a smoothed path passing through all the points of a given path, or passing through all the segment midpoint tangents.

```openscad
color("green")stroke(square(4), width=0.06);
stroke(smooth_path(square(4),size=0.4), width=0.1);
stroke(smooth_path(square(4),method="corners",size=0.4),
color="red", width=0.1);
```

#### snap_lock(thick, [snaplen=], [snapdiam=], [layerheight=], [foldangle=], [hingegap=], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];

Creates a snap-lock shape.

```openscad
snap_lock(thick=3, foldangle=60);
```

#### snap_pin(size, [pointed=], [anchor=], [spin=], [orient]=) [ATTACHMENTS];

Creates a snap-pin that can slot into a {{snap_pin_socket()}} to join two parts.

```openscad
snap_pin("standard", anchor=CENTER, orient=UP, thickness = 1, $fn=40);
```

#### snap_pin_socket(size, [fixed=], [fins=], [pointed=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];

Creates a snap-pin socket for a {{snap_pin()}} to slot into.

```openscad
snap_pin_socket("standard", anchor=CENTER, orient=UP, fins=true, $fn=40);
```

#### snap_socket(thick, [snaplen=], [snapdiam=], [layerheight=], [foldangle=], [hingegap=], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];

Creates a snap-lock socket shape.

```openscad
snap_socket(thick=3, foldangle=60);
```

#### slist = sort(list, [idx]);

Returns a sorted list.

```openscad
// Homogeneous lists
l1 = [45,2,16,37,8,3,9,23,89,12,34];
sorted1 = sort(l1);  // Returns [2,3,8,9,12,16,23,34,37,45,89]
l2 = [["oat",0], ["cat",1], ["bat",3], ["bat",2], ["fat",3]];
sorted2 = sort(l2); // Returns: [["bat",2],["bat",3],["cat",1],["fat",3],["oat",0]]
// Non-homegenous list
l3 = [[4,0],[7],[3,9],20,[4],[3,1],[8]];
sorted3 = sort(l3); // Returns: [20,[3,1],[3,9],[4],[4,0],[7],[8]]
```

#### idxlist = sortidx(list, [idx]);

Returns a list of sorted indices into a list.

```openscad
lst = ["d","b","e","c"];
idxs = sortidx(lst);  // Returns: [1,3,0,2]
ordered = select(lst, idxs);   // Returns: ["b", "c", "d", "e"]
```

#### sp_cap(diam, type, wall, [style=], [top_adj=], [bot_adj=], [texture=], [$slop]) [ATTACHMENTS];

Creates an SPI threaded bottle cap.

```openscad
sp_cap(48,400,2);
sp_cap(22,400,2);
sp_cap(22,410,2);
sp_cap(28,415,1.5,style="M");
```

#### true_diam = sp_diameter(diam,type)

Returns the base diameter of an SPI bottle neck from the nominal diameter and type number.

```openscad
// sp_diameter — Returns the base diameter of an SPI bottle neck from the nominal diameter and type number.
// Usage: true_diam = sp_diameter(diam,type)
```

#### sp_neck(diam, type, wall|id=, [style=], [bead=]) [ATTACHMENTS];

Creates an SPI threaded bottle neck.

```openscad
sp_neck(48,400,2);
sp_neck(48,400,2,bead=true);
sp_neck(22,410,2);
sp_neck(22,410,2,bead=true);
sp_neck(28,415,id=20,style="M");
sp_neck(13,415,wall=1,style="M",bead=true);
```

#### sparse_cuboid(size, [dir], [maxang=], [struct=]

Makes an open cross-braced cuboid

```openscad
sparse_cuboid([10,20,30], strut=1);
sparse_cuboid([10,20,30], "Y", strut=1);
sparse_cuboid([10,20,30], UP, strut=1);
sparse_cuboid(30, FWD, strut=2, rounding=2, $fn=24);
```

#### sparse_wall(h, l, thick, [maxang=], [strut=], [max_bridge=]) [ATTACHMENTS];

Makes an open cross-braced rectangular wall.

```openscad
sparse_wall(h=40, l=100, thick=3);
```

#### sparse_wall2d(size, [maxang=], [strut=], [max_bridge=]) [ATTACHMENTS];

Makes an open cross-braced rectangular wall.

```openscad
sparse_wall2d(size=[40,100]);
```

#### sphere(r|d=);

Creates an attachable spherical object.

```openscad
sphere(r=50);
```

#### sphere_copies(n, r|d=, [cone_ang=], [scale=], [perp=]) CHILDREN;

Distributes copies of children over the surface of a sphere.

```openscad
sphere_copies(n=250, d=100, cone_ang=45, scale=[3,3,1])
cylinder(d=10, h=10, center=false);
```

#### isect = sphere_line_intersection(r|d=, cp, line, [bounded], [eps=]);

Find intersection between a sphere and line, ray or segment.

```openscad
cp = [10,20,5];  r = 40;
line = [[-50,-10,25], [70,0,40]];
isects = sphere_line_intersection(r=r, cp=cp, line=line);
color("cyan") stroke(line);
move(cp) sphere(r=r, $fn=72);
color("red") move_copies(isects) sphere(d=3, $fn=12);
```

#### points = spherical_random_points([n], [radius], [seed]);

Returns a list of random points on the surface of a sphere.

```openscad
// spherical_random_points — Returns a list of random points on the surface of a sphere.
// Usage: points = spherical_random_points([n], [radius], [seed]);
```

#### pt = spherical_to_xyz(r, theta, phi);

Convert spherical coordinates to 3D cartesian coordinates.

```openscad
xyz = spherical_to_xyz(20,30,40);
xyz = spherical_to_xyz([40,60,50]);
xyzs = spherical_to_xyz([[40,60,50], [50,120,100]]);
```

#### spheroid(r|d, [circum], [style]) [ATTACHMENTS];

Creates an attachable spherical object with controllable triangulation.

```openscad
spheroid(r=50);
```

#### spiral_sweep(poly, h, r|d=, turns, [taper=], [center=], [taper1=], [taper2=], [internal=], ...)[ATTACHMENTS];

Sweep a path along a helix.

```openscad
poly = [[-10,0], [-3,-5], [3,-5], [10,0], [0,-30]];
spiral_sweep(poly, h=200, r=50, turns=3, $fn=36);
```

#### paths = split_path_at_self_crossings(path, [closed], [eps]);

Split a 2D {{path}} wherever it crosses itself.

```openscad
path = [ [-100,100], [0,-50], [100,100], [100,-100], [0,50], [-100,-100] ];
paths = split_path_at_self_crossings(path);
rainbow(paths) stroke($item, closed=false, width=3);
```

#### split_region = split_region_at_region_crossings(region1, region2, [closed1], [closed2], [eps])

Splits {{regions}} where {{polygons}} touch and at intersections.

```openscad
path = square(50,center=false);
region = [circle(d=80), circle(d=40)];
paths = split_region_at_region_crossings(path, region);
color("#aaa") region(region);
rainbow(paths[0][0]) stroke($item, width=2);
right(110){
color("#aaa") region([path]);
rainbow(flatten(paths[1])) stroke($item, width=2);
}
```

#### spur_gear(circ_pitch, teeth, [thickness], [helical=], [pressure_angle=], [profile_shift=], [backlash=], [shaft_diam=], [hide=], [clearance=], [slices=], [internal=], [herringbone=]) [ATTACHMENTS];

Creates a spur gear, helical gear, or internal ring gear.

```openscad
spur_gear(circ_pitch=5, teeth=20, thickness=8, shaft_diam=5);
```

#### spur_gear2d(circ_pitch, teeth, [pressure_angle=], [profile_shift=], [shorten=], [hide=], [shaft_diam=], [clearance=], [backlash=], [internal=]) [ATTACHMENTS];

Creates a 2D spur gear or internal ring gear.

```openscad
spur_gear2d(circ_pitch=5, teeth=20, shaft_diam=5);
```

#### x2 = sqr(x);

Returns the square of the given value.

```openscad
sqr(3);     // Returns: 9
sqr(-4);    // Returns: 16
sqr([2,3,4]); // Returns: 29
sqr([[1,2],[3,4]]);  // Returns [[7,10],[15,22]]
```

#### square(size, [center], ...);

Creates a 2D square or rectangle.

```openscad
square(40);
```

#### square_threaded_nut(nutwidth, id, h|height|thickness, pitch, ...) [ATTACHMENTS];

Creates a square-threaded nut.

```openscad
square_threaded_nut(nutwidth=16, id=10, h=10, pitch=2, starts=2, $slop=0.1, $fn=32);
```

#### square_threaded_rod(d, l|length, pitch, [internal=], ...) [ATTACHMENTS];

Creates a square-threaded rod.

```openscad
projection(cut=true)
square_threaded_rod(d=10, l=15, pitch=2, orient=BACK);
```

#### squircle(size, [squareness], [style=]) [ATTACHMENTS];

Creates a shape between a circle and a square.

```openscad
// squircle — Creates a shape between a circle and a square.
// Usage: squircle(size, [squareness], [style=]) [ATTACHMENTS];
```

#### star(n, r/or, ir, [realign=], [align_tip=], [align_pit=], ...) [ATTACHMENTS];

Creates a star-shaped polygon or returns a star-shaped region.

```openscad
star(n=5, r=50, ir=25);
star(n=5, r=50, step=2);
star(n=7, r=50, step=2);
star(n=7, r=50, step=3);
```

#### bool = starts_with(str,pattern);

Returns true if the string starts with a given substring.

```openscad
b1=starts_with("abcdef","abc");  // Returns true
b2=starts_with("abcdef","def");  // Returns false
b3=starts_with("abcdef","");     // Returns true
```

#### ind = str_find(str,pattern,[last=],[all=],[start=]);

Finds a substring in a string.

```openscad
a=str_find("abc123def123abc","123");   // Returns 3
b=str_find("abc123def123abc","b");     // Returns 1
c=str_find("abc123def123abc","1234");  // Returns undef
d=str_find("abc","");                  // Returns 0
e=str_find("abc123def123", "123", start=4);     // Returns 9
f=str_find("abc123def123abc","123",last=true);  // Returns 9
g=str_find("abc123def123abc","b",last=true);    // Returns 13
h=str_find("abc123def123abc","1234",last=true); // Returns undef
i=str_find("abc","",last=true);                 // Returns 3
j=str_find("abc123def123", "123", start=8, last=true));  // Returns 3
k=str_find("abc123def123abc","123",all=true);   // Returns [3,9]
l=str_find("abc123def123abc","b",all=true);     // Returns [1,13]
m=str_find("abc123def123abc","1234",all=true);  // Returns []
n=str_find("abc","",all=true);                  // Returns [0,1,2]
```

#### str = str_join(list, [sep]);

Joints a list of strings into a single string.

```openscad
s1=str_join(["abc","def","ghi"]);         // Returns "abcdefghi"
s2=str_join(["abc","def","ghi"], " + ");  // Returns "abc + def + ghi"
```

#### padded = str_pad(str, length, char, [left]);

Pads a string to a given length.

```openscad
s1=str_pad("hello", 10, "*");            // Returns: "hello*****"
s2=str_pad("hello", 10, "*", left=true); // Returns: "*****hello"
```

#### newstr = str_replace_char(str, char, replace);

Replace specified character in a string with a string.

```openscad
s1 = str_replace_char("abcdcba","c","_123_");     // Returns: "ab123d123ba"
s2 = str_replace_char(" s t r i n g ", " ", "");  // Returns: "string"
```

#### string_list = str_split(str, sep, [keep_nulls]);

Splits a longer string wherever a given substring occurs.

```openscad
s1=str_split("abc+def-qrs*iop","*-+");     // Returns ["abc", "def", "qrs", "iop"]
s2=str_split("abc+*def---qrs**iop+","*-+");// Returns ["abc", "", "def", "", "", "qrs", "", "iop", ""]
s3=str_split("abc      def"," ");          // Returns ["abc", "", "", "", "", "", "def"]
s4=str_split("abc      def"," ",keep_nulls=false); // Returns ["abc", "def"]
s5=str_split("abc+def-qrs*iop",["+","-","*"]);     // Returns ["abc", "def", "qrs", "iop"]
s6=str_split("abc+def-qrs*iop",["-","+","*"]);     // Returns ["abc+def", "qrs*iop", "", ""]
```

#### str = str_strip(s,c,[start],[end]);

Strips given leading and trailing characters from a string.

```openscad
s1=str_strip("--##--123--##--","#-");  // Returns: "123"
s2=str_strip("--##--123--##--","-");   // Returns: "##--123--##"
s3=str_strip("--##--123--##--","#");   // Returns: "--##--123--##--"
s4=str_strip("--##--123--##--","#-",end=true);   // Returns: "--##--123"
s5=str_strip("--##--123--##--","-",end=true);    // Returns: "--##--123--##"
s6=str_strip("--##--123--##--","#",end=true);    // Returns: "--##--123--##--"
s7=str_strip("--##--123--##--","#-",start=true); // Returns: "123--##--"
s8=str_strip("--##--123--##--","-",start=true);  // Returns: "##--123--##--"
s9=str_strip("--##--123--##--","#",start=true);  // Returns: "--##--123--##--"
```

#### stroke(path, [width], [closed], [endcaps], [endcap_width], [endcap_length], [endcap_extent], [trim]);

Draws a line along a path or region boundary.

```openscad
path = [[0,100], [100,100], [200,0], [100,-100], [100,0]];
stroke(path, width=20);
```

#### keys = struct_keys(struct);

Returns a list of keys for a struct.

```openscad
// struct_keys — Returns a list of keys for a struct.
// Usage: keys = struct_keys(struct);
```

#### struct2 = struct_remove(struct, key);

Removes one or more keys from a struct.

```openscad
// struct_remove — Removes one or more keys from a struct.
// Usage: struct2 = struct_remove(struct, key);
```

#### struct2 = struct_set(struct, key, value, [grow=]);

Sets one or more key-value pairs in a struct.

```openscad
some_struct = struct_set([], "answer", 42);
// 'some_struct' now contains a single value, 42, under one key, "answer".
```

#### val = struct_val(struct, key, default);

Returns the value for an key in a struct.

```openscad
// struct_val — Returns the value for an key in a struct.
// Usage: val = struct_val(struct, key, default);
```

#### newprof = subdivide_and_slice(profiles, slices, [numpoints], [method], [closed]);

Resample list of paths to have the same point count and interpolate additional paths.

```openscad
// subdivide_and_slice — Resample list of paths to have the same point count and interpolate additional paths.
// Usage: newprof = subdivide_and_slice(profiles, slices, [numpoints], [method], [closed]);
```

#### newpath = subdivide_path(path, n|refine=|maxlen=, [method=], [closed=], [exact=]);

Subdivides a path to produce a more finely sampled path.

```openscad
mypath = subdivide_path(square([2,2],center=true), 12);
move_copies(mypath)circle(r=.1,$fn=32);
```

#### mat = submatrix(M, idx1, idx2);

Extract a submatrix from a matrix

```openscad
M = [[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 9,10],
[11,12,13,14,15],
[16,17,18,19,20],
[21,22,23,24,25]];
submatrix(M,[1:2],[3:4]);  // Returns [[9, 10], [14, 15]]
submatrix(M,[1], [3,4]));  // Returns [[9,10]]
submatrix(M,1, [3,4]));  // Returns [[9,10]]
submatrix(M,1,3));  // Returns [[9]]
submatrix(M, [3,4],1); // Returns  [[17],[22]]);
submatrix(M, [1,3],[2,4]); // Returns [[8,10],[18,20]]);
A = [[true,    17, "test"],
[[4,2],   91, false],
[6,    [3,4], undef]];
submatrix(A,[0,2],[1,2]);   // Returns [[17, "test"], [[3, 4], undef]]
```

#### mat = submatrix_set(M, A, [m], [n]);

Takes a matrix as input and change values in a submatrix.

```openscad
// submatrix_set — Takes a matrix as input and change values in a submatrix.
// Usage: mat = submatrix_set(M, A, [m], [n]);
```

#### newstr = substr(str, [pos], [len]);

Returns a substring from a string.

```openscad
s1=substr("abcdefg",3,3);     // Returns "def"
s2=substr("abcdefg",2);       // Returns "cdefg"
s3=substr("abcdefg",len=3);   // Returns "abc"
s4=substr("abcdefg",[2,4]);   // Returns "cde"
s5=substr("abcdefg",len=-2);  // Returns ""
```

#### substr_match(...)

Returns true if the string `pattern` matches the string `str`.

```openscad
a=substr_match("abcde",2,"cd");   // Returns true
b=substr_match("abcde",2,"cx");   // Returns false
c=substr_match("abcde",2,"cdef"); // Returns false
d=substr_match("abcde",-2,"cd");  // Returns false
e=substr_match("abcde",19,"cd");  // Returns false
f=substr_match("abc",1,"");       // Returns true
```

#### newstr = suffix(str,len);

Returns the last few characters of a string.

```openscad
// suffix — Returns the last few characters of a string.
// Usage: newstr = suffix(str,len);
```

#### x = sum(v, [dflt]);

Returns the sum of a list of values.

```openscad
sum([1,2,3]);  // returns 6.
sum([[1,2,3], [3,4,5], [5,6,7]]);  // returns [9, 12, 15]
```

#### sum_of_sines(a,sines)

Returns the sum of one or more sine waves at a given angle.

```openscad
v = sum_of_sines(30, [[10,3,0], [5,5.5,60]]);
```

#### supershape([step],[n=], [m1=], [m2=], [n1=], [n2=], [n3=], [a=], [b=], [r=/d=]) [ATTACHMENTS];

Creates a 2D [Superformula](https://en.wikipedia.org/wiki/Superformula) shape.

```openscad
supershape(step=0.5,m1=16,m2=16,n1=0.5,n2=0.5,n3=16,r=50);
```

#### normals = surface_normals(surf, [col_wrap=], [row_wrap=]);

Estimates the normals to a surface defined by a {{point}} array

```openscad
// surface_normals — Estimates the normals to a surface defined by a {{point}} array
// Usage: normals = surface_normals(surf, [col_wrap=], [row_wrap=]);
```

#### M = swap_columns(M, i, j);

Swap two columns of a matrix

```openscad
M = [
[1, 2, 3, 0, 1], 
[4, 5, 6, 0, 1], 
[7, 8, 9, 0, 1]
];
S = swap_columns(M,1,3);
// Returns
// [  
//   [1, 0, 3, 2, 1], 
//   [4, 0, 6, 5, 1], 
//   [7, 0, 9, 8, 1]
// ]
```

#### sweep(shape, transforms, [closed], [caps], [style], [convexity=], [anchor=], [spin=], [orient=], [atype=]) [ATTACHMENTS];

Construct a 3d object from arbitrary transformations of a 2d polygon path.

```openscad
radius = 75;
angle = 40;
shape = circle(r=5,$fn=32);
T = [for(i=[0:25]) xrot(-angle*i/25,cp=[0,radius,0])*scale([1+i/25, 2-i/25,1])];
sweep(shape,T);
```

#### path_sweep(...) { sweep_attach(parent, [child], [frac], [idx=], [len=], [spin=], [overlap=], [atype=]) CHILDREN; }

Attach children to sides of a path_sweep parent object

```openscad
function a(h) = arc(points=[[-20,0],[0,h],[20,0]],n=24);
shape = concat(
a(2), // bottom
back(6,reverse(a(4))) // top
);
path = xrot(90,path3d(arc(points=[[-40,0],[0,5],[40,-20]],n=36)));
path_sweep(shape,path) {
sweep_attach(BACK,BOT,0.2) recolor("red") cyl(d1=5,d2=0,h=8,$fn=12);
sweep_attach(BACK,TOP,0.5,overlap=3) recolor("blue") cyl(d1=5,d2=0,h=8,$fn=12);
sweep_attach(RIGHT,BOT,idx=15) recolor("orange") cuboid([3,3,5]);
sweep_attach(RIGHT,BOT,idx=1) recolor("orange") cuboid([3,3,5]);
sweep_attach(RIGHT,BOT,idx=32) recolor("orange") cuboid([3,3,5]);        
}
```

#### PARENT() tag(tag) CHILDREN;

Assigns a tag to an object

```openscad
diff("remove")
cuboid(10){
position(TOP) cuboid(3);
tag("remove")
{
position(FRONT) cuboid(3);
position(RIGHT) cuboid(3);
}
}
```

#### tag_conv_hull([tag], [keep]) CHILDREN;

Performs a {{conv_hull()}} and then sets a tag on the result.

```openscad
diff(){
cuboid([30,30,9])
tag("remove")conv_hull("remove")
cuboid(10,anchor=LEFT+FRONT){
position(RIGHT+BACK)cyl(r=4,h=10);
tag("keep")position(FRONT+LEFT)cyl(r=4,h=10);
}
}
```

#### tag_diff([tag], [remove], [keep]) PARENT() CHILDREN;

Performs a {{diff()}} and then sets a tag on the result.

```openscad
diff("rem"){
cuboid([20,10,30],anchor=FRONT);
tag("rem")diff("remove","keep"){
cuboid([10,10,20]);
tag("remove")cuboid([11,11,5]);
tag("keep")cuboid([2,2,20]);
}
}
```

#### tag_intersect([tag], [intersect], [keep]) PARENT() CHILDREN;

Performs an {{intersect()}} and then tags the result.

```openscad
$fn=32;
diff()
cuboid([20,15,9])
tag("remove")intersect()
cuboid(10){
tag("intersect")position(RIGHT) cyl(r=7,h=10);
tag("keep")position(LEFT)cyl(r=4,h=10);
}
```

#### tag_scope([scope]) CHILDREN;

Creates a new tag scope.

```openscad
module myring(){
attachable(anchor=CENTER, spin=0, d=60, l=60) {
tag_scope()
diff()
cyl(d=60, l=60){
tag("remove")
color_this("lightblue")
cyl(d=30, l=61);
}      
children();
}
}
diff()
myring()
color_this("green") cyl(d=20, l=61)
tag("remove") color_this("yellow") cyl(d=10, l=65);
```

#### PARENT() tag(tag) CHILDREN;

Assigns a tag to an object at the current level only.

```openscad
diff()
cuboid([10,10,4])
tag_this("remove")position(TOP) cuboid(3)  // This cube is subtracted
attach(TOP,BOT) cuboid(1);  // Tag is reset so this cube displays
```

#### a = tanh(x);

Returns the hyperbolic tangent of the given value.

```openscad
// tanh — Returns the hyperbolic tangent of the given value.
// Usage: a = tanh(x);
```

#### teardrop(h|l=|length=|height=, r, [ang], [cap_h], [chamfer=], [bot_corner=], ...) [ATTACHMENTS];

Creates a teardrop shape.

```openscad
teardrop(r=30, h=10, ang=30);
```

#### teardrop_corner_mask(r|d=, [angle], [excess], [anchor], [spin], [orient]) [ATTACHMENTS];

Creates a shape to round a 90° corner but limit the angle of overhang.

```openscad
teardrop_corner_mask(r=20, angle=40);
```

#### teardrop_edge_mask(l|h=|length=|height=, r|d=, [angle], [excess], [anchor], [spin], [orient]) [ATTACHMENTS];

Creates a shape to round an edge while limiting the angle of overhang.

```openscad
teardrop_edge_mask(l=20, r=10, angle=40);
```

#### teardrop2d(r/d=, [ang], [cap_h], [circum=], [realign=], [bot_corner=]) [ATTACHMENTS];

Creates a 2D teardrop shape.

```openscad
teardrop2d(r=30, ang=30);
```

#### text(text, [size], [font], ...);

Creates an attachable block of text.

```openscad
text("Foobar", size=10);
text("Foobar", size=12, font="Liberation Mono");
text("Foobar", anchor=CENTER);
text("Foobar", anchor=str("baseline",CENTER));
```

#### text3d(text, [h], [size], [font], [language=], [script=], [direction=], [atype=], [anchor=], [spin=], [orient=]);

Creates an attachable 3d text block.

```openscad
text3d("Fogmobar", h=3, size=10);
text3d("Fogmobar", h=2, size=12, font=":style=bold");
text3d("Fogmobar", h=2, anchor=CENTER);
text3d("Fogmobar", h=2, anchor=CENTER, atype="ycenter");
text3d("Fogmobar", h=2, anchor=RIGHT);
text3d("Fogmobar", h=2, anchor=RIGHT+BOT, atype="ycenter");
```

#### tx = texture(tex, [n=], [inset=], [gap=], [roughness=]);

Produce a standard texture.

```openscad
tex = texture("bricks");
linear_sweep(
rect(30), texture=tex, h=30,
tex_depth=1/2, tex_size=[10,10]
);
```

#### textured_tile(texture, [size], [w1=], [w2=], [ang=], [shift=], [h=/height=/thickness=], [atype=], [diff=], [tex_extra=], [tex_skip=], ...) [ATTACHMENTS];

Creates a cube or trapezoidal prism with a textured top face for attaching to objects.

```openscad
textured_tile("trunc_diamonds", 10, tex_reps=[5,5]);
```

#### thinning_triangle(h, l, thick, [ang=], [strut=], [wall=], [diagonly=], [center=]) [ATTACHMENTS];

Makes a triangular wall with a thin middle.

```openscad
thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, center=true);
```

#### thinning_wall(h, l, thick, [ang=], [braces=], [strut=], [wall=]) [ATTACHMENTS];

Makes a rectangular wall with a thin middle.

```openscad
thinning_wall(h=50, l=80, thick=4);
```

#### thread_helix(d, pitch, turns=, [thread_depth=], [thread_angle=|flank_angle=], [profile=], [starts=], [internal=], ...) {ATTACHMENTS};

Creates a thread helix to add to a cylinder.

```openscad
pitch = 2;
depth = pitch * cos(30) * 5/8;
profile = [
[-6/16, 0           ],
[-1/16, depth/pitch ],
[ 1/16, depth/pitch ],
[ 6/16, 0           ],
];
stroke(profile, width=0.02);
```

#### thread_specification(screw_spec, [tolerance], [internal])

Returns the thread geometry for a given screw.

```openscad
// thread_specification — Returns the thread geometry for a given screw.
// Usage: thread_specification(screw_spec, [tolerance], [internal])
```

#### threaded_nut(nutwidth, id, h|height|thickness, pitch,...) [ATTACHMENTS];

Creates an UTS/ISO triangular threaded nut.

```openscad
threaded_nut(nutwidth=16, id=8, h=8, pitch=1.25, $slop=0.05, $fa=1, $fs=1);
threaded_nut(nutwidth=16, id=8, h=8, pitch=1.25, left_handed=true, bevel=false, $slop=0.1, $fa=1, $fs=1);
threaded_nut(shape="square", nutwidth=16, id=8, h=8, pitch=1.25, $slop=0.1, $fa=1, $fs=1);
threaded_nut(shape="square", nutwidth=16, id=8, h=8, pitch=1.25, bevel2=true, $slop=0.1, $fa=1, $fs=1);
rot(90)threaded_nut(nutwidth=16, id=8, h=8, pitch=1.25,blunt_start=false, $slop=0.1, $fa=1, $fs=1);
```

#### threaded_rod(d, l|length, pitch, [internal=], ...) [ATTACHMENTS];

Creates an UTS/ISO triangular threaded rod.

```openscad
projection(cut=true)
threaded_rod(d=10, l=15, pitch=1.5, orient=BACK);
```

#### tilt(to, [reverse=], [cp=]) CHILDREN;

Tilts children toward a direction

```openscad
#cube([2,4,9]);
tilt(LEFT+BACK) cube([2,4,9]);
```

#### top_half([s], [z]) CHILDREN;

Masks the bottom half of an object along the X-Y plane, leaving the top half.

```openscad
top_half() sphere(r=20);
top_half(z=5) sphere(r=20);
```

#### torus(r_maj|d_maj, r_min|d_min, [center], ...) [ATTACHMENTS];

Creates an attachable torus.

```openscad
// These all produce the same torus.
torus(r_maj=22.5, r_min=7.5);
torus(d_maj=45, d_min=15);
torus(or=30, ir=15);
torus(od=60, id=30);
torus(d_maj=45, id=30);
torus(d_maj=45, od=60);
torus(d_min=15, id=30);
torus(d_min=15, od=60);
vnf_polyhedron(torus(d_min=15, od=60), convexity=4);
```

#### depth = torx_depth(size);

Returns the typical depth of a torx drive recess.

```openscad
// torx_depth — Returns the typical depth of a torx drive recess.
// Usage: depth = torx_depth(size);
```

#### diam = torx_diam(size);

Returns the diameter of a torx drive.

```openscad
// torx_diam — Returns the diameter of a torx drive.
// Usage: diam = torx_diam(size);
```

#### info = torx_info(size);

Returns the dimensions of a torx drive.

```openscad
// torx_info — Returns the dimensions of a torx drive.
// Usage: info = torx_info(size);
```

#### torx_mask(size, l, [center]) [ATTACHMENTS];

Creates a mask for a torx drive recess.

```openscad
torx_mask(size=30, l=10, $fa=1, $fs=1);
```

#### torx_mask2d(size);

Creates the 2D cross section for a torx drive recess.

```openscad
torx_mask2d(size=30, $fa=1, $fs=1);
```

#### new_desc = transform_desc(T, desc);

Applies a transformation matrix to a description

```openscad
// transform_desc — Applies a transformation matrix to a description
// Usage: new_desc = transform_desc(T, desc);
```

#### M = transpose(M, [reverse]);

Transpose a matrix

```openscad
M = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
t = transpose(M);
// Returns:
// [
//     [1, 4, 7], 
//     [2, 5, 8], 
//     [3, 6, 9]
// ]
```

#### trapezoid(h, w1, w2, [shift=], [rounding=], [chamfer=], [flip=], ...) [ATTACHMENTS];

Creates a trapezoid with parallel top and bottom sides.

```openscad
trapezoid(h=30, w1=40, w2=20);
trapezoid(h=25, w1=20, w2=35);
trapezoid(h=20, w1=40, w2=0);
trapezoid(h=20, w1=30, ang=60);
trapezoid(h=20, w1=20, ang=120);
trapezoid(h=20, w2=10, ang=60);
trapezoid(h=20, w1=50, ang=[40,60]);
trapezoid(w1=30, w2=10, ang=[30,90]);
```

#### trapezoidal_threaded_nut(nutwidth, id, h|height|thickness, pitch, [thread_angle=|flank_angle=], [thread_depth], ...) [ATTACHMENTS];

Creates a trapezoidal threaded nut.

```openscad
trapezoidal_threaded_nut(nutwidth=16, id=8, h=8, pitch=2, $slop=0.1, anchor=UP);
trapezoidal_threaded_nut(nutwidth=16, id=8, h=8, pitch=2, bevel=false, $slop=0.05, anchor=UP);
trapezoidal_threaded_nut(nutwidth=17.4, id=10, h=10, pitch=2, $slop=0.1, left_handed=true);
trapezoidal_threaded_nut(nutwidth=17.4, id=10, h=10, pitch=2, starts=3, $fa=1, $fs=1, $slop=0.15);
trapezoidal_threaded_nut(nutwidth=17.4, id=10, h=10, pitch=2, starts=3, $fa=1, $fs=1, $slop=0.15, blunt_start=false);
trapezoidal_threaded_nut(nutwidth=17.4, id=10, h=10, pitch=0, $slop=0.2);   // No threads
```

#### trapezoidal_threaded_rod(d, l|length, pitch, [thread_angle=|flank_angle=], [thread_depth=], [internal=], ...) [ATTACHMENTS];

Creates a trapezoidal threaded rod.

```openscad
projection(cut=true)
trapezoidal_threaded_rod(d=10, l=15, pitch=2, orient=BACK);
```

#### list = triplet(list, [wrap]);

Returns a list of overlapping consecutive triplets in a list.

```openscad
list = [0,1,2,3,4];
a = triplet(list);               // Returns [[0,1,2],[1,2,3],[2,3,4]]
b = triplet(list,wrap=true);     // Returns [[4,0,1],[0,1,2],[1,2,3],[2,3,4],[3,4,0]]
letters = ["A","B","C","D","E"];
[for (p=triplet(letters)) str(p.z,p.y,p.x)];     // Returns: ["CBA", "DCB", "EDC"]
```

#### tube(h|l, or, ir, [center|anchor=], [realign=], [spin=],[orient=]) [ATTACHMENTS];

Creates a cylindrical or conical tube.

```openscad
tube(h=30, or=40, wall=5);
tube(h=30, ir=35, wall=5);
tube(h=30, or=40, ir=35);
tube(h=30, od=80, id=70);
```

#### path = turtle(commands, [state], [full_state=], [repeat=])

Uses [turtle graphics](https://en.wikipedia.org/wiki/Turtle_graphics) to generate a 2D path.

```openscad
path = turtle(["xmove",3, "ymove", "xmove",-3, "ymove",-1]);
stroke(path,width=.1);
```

#### path = turtle3d(commands, [state=], [repeat=]);

Extends [turtle graphics](https://en.wikipedia.org/wiki/Turtle_graphics) to 3d. Generates a 3D path or returns a list of transforms.

```openscad
path = turtle3d(["up",25,"move","left","move",3,"left","move"]);
stroke(path,closed=true, width=.2);
```

#### typ = typeof(x);

Returns a string representing the type of the value.

```openscad
typ = typeof(undef);  // Returns: "undef"
typ = typeof(true);  // Returns: "boolean"
typ = typeof(42);  // Returns: "number"
typ = typeof(NAN);  // Returns: "nan"
typ = typeof("foo");  // Returns: "string"
typ = typeof([3,4,5]);  // Returns: "list"
typ = typeof([3:1:8]);  // Returns: "range"
typ = typeof(function (x,y) x+y);  // Returns: "function"
```

#### x = u_add(a, b);

Returns the sum of 2 numbers if both are defined, otherwise returns undef.

```openscad
// u_add — Returns the sum of 2 numbers if both are defined, otherwise returns undef.
// Usage: x = u_add(a, b);
```

#### x = u_div(a, b);

Returns the quotient of 2 numbers if both are defined, otherwise returns undef.

```openscad
// u_div — Returns the quotient of 2 numbers if both are defined, otherwise returns undef.
// Usage: x = u_div(a, b);
```

#### x = u_mul(a, b);

Returns the product of 2 numbers if both are defined, otherwise returns undef.

```openscad
// u_mul — Returns the product of 2 numbers if both are defined, otherwise returns undef.
// Usage: x = u_mul(a, b);
```

#### x = u_sub(a, b);

Returns the difference of 2 numbers if both are defined, otherwise returns undef.

```openscad
// u_sub — Returns the difference of 2 numbers if both are defined, otherwise returns undef.
// Usage: x = u_sub(a, b);
```

#### union() CHILDREN;

Performs a Boolean union operation.

```openscad
shape1 = move([-8,-8,0], p=circle(d=50));
shape2 = move([ 8, 8,0], p=circle(d=50));
color("green") region(union(shape1,shape2));
for (shape = [shape1,shape2])
stroke(shape, width=0.5, closed=true, color="red");
```

#### ulist = unique(list);

Returns a sorted list with all duplicates removed.

```openscad
sorted = unique([5,2,8,3,1,3,8,7,5]);  // Returns: [1,2,3,5,7,8]
sorted = unique("axdbxxc");  // Returns: "abcdx"
sorted = unique([true,2,"xba",[1,0],true,[0,0],3,"a",[0,0],2]); // Returns: [true,2,3,"a","xba",[0,0],[1,0]]
```

#### ulist = unique_approx(data, [eps]);



```openscad
// unique_approx
// Usage: ulist = unique_approx(data, [eps]);
```

#### ulist = unique_approx(data, [eps]);



```openscad
// unique_approx_indexed
// Usage: ulist = unique_approx(data, [eps]);
```

#### sorted_counts = unique_count(list);

Returns a sorted list of unique items with counts.

```openscad
sorted = unique([5,2,8,3,1,3,8,3,5]);  // Returns: [ [1,2,3,5,8], [1,1,3,2,2] ]
```

#### v = unit(v, [error]);

Returns the unit length of a given vector.

```openscad
v1 = unit([10,0,0]);   // Returns: [1,0,0]
v2 = unit([0,10,0]);   // Returns: [0,1,0]
v3 = unit([0,0,10]);   // Returns: [0,0,1]
v4 = unit([0,-10,0]);  // Returns: [0,-1,0]
v5 = unit([0,0,0],[1,2,3]);    // Returns: [1,2,3]
v6 = unit([0,0,0]);    // Asserts an error.
```

#### up(z) CHILDREN;

Translates children upward (Z+).

```openscad
#sphere(d=10);
up(20) sphere(d=10);
```

#### newstr = upcase(str);

Uppercases all characters in a string.

```openscad
s=upcase("ABCdef");   // Returns "ABCDEF"
```

#### v2 = v_abs(v);

Returns the absolute values of the given vector.

```openscad
v_abs([-1,3,-9]);  // Returns: [1,3,9]
```

#### v2 = v_ceil(v);

Returns the values of the given vector, rounded up.

```openscad
// v_ceil — Returns the values of the given vector, rounded up.
// Usage: v2 = v_ceil(v);
```

#### v3 = v_div(v1, v2);

Returns the element-wise division of two equal-length vectors.

```openscad
v_div([24,28,30], [8,7,6]);  // Returns [3, 4, 5]
```

#### v2 = v_floor(v);

Returns the values of the given vector, rounded down.

```openscad
// v_floor — Returns the values of the given vector, rounded down.
// Usage: v2 = v_floor(v);
```

#### v2 = v_lookup(x, v);

Like `lookup()`, but it can interpolate between vector results.

```openscad
x = v_lookup(4.5, [[4, [3,4,5]], [5, [5,6,7]]]);  // Returns: [4,5,6]
```

#### v3 = v_mul(v1, v2);

Returns the element-wise multiplication of two equal-length vectors.

```openscad
v_mul([3,4,5], [8,7,6]);  // Returns [24, 28, 30]
```

#### v2 = v_round(v);

Returns the values of the given vector, rounded to the nearest whole number.

```openscad
// v_round — Returns the values of the given vector, rounded to the nearest whole number.
// Usage: v2 = v_round(v);
```

#### theta = v_theta([X,Y]);

Returns the angle counter-clockwise from X+ on the XY plane.

```openscad
// v_theta — Returns the angle counter-clockwise from X+ on the XY plane.
// Usage: theta = v_theta([X,Y]);
```

#### bool = valid_range(x);

Returns true if the argument is a valid range.

```openscad
bool = is_range(undef);   // Returns: false
bool = is_range(false);   // Returns: false
bool = is_range(42);      // Returns: false
bool = is_range([3,4,5]); // Returns: false
bool = is_range("foo");   // Returns: false
bool = is_range([3:5]);   // Returns: true
bool = is_range([3:1]);   // Returns: false
```

#### ang = vector_angle(v1,v2);

Returns the minor angle between two vectors.

```openscad
ang1 = vector_angle(UP,LEFT);     // Returns: 90
ang2 = vector_angle(RIGHT,LEFT);  // Returns: 180
ang3 = vector_angle(UP+RIGHT,RIGHT);  // Returns: 45
ang4 = vector_angle([10,10], [0,0], [10,-10]);  // Returns: 90
ang5 = vector_angle([10,0,10], [0,0,0], [-10,10,0]);  // Returns: 120
ang6 = vector_angle([[10,0,10], [0,0,0], [-10,10,0]]);  // Returns: 120
```

#### axis = vector_axis(v1,v2);

Returns the perpendicular axis between two vectors.

```openscad
axis1 = vector_axis(UP,LEFT);     // Returns: [0,-1,0] (FWD)
axis2 = vector_axis(RIGHT,LEFT);  // Returns: [0,-1,0] (FWD)
axis3 = vector_axis(UP+RIGHT,RIGHT);  // Returns: [0,1,0] (BACK)
axis4 = vector_axis([10,10], [0,0], [10,-10]);  // Returns: [0,0,-1] (DOWN)
axis5 = vector_axis([10,0,10], [0,0,0], [-10,10,0]);  // Returns: [-0.57735, -0.57735, 0.57735]
axis6 = vector_axis([[10,0,10], [0,0,0], [-10,10,0]]);  // Returns: [-0.57735, -0.57735, 0.57735]
```

#### newv = vector_bisect(v1,v2);

Returns the vector that bisects two vectors.

```openscad
// vector_bisect — Returns the vector that bisects two vectors.
// Usage: newv = vector_bisect(v1,v2);
```

#### indices = vector_nearest(query, k, target);

Finds the `k` nearest points in a list to a given point.

```openscad
$fn=32;
k = 1000;
points = list_to_matrix(rands(0,10,k*2,seed=13333),2);
tree = vector_search_tree(points);
queries = [for(i=[3,7],j=[3,7]) [i,j]];
search_ind = [for(q=queries) vector_nearest(q, 15, tree)];
move_copies(points) circle(r=.08);
for(i=idx(queries)){
circle = circle(r=norm(points[last(search_ind[i])]-queries[i]));
color("red")  move_copies(select(points, search_ind[i])) circle(r=.08);
color("blue") stroke(move(queries[i], circle), closed=true, width=.08);  
}
```

#### perp = vector_perp(v,w);

Returns component of a vector perpendicular to a second vector

```openscad
v = [12,6];
w = [13,22];
stroke([[0,0],v],endcap2="arrow2");
stroke([[0,0],w],endcap2="arrow2",color="red");
stroke([[0,0],vector_perp(v,w)], endcap2="arrow2", color="blue");
```

#### indices = vector_search(query, r, target);

Finds points in a list that are close to a given point.

```openscad
$fn=32;
k = 2000;
points = list_to_matrix(rands(0,10,k*2,seed=13333),2);
queries = [for(i=[3,7],j=[3,7]) [i,j]];
search_ind = vector_search(queries, points, 1);
move_copies(points) circle(r=.08);
for(i=idx(queries)){
color("blue")stroke(move(queries[i],circle(r=1)), closed=true, width=.08);
color("red") move_copies(select(points, search_ind[i])) circle(r=.08);
}
```

#### tree = vector_search_tree(points,leafsize);

Makes a distance search tree for a list of points.

```openscad
$fn=32;
k = 2000;
points = random_points(k, scale=10, dim=2,seed=13333);
queries = [for(i=[3,7],j=[3,7]) [i,j]];
search_tree = vector_search_tree(points);
search_ind = vector_search(queries,1,search_tree);
move_copies(points) circle(r=.08);
for(i=idx(queries)){
color("blue") stroke(move(queries[i],circle(r=1)), closed=true, width=.08);
color("red")  move_copies(select(points, search_ind[i])) circle(r=.08);
}
```

#### cmp = version_cmp(a,b);

Compares two versions.

```openscad
cmp1 = version_cmp(2.010034, "2.1.33");  // Returns: >0
cmp2 = version_cmp(2.010034, "2.1.34");  // Returns: 0
cmp3 = version_cmp(2.010034, "2.1.35");  // Returns: <0
```

#### ver = version_to_list(x);

Splits a version into a list of integer version parts.

```openscad
v1 = version_to_list("2.1.43");  // Returns: [2,1,43]
v2 = version_to_list(2.120234);  // Returns: [2,12,234]
v3 = version_to_list([2,3,4]);   // Returns: [2,3,4]
v4 = version_to_list([2,3,4,5]); // Returns: [2,3,4]
```

#### str = version_to_num(version);

Coerces a version into a standard version float.

```openscad
v1 = version_to_num([2,1,43]);   // Returns: 2.010043
v2 = version_to_num([2,34,567]); // Returns: 2.340567
v3 = version_to_num(2.120567);   // Returns: 2.120567
v4 = version_to_num("2.6.79");   // Returns: 2.060079
```

#### str = version_to_str(version);

Coerces a version into a standard version string.

```openscad
v1 = version_to_str([2,1,43]);  // Returns: "2.1.43"
v2 = version_to_str(2.010043);  // Returns: "2.1.43"
v3 = version_to_str(2.340789);  // Returns: "2.34.789"
v4 = version_to_str("2.3.89");  // Returns: "2.3.89"
```

#### area = vnf_area(vnf);

Returns the surface area of a VNF.

```openscad
// vnf_area — Returns the surface area of a VNF.
// Usage: area = vnf_area(vnf);
```

#### bentvnf = vnf_bend(vnf,r|d=,[axis=]);

Bends a VNF around an axis.

```openscad
vnf0 = cube([100,40,10], center=true);
vnf1 = up(50, p=vnf0);
vnf2 = down(50, p=vnf0);
bent1 = vnf_bend(vnf1, axis="Y");
bent2 = vnf_bend(vnf2, axis="Y");
vnf_polyhedron([bent1,bent2]);
```

#### boundary = vnf_boundary(vnf, [merge=], [idx=]);

Returns the boundary of a VNF as a list of paths

```openscad
patch = [
// u=0,v=0                                         u=1,v=0
[[-50,-50,  0], [-16,-50,  20], [ 16,-50, -20], [50,-50,  0]],
[[-50,-16, 20], [-16,-16,  20], [ 16,-16, -20], [50,-16, 20]],
[[-50, 16, 20], [-16, 16, -20], [ 16, 16,  20], [50, 16, 20]],
[[-50, 50,  0], [-16, 50, -20], [ 16, 50,  20], [50, 50,  0]],
// u=0,v=1                                         u=1,v=1
];                
bezvnf = bezier_vnf(patch);
boundary = vnf_boundary(bezvnf);
vnf_polyhedron(bezvnf);
stroke(boundary,color="green");
```

#### min_max = vnf_bounds(vnf, [fast]);

Returns the min and max bounding coordinates for the VNF.

```openscad
echo(vnf_bounds(cube([2,3,4],center=true)));   // Displays [[-1, -1.5, -2], [1, 1.5, 2]]
```

#### clean_vnf = vnf_drop_unused_points(vnf);

Removes unreferenced vertices from a VNF.

```openscad
// vnf_drop_unused_points — Removes unreferenced vertices from a VNF.
// Usage: clean_vnf = vnf_drop_unused_points(vnf);
```

#### vnf_faces(...)

Returns the list of faces from a VNF.

```openscad
// vnf_faces — Returns the list of faces from a VNF.
// Usage: vnf_faces(...)
```

#### vnf = vnf_from_polygons(polygons, [eps]);

Returns a VNF from a list of 3D polygons.

```openscad
dihedral = 2*atan(PHI);   // dodecahedron face dihedral
rpenta = 10;              // pentagon face radius
edge = 2*rpenta*sin(36);  // edge length
inrad = 0.5*edge * PHI*PHI/sqrt(3-PHI);   // inner radius
face3d = path3d(pentagon(rpenta), inrad); // single face
facepoints = [
face3d,
for(a=[36:72:360]) zrot(a, yrot(180-dihedral, face3d)),
for(a=[36:72:360]) zrot(a, yrot(360-dihedral, face3d)),
yrot(180, face3d)
];
vnf = vnf_from_polygons(facepoints, fast=true);
vnf_polyhedron(vnf);
```

#### vnf = vnf_from_region(region, [transform], [reverse]);

Returns a 3D VNF given a 2D region.

```openscad
region = [square([20,10],center=true),
right(5,square(4,center=true)),
left(5,square(6,center=true))];
vnf = vnf_from_region(region);
color("gray")down(.125)
linear_extrude(height=.125)region(region);
vnf_wireframe(vnf,width=.25);
```

#### newvnf = vnf_halfspace(plane, vnf, [closed], [boundary]);

Returns the intersection of the vnf with a half space.

```openscad
vnf = cube(10,center=true);
cutvnf = vnf_halfspace([-1,1,-1,0], vnf);
vnf_polyhedron(cutvnf);
```

#### vnf_hull = hull_vnf(vnf);

Compute convex hull of VNF or 3d path

```openscad
ellipse = xscale(2, p=circle($fn=48, r=3));
pentagon = subdivide_path(pentagon(r=1), 20);
vnf=path_sweep(pentagon, path3d(ellipse),
closed=true, twist=360*2);
vnfhull = vnf_hull(vnf);
vnf_polyhedron(vnf);
move([10,10])
vnf_polyhedron(vnfhull);
```

#### vnf = vnf_join([VNF, VNF, VNF, ...]);

Returns a single VNF structure from a list of VNF structures.

```openscad
bottom = vnf_vertex_array([path3d(rect(8)), path3d(rect(5),4)],col_wrap=true,cap1=true);
vnf_polyhedron(bottom);
```

#### new_vnf = vnf_merge_points(vnf, [eps]);

Consolidates duplicate vertices of a VNF.

```openscad
// vnf_merge_points — Consolidates duplicate vertices of a VNF.
// Usage: new_vnf = vnf_merge_points(vnf, [eps]);
```

#### vnf_polyhedron(vnf) [ATTACHMENTS];

Returns a polyhedron from a VNF or list of VNFs.

```openscad
// vnf_polyhedron — Returns a polyhedron from a VNF or list of VNFs.
// Usage: vnf_polyhedron(vnf) [ATTACHMENTS];
```

#### vnf2 = vnf_quantize(vnf,[q]);

Quantizes the vertex coordinates of a VNF.

```openscad
// vnf_quantize — Quantizes the vertex coordinates of a VNF.
// Usage: vnf2 = vnf_quantize(vnf,[q]);
```

#### rvnf = vnf_reverse_faces(vnf);

Reverses the faces of a VNF.

```openscad
// vnf_reverse_faces — Reverses the faces of a VNF.
// Usage: rvnf = vnf_reverse_faces(vnf);
```

#### newvnf = vnf_sheet(vnf, delta, [style=], [merge=]);

Extends a VNF into a thin sheet by extruding normal to the VNF

```openscad
pts = [
for(x=[30:5:180]) [
for(y=[-6:0.5:6])
[7*y,x, sin(x)*y^2]
]
];
vnf=vnf_vertex_array(pts);
vnf_polyhedron(vnf_sheet(vnf,[-10,0]));
```

#### sliced = vnf_slice(vnf, dir, cuts);

Slice the faces of a VNF along an axis.

```openscad
include <BOSL2/polyhedra.scad>
vnf = regular_polyhedron_info("vnf", "dodecahedron", side=12);
vnf_polyhedron(vnf);
sliced = vnf_slice(vnf, "X", [-6,-1,10]);
color("red")vnf_wireframe(sliced,width=.3);
```

#### newvnf = vnf(vnf, delta, [merge=]);

Computes an offset surface to a VNF for small offset distances

```openscad
vnf = sphere(d=100);
xdistribute(spacing=125){
vnf_polyhedron(vnf);
vnf_polyhedron(vnf_small_offset(vnf,18));
}
```

#### vnf = vnf_tri_array(points, [caps=], [cap1=], [cap2=], [reverse=], [col_wrap=], [row_wrap=], [limit_bunching=])

Returns a VNF from an array of points. The array need not be rectangular.

```openscad
pts = [for(y=[1:1:10]) [for(x=[0:y-1]) [x,y,y]]];
vnf = vnf_tri_array(pts);
vnf_wireframe(vnf,width=0.1);
color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
```

#### vnf2 = vnf_triangulate(vnf);

Triangulates the faces of a VNF.

```openscad
include <BOSL2/polyhedra.scad>
vnf = zrot(33,regular_polyhedron_info("vnf", "dodecahedron", side=12));
vnf_polyhedron(vnf);
triangulated = vnf_triangulate(vnf);
color("red")vnf_wireframe(triangulated,width=.3);
```

#### newvnf = vnf_unify_faces(vnf);

Remove triangulation from VNF, returning a copy with full faces

```openscad
$fn=16;
poly = linear_sweep(hexagon(side=10),h=35);
vnf = vnf_unify_faces(poly);
vnf_wireframe(poly);
color([0,1,1,.70])vnf_polyhedron(poly);
right(25){
vnf_wireframe(vnf);
color([0,1,1,.70])vnf_polyhedron(vnf);
}
```

#### vnf_validate(vnf, [size], [show_warns=], [check_isects=], [big_face=], [opacity=], [adjacent=], [label_verts=], [label_faces=], [wireframe=]);

Echos non-manifold VNF errors to the console.

```openscad
vnf = skin([
path3d(regular_ngon(n=3, d=100),0),
path3d(regular_ngon(n=5, d=100),100)
], slices=0, caps=true, method="tangent");
vnf_validate(vnf);
```

#### vnf = vnf_vertex_array(points, [caps=], [cap1=], [cap2=], [style=], [reverse=], [col_wrap=], [row_wrap=], [triangulate=]);

Returns a VNF structure from a rectangular vertex list.

```openscad
vnf = vnf_vertex_array(
points=[
for (h = [0:5:179]) [
for (t = [0:5:359])
cylindrical_to_xyz(100 + 12 * cos((h/2 + t)*6), t, h)
]
],
col_wrap=true, caps=true, reverse=true, style="alt"
);
vnf_polyhedron(vnf);
```

#### vnf_vertices(...)

Returns the list of vertex points from a VNF.

```openscad
// vnf_vertices — Returns the list of vertex points from a VNF.
// Usage: vnf_vertices(...)
```

#### vol = vnf_volume(vnf);

Returns the volume of a VNF.

```openscad
// vnf_volume — Returns the volume of a VNF.
// Usage: vol = vnf_volume(vnf);
```

#### vnf_wireframe(vnf, [width]);

Creates a wireframe model from a VNF.

```openscad
$fn=32;
ball = sphere(r=20, $fn=6);
vnf_wireframe(ball,width=1);
```

#### wedge(size, [center|anchor=], ...) [ATTACHMENTS];

Creates a 3d triangular wedge.

```openscad
wedge([20, 40, 15], center=true);
```

#### x = while(init, cond, func);

While a `cond` function returns true, iteratively calls a work function, returning the final result.

```openscad
fibs = while(
init = [1,1],
cond = function (i,x) select(x,-1)<25,
func = function (i,x) concat(x, [sum(select(x,-2,-1))])
);  // Returns: [1,1,2,3,5,8,13,21]
```

#### wire_bundle(path, wires, [wirediam], [rounding], [wirenum=], [corner_steps=]);

Creates a wire bundle for a given number of wires.

```openscad
wire_bundle([[50,0,-50], [50,50,-50], [0,50,-50], [0,0,-50], [0,0,0]], rounding=10, wires=13);
```

#### worm(circ_pitch, d, l, [starts=], [left_handed=], [pressure_angle=], [backlash=], [clearance=]);

Creates a worm that will mate with a worm gear.

```openscad
worm(circ_pitch=8, d=30, l=50, $fn=72);
```

#### dist = worm_dist(mod=|diam_pitch=|circ_pitch=, d, starts, teeth, [profile_shift], [pressure_angle=]);

Returns the distance between a worm and a worm gear

```openscad
// worm_dist — Returns the distance between a worm and a worm gear
// Usage: dist = worm_dist(mod=|diam_pitch=|circ_pitch=, d, starts, teeth, [profile_shift], [pressure_angle=]);
```

#### worm_gear(circ_pitch, teeth, worm_diam, [worm_starts=], [worm_arc=], [crowning=], [left_handed=], [pressure_angle=], [backlash=], [clearance=], [slices=], [shaft_diam=]) [ATTACHMENTS];

Creates a worm gear that will mate with a worm.

```openscad
worm_gear(circ_pitch=5, teeth=36, worm_diam=30, worm_starts=1);
```

#### thick = worm_gear_thickness(pitch, teeth, worm_diam, [worm_arc=], [crowning=], [clearance=]);

Returns the thickness for a worm gear.

```openscad
thick = worm_gear_thickness(circ_pitch=5, teeth=36, worm_diam=30);
thick = worm_gear_thickness(mod=2, teeth=28, worm_diam=25);
```

#### xcopies(spacing, [n], [sp=]) CHILDREN;

Places copies of children along the X axis.

```openscad
xcopies(20) sphere(3);
xcopies(20, n=3) sphere(3);
xcopies(spacing=15, l=50) sphere(3);
xcopies(n=4, l=30, sp=[0,10,0]) sphere(3);
```

#### xcyl(l|h|length|height, r|d=, [anchor=], ...) [ATTACHMENTS];

Creates a cylinder oriented along the X axis.

```openscad
ydistribute(50) {
xcyl(l=35, r=10);
xcyl(l=35, r1=15, r2=5) {
attach(TOP) anchor_arrow();
attach(RIGHT) anchor_arrow();
}
}
```

#### xdistribute(spacing, [sizes]) CHILDREN;

Distributes each child, individually, out along the X axis.

```openscad
xdistribute(sizes=[100, 10, 30], spacing=40) {
sphere(r=50);
cube([10,20,30], center=true);
cylinder(d=30, h=50, center=true);
}
```

#### xflip([x=]) CHILDREN;

Reflects children across the YZ plane.

```openscad
xflip() yrot(90) cylinder(d1=10, d2=0, h=20);
color("blue", 0.25) cube([0.01,15,15], center=true);
color("red", 0.333) yrot(90) cylinder(d1=10, d2=0, h=20);
```

#### xflip_copy([offset], [x]) CHILDREN;

Makes a copy of children mirrored across the X axis.

```openscad
xflip_copy() yrot(90) cylinder(h=20, r1=4, r2=0);
color("blue",0.25) cube([0.01,15,15], center=true);
```

#### xrot(a, [cp=]) CHILDREN;

Rotates children around the X axis using the right-hand rule.

```openscad
#cylinder(h=50, r=10, center=true);
xrot(90) cylinder(h=50, r=10, center=true);
```

#### xrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=]) CHILDREN;

Rotates copies of children around the X axis.

```openscad
xrot_copies([180, 270, 315])
cylinder(h=20, r1=5, r2=0);
color("red",0.333) cylinder(h=20, r1=5, r2=0);
```

#### xscale(x, [cp=]) CHILDREN;

Scales children along the X axis.

```openscad
xscale(3) sphere(r=10);
```

#### newfunc = xval(func);

Generates a function with signature `(i,x)` that calls `func(x)`

```openscad
x = while(0, ival(f_lt(5)), xval(f_add(1)));
```

#### r_theta = xy_to_polar(x,y);

Convert 2D cartesian coordinates to polar coordinates (radius and angle)

```openscad
plr = xy_to_polar(20,30);
plr = xy_to_polar([40,60]);
plrs = xy_to_polar([[40,60],[-10,20]]);
```

#### alt_az_r = xyz_to_altaz(x,y,z);

Convert 3D cartesian coordinates to [altitude,azimuth,range].

```openscad
aa = xyz_to_altaz(20,30,40);
aa = xyz_to_altaz([40,50,70]);
```

#### rtz = xyz_to_cylindrical(x,y,z);

Convert 3D cartesian coordinates to cylindrical coordinates.

```openscad
cyl = xyz_to_cylindrical(20,30,40);
cyl = xyz_to_cylindrical([40,50,70]);
cyls = xyz_to_cylindrical([[40,50,70], [-10,15,-30]]);
```

#### r_theta_phi = xyz_to_spherical(x,y,z)

Convert 3D cartesian coordinates to spherical coordinates.

```openscad
sph = xyz_to_spherical(20,30,40);
sph = xyz_to_spherical([40,50,70]);
sphs = xyz_to_spherical([[40,50,70], [25,-14,27]]);
```

#### ycopies(spacing, [n], [sp=]) CHILDREN;

Places copies of children along the Y axis.

```openscad
ycopies(20) sphere(3);
ycopies(20, n=3) sphere(3);
ycopies(spacing=15, l=50) sphere(3);
ycopies(n=4, l=30, sp=[10,0,0]) sphere(3);
```

#### ycyl(l|h|length|height, r|d=, [anchor=], ...) [ATTACHMENTS];

Creates a cylinder oriented along the y axis.

```openscad
xdistribute(50) {
ycyl(l=35, r=10);
ycyl(l=35, r1=15, r2=5) {
attach(TOP) anchor_arrow();
attach(RIGHT) anchor_arrow();
}
}
```

#### ydistribute(spacing, [sizes]) CHILDREN;

Distributes each child, individually, out along the Y axis.

```openscad
ydistribute(sizes=[30, 20, 100], spacing=40) {
cylinder(d=30, h=50, center=true);
cube([10,20,30], center=true);
sphere(r=50);
}
```

#### yflip([y=]) CHILDREN;

Reflects children across the XZ plane.

```openscad
yflip() xrot(90) cylinder(d1=10, d2=0, h=20);
color("blue", 0.25) cube([15,0.01,15], center=true);
color("red", 0.333) xrot(90) cylinder(d1=10, d2=0, h=20);
```

#### yflip_copy([offset], [y]) CHILDREN;

Makes a copy of children mirrored across the Y axis.

```openscad
yflip_copy() xrot(-90) cylinder(h=20, r1=4, r2=0);
color("blue",0.25) cube([15,0.01,15], center=true);
```

#### yrot(a, [cp=]) CHILDREN;

Rotates children around the Y axis using the right-hand rule.

```openscad
#cylinder(h=50, r=10, center=true);
yrot(90) cylinder(h=50, r=10, center=true);
```

#### yrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=]) CHILDREN;

Rotates copies of children around the Y axis.

```openscad
yrot_copies([180, 270, 315])
cylinder(h=20, r1=5, r2=0);
color("red",0.333) cylinder(h=20, r1=5, r2=0);
```

#### yscale(y, [cp=]) CHILDREN;

Scales children along the Y axis.

```openscad
yscale(3) sphere(r=10);
```

#### zcopies(spacing, [n], [sp=]) CHILDREN;

Places copies of children along the Z axis.

```openscad
zcopies(20) sphere(3);
zcopies(20, n=3) sphere(3);
zcopies(spacing=15, l=50) sphere(3);
zcopies(n=4, l=30, sp=[10,0,0]) sphere(3);
```

#### zcyl(l|h|length|height, r|d=, [anchor=], ...) [ATTACHMENTS];

Creates a cylinder oriented along the y axis.

```openscad
xdistribute(50) {
zcyl(l=35, r=10);
zcyl(l=35, r1=15, r2=5) {
attach(TOP) anchor_arrow();
attach(RIGHT) anchor_arrow();
}
}
```

#### zdistribute(spacing, [sizes]) CHILDREN;

Distributes each child, individually, out along the Z axis.

```openscad
zdistribute(sizes=[30, 20, 100], spacing=40) {
cylinder(d=30, h=50, center=true);
cube([10,20,30], center=true);
sphere(r=50);
}
```

#### zflip([z=]) CHILDREN;

Reflects children across the XY plane.

```openscad
zflip() cylinder(d1=10, d2=0, h=20);
color("blue", 0.25) cube([15,15,0.01], center=true);
color("red", 0.333) cylinder(d1=10, d2=0, h=20);
```

#### zflip_copy([offset], [z]) CHILDREN;

Makes a copy of children mirrored across the Z axis.

```openscad
zflip_copy() cylinder(h=20, r1=4, r2=0);
color("blue",0.25) cube([15,15,0.01], center=true);
```

#### zrot(a, [cp=]) CHILDREN;

Rotates children around the Z axis using the right-hand rule.

```openscad
#cube(size=[60,20,40], center=true);
zrot(90) cube(size=[60,20,40], center=true);
```

#### zrot_copies(rots, [cp], [r=|d=], [sa=], [subrot=]) CHILDREN;

Rotates copies of children around the Z axis.

```openscad
zrot_copies([180, 270, 315])
yrot(90) cylinder(h=20, r1=5, r2=0);
color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0);
```

#### zscale(z, [cp=]) CHILDREN;

Scales children along the Z axis.

```openscad
zscale(3) sphere(r=10);
```

#### 2020 corner bracket (3-way)

A 3-way corner bracket that joins three 2020 aluminium extrusions at a right-angle corner, with M5 bolt holes. From the adgaudio OpenSCAD_connectors library.

```openscad
// 2020 corner bracket (3-way)
//
// Source: OpenSCAD_connectors (adgaudio) — MIT
// Generated from the library above, used under its license.

include <oscad-connectors/_defaults.scad>
use <oscad-connectors/connector.scad>
use <oscad-connectors/corner_bracket.scad>
use <oscad-connectors/grid.scad>
use <oscad-connectors/repeat_grid.scad>

corner_bracket(2, 2, 2);
```

#### 2020 corner bracket (flat L)

A flat L-shaped bracket that joins two 2020 aluminium extrusions in-plane, with M5 bolt holes. From the adgaudio OpenSCAD_connectors library.

```openscad
// 2020 corner bracket (flat L)
//
// Source: OpenSCAD_connectors (adgaudio) — MIT
// Generated from the library above, used under its license.

include <oscad-connectors/_defaults.scad>
use <oscad-connectors/connector.scad>
use <oscad-connectors/corner_bracket.scad>
use <oscad-connectors/grid.scad>
use <oscad-connectors/repeat_grid.scad>

corner_bracket(2, 2, 0);
```

#### 2020 extrusion end cap

A press-fit end cap that closes the open end of a 20x20 aluminium extrusion, with an M5 bolt clearance hole. From the adgaudio OpenSCAD_connectors library.

```openscad
// 2020 extrusion end cap
//
// Source: OpenSCAD_connectors (adgaudio) — MIT
// Generated from the library above, used under its license.

include <oscad-connectors/_defaults.scad>
use <oscad-connectors/endcap.scad>
use <oscad-connectors/grid.scad>
use <oscad-connectors/repeat_grid.scad>

endcap(20, 20, 8, 2, m5_hole_tight);
```

#### 2020 T-slot extrusion bar

A length of standard 2020 aluminium-style T-slot extrusion (20 by 20 mm cross section) with four T-channels, ready to print as a jig rail, frame member, or mock-up of real metal extrusion. Change the length to suit your build.

```openscad
// 2020 T-slot extrusion bar
//
// Source: OpenSCAD_AluminumExtrusionProfile_Library — Apache-2.0
// Generated from the library above, used under its license.

include <extrusion-profiles/AluminumExtrusionProfile.scad>

$fn = 80;

length = 120;  // bar length in mm

linear_extrude(height = length)
  2020_extrusion_profile(slot = "t");
```

#### 2040 V-slot extrusion rail

A length of 2040 aluminium-style extrusion (40 by 20 mm cross section) cut with V-shaped channels, the kind used for CNC and 3D-printer gantries that ride on V-wheels. Print it as a linear-motion rail mock-up or a sturdy frame member.

```openscad
// 2040 V-slot extrusion rail
//
// Source: OpenSCAD_AluminumExtrusionProfile_Library — Apache-2.0
// Generated from the library above, used under its license.

include <extrusion-profiles/AluminumExtrusionProfile.scad>

$fn = 80;

length = 160;  // rail length in mm

linear_extrude(height = length)
  2040_extrusion_profile(slot = "v");
```

#### 2060 extrusion base bar

A length of wide 2060 aluminium-style extrusion (60 by 20 mm cross section) with six T-channels across its face, perfect as a base bar or work-surface rail that needs lots of mounting slots. Resize the length for benches, fixtures, or camera sliders.

```openscad
// 2060 extrusion base bar
//
// Source: OpenSCAD_AluminumExtrusionProfile_Library — Apache-2.0
// Generated from the library above, used under its license.

include <extrusion-profiles/AluminumExtrusionProfile.scad>

$fn = 80;

length = 140;  // bar length in mm

linear_extrude(height = length)
  2060_extrusion_profile(slot = "t");
```

#### 3030 T-slot extrusion beam

A length of 3030 aluminium-style extrusion (30 by 30 mm cross section) with four T-channels, a popular mid-weight size for printer frames, camera rigs, and shelving. Print it as a structural beam and adjust the length to fit your build.

```openscad
// 3030 T-slot extrusion beam
//
// Source: OpenSCAD_AluminumExtrusionProfile_Library — Apache-2.0
// Generated from the library above, used under its license.

include <extrusion-profiles/AluminumExtrusionProfile.scad>

$fn = 80;

length = 130;  // beam length in mm

linear_extrude(height = length)
  3030_extrusion_profile(slot = "t");
```

#### 4040 heavy-duty extrusion post

A short length of beefy 4040 aluminium-style extrusion (40 by 40 mm cross section) with eight T-channels, ideal as a corner post or load-bearing upright in a printed frame. Lengthen it for a full column or keep it short as a connector block.

```openscad
// 4040 heavy-duty extrusion post
//
// Source: OpenSCAD_AluminumExtrusionProfile_Library — Apache-2.0
// Generated from the library above, used under its license.

include <extrusion-profiles/AluminumExtrusionProfile.scad>

$fn = 80;

length = 80;  // post length in mm

linear_extrude(height = length)
  4040_extrusion_profile(slot = "t");
```

#### Gridfinity baseplate 2×2

A 2×2 Gridfinity baseplate — the grid foundation that bins drop into. Built with the kennetek gridfinity-rebuilt library; change gridx/gridy for a larger plate.

```openscad
// Gridfinity baseplate 2×2
//
// Source: gridfinity-rebuilt (kennetek) — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-rebuilt/src/core/standard.scad>
include <gridfinity-rebuilt/src/core/gridfinity-baseplate.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-utility.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-holes.scad>
use <gridfinity-rebuilt/src/helpers/generic-helpers.scad>
use <gridfinity-rebuilt/src/helpers/grid.scad>
use <gridfinity-rebuilt/gridfinity-rebuilt-baseplate.scad>

gridx = 2;  // columns
gridy = 2;  // rows
gridfinityBaseplate([gridx, gridy], 42, [0, 0], 0, bundle_hole_options(), 0, [0, 0]);
```

#### Gridfinity baseplate 3×3

A 3×3 Gridfinity baseplate for a 9-cell organizer foundation. Built with the kennetek gridfinity-rebuilt library.

```openscad
// Gridfinity baseplate 3×3
//
// Source: gridfinity-rebuilt (kennetek) — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-rebuilt/src/core/standard.scad>
include <gridfinity-rebuilt/src/core/gridfinity-baseplate.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-utility.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-holes.scad>
use <gridfinity-rebuilt/src/helpers/generic-helpers.scad>
use <gridfinity-rebuilt/src/helpers/grid.scad>
use <gridfinity-rebuilt/gridfinity-rebuilt-baseplate.scad>

gridx = 3;  // columns
gridy = 3;  // rows
gridfinityBaseplate([gridx, gridy], 42, [0, 0], 0, bundle_hole_options(), 0, [0, 0]);
```

#### Gridfinity bin 1×1

The smallest Gridfinity storage bin — a single 1×1 cell, 3 units tall, with a stacking lip. Built with the kennetek gridfinity-rebuilt library.

```openscad
// Gridfinity bin 1×1
//
// Source: gridfinity-rebuilt (kennetek) — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-rebuilt/src/core/standard.scad>
use <gridfinity-rebuilt/src/core/bin.scad>
use <gridfinity-rebuilt/src/core/cutouts.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-holes.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-utility.scad>
use <gridfinity-rebuilt/src/helpers/generic-helpers.scad>
use <gridfinity-rebuilt/src/helpers/grid.scad>
use <gridfinity-rebuilt/src/helpers/grid_element.scad>

gridx = 1;  // columns
gridy = 1;  // rows
gridz = 3;  // height in 7mm units
divx = 1;  // compartment columns
divy = 1;  // compartment rows

bin1 = new_bin([gridx, gridy], gridz * 7);
bin_render(bin1) {
    bin_subdivide(bin1, [divx, divy]) {
        cut_compartment_auto(cgs(), 5, false, 0);
    }
}
```

#### Gridfinity bin 2×1

A 2×1 Gridfinity storage bin, 6 units tall, one open compartment. Built with the kennetek gridfinity-rebuilt library.

```openscad
// Gridfinity bin 2×1
//
// Source: gridfinity-rebuilt (kennetek) — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-rebuilt/src/core/standard.scad>
use <gridfinity-rebuilt/src/core/bin.scad>
use <gridfinity-rebuilt/src/core/cutouts.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-holes.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-utility.scad>
use <gridfinity-rebuilt/src/helpers/generic-helpers.scad>
use <gridfinity-rebuilt/src/helpers/grid.scad>
use <gridfinity-rebuilt/src/helpers/grid_element.scad>

gridx = 2;  // columns
gridy = 1;  // rows
gridz = 6;  // height in 7mm units
divx = 1;  // compartment columns
divy = 1;  // compartment rows

bin1 = new_bin([gridx, gridy], gridz * 7);
bin_render(bin1) {
    bin_subdivide(bin1, [divx, divy]) {
        cut_compartment_auto(cgs(), 5, false, 0);
    }
}
```

#### Gridfinity bin 2×2

A 2×2 Gridfinity storage bin, 6 units tall, one large compartment. Built with the kennetek gridfinity-rebuilt library.

```openscad
// Gridfinity bin 2×2
//
// Source: gridfinity-rebuilt (kennetek) — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-rebuilt/src/core/standard.scad>
use <gridfinity-rebuilt/src/core/bin.scad>
use <gridfinity-rebuilt/src/core/cutouts.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-holes.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-utility.scad>
use <gridfinity-rebuilt/src/helpers/generic-helpers.scad>
use <gridfinity-rebuilt/src/helpers/grid.scad>
use <gridfinity-rebuilt/src/helpers/grid_element.scad>

gridx = 2;  // columns
gridy = 2;  // rows
gridz = 6;  // height in 7mm units
divx = 1;  // compartment columns
divy = 1;  // compartment rows

bin1 = new_bin([gridx, gridy], gridz * 7);
bin_render(bin1) {
    bin_subdivide(bin1, [divx, divy]) {
        cut_compartment_auto(cgs(), 5, false, 0);
    }
}
```

#### Gridfinity bin 2×2 (4 compartments)

A 2×2 Gridfinity bin subdivided into a 2×2 grid of four compartments for small-parts sorting. Built with the kennetek gridfinity-rebuilt library.

```openscad
// Gridfinity bin 2×2 (4 compartments)
//
// Source: gridfinity-rebuilt (kennetek) — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-rebuilt/src/core/standard.scad>
use <gridfinity-rebuilt/src/core/bin.scad>
use <gridfinity-rebuilt/src/core/cutouts.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-holes.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-utility.scad>
use <gridfinity-rebuilt/src/helpers/generic-helpers.scad>
use <gridfinity-rebuilt/src/helpers/grid.scad>
use <gridfinity-rebuilt/src/helpers/grid_element.scad>

gridx = 2;  // columns
gridy = 2;  // rows
gridz = 6;  // height in 7mm units
divx = 2;  // compartment columns
divy = 2;  // compartment rows

bin1 = new_bin([gridx, gridy], gridz * 7);
bin_render(bin1) {
    bin_subdivide(bin1, [divx, divy]) {
        cut_compartment_auto(cgs(), 5, false, 0);
    }
}
```

#### Gridfinity bin 2×1 (label tab)

A 2×1 Gridfinity bin with an auto label tab ledge for a printed or written label. Built with the kennetek gridfinity-rebuilt library.

```openscad
// Gridfinity bin 2×1 (label tab)
//
// Source: gridfinity-rebuilt (kennetek) — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-rebuilt/src/core/standard.scad>
use <gridfinity-rebuilt/src/core/bin.scad>
use <gridfinity-rebuilt/src/core/cutouts.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-holes.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-utility.scad>
use <gridfinity-rebuilt/src/helpers/generic-helpers.scad>
use <gridfinity-rebuilt/src/helpers/grid.scad>
use <gridfinity-rebuilt/src/helpers/grid_element.scad>

gridx = 2;  // columns
gridy = 1;  // rows
gridz = 6;  // height in 7mm units
divx = 1;  // compartment columns
divy = 1;  // compartment rows

bin1 = new_bin([gridx, gridy], gridz * 7);
bin_render(bin1) {
    bin_subdivide(bin1, [divx, divy]) {
        cut_compartment_auto(cgs(), 1, false, 0);
    }
}
```

#### Gridfinity bin 1×2 (scooped)

A 1×2 Gridfinity bin with a curved front scoop that sweeps parts toward your fingers. Built with the kennetek gridfinity-rebuilt library.

```openscad
// Gridfinity bin 1×2 (scooped)
//
// Source: gridfinity-rebuilt (kennetek) — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-rebuilt/src/core/standard.scad>
use <gridfinity-rebuilt/src/core/bin.scad>
use <gridfinity-rebuilt/src/core/cutouts.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-holes.scad>
use <gridfinity-rebuilt/src/core/gridfinity-rebuilt-utility.scad>
use <gridfinity-rebuilt/src/helpers/generic-helpers.scad>
use <gridfinity-rebuilt/src/helpers/grid.scad>
use <gridfinity-rebuilt/src/helpers/grid_element.scad>

gridx = 1;  // columns
gridy = 2;  // rows
gridz = 6;  // height in 7mm units
divx = 1;  // compartment columns
divy = 1;  // compartment rows

bin1 = new_bin([gridx, gridy], gridz * 7);
bin_render(bin1) {
    bin_subdivide(bin1, [divx, divy]) {
        cut_compartment_auto(cgs(), 5, false, 1);
    }
}
```

#### Gridfinity storage bin 2x1x3

A printable Gridfinity storage bin two grid cells wide, one deep, and three units tall, with magnet and screw pockets in the base and a finger-slide scoop for easy parts removal.

```openscad
// Gridfinity storage bin 2x1x3
//
// Source: vector76-gridfinity — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-vector76/gridfinity_modules.scad>
use <gridfinity-vector76/gridfinity_cup_modules.scad>

$fn = 32;

basic_cup(
  num_x = 2,
  num_y = 1,
  num_z = 3,
  chambers = 1,
  withLabel = "disabled",
  labelWidth = 0,
  fingerslide = true,
  magnet_diameter = 6.5,
  screw_depth = 6,
  floor_thickness = 1.2,
  wall_thickness = 0.95,
  hole_overhang_remedy = false,
  efficient_floor = false,
  half_pitch = false,
  lip_style = "normal",
  box_corner_attachments_only = false
);
```

#### Gridfinity divided bin with label shelf

A printable Gridfinity bin three cells wide and two deep, split into three chambers by internal dividers, with a sloped label shelf across the front for marking each compartment.

```openscad
// Gridfinity divided bin with label shelf
//
// Source: vector76-gridfinity — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-vector76/gridfinity_modules.scad>
use <gridfinity-vector76/gridfinity_cup_modules.scad>

$fn = 32;

basic_cup(
  num_x = 3,
  num_y = 2,
  num_z = 4,
  chambers = 3,
  withLabel = "left",
  labelWidth = 0,
  fingerslide = true,
  magnet_diameter = 0,
  screw_depth = 0,
  floor_thickness = 1.0,
  wall_thickness = 0.95,
  hole_overhang_remedy = false,
  efficient_floor = false,
  half_pitch = false,
  lip_style = "normal",
  box_corner_attachments_only = false
);
```

#### Gridfinity glue stick holder

A printable Gridfinity cup sized to cradle a standard glue stick upright, with a snug bored pocket and a slightly flared mouth so the stick drops in and lifts out cleanly.

```openscad
// Gridfinity glue stick holder
//
// Source: vector76-gridfinity — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

cup_height = 5;
stick_diameter = 30;

include <gridfinity-vector76/gridfinity_glue_stick.scad>
```

#### Gridfinity socket wrench holder

A printable Gridfinity tray that holds a row of socket-wrench sockets at a 45-degree tilt for easy grabbing, with an engraved size label along the front edge.

```openscad
// Gridfinity socket wrench holder
//
// Source: vector76-gridfinity — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-vector76/gridfinity_modules.scad>
use <gridfinity-vector76/gridfinity_socket_holder.scad>

$fn = 32;

socket_holder(4, [12, 12, 12, 13, 14, 16, 16, 17, 21, 22], "METRIC", num_z = 5);
```

#### Gridfinity weighted baseplate 3x2

A printable Gridfinity baseplate three cells wide and two deep with hollow pockets in the underside for steel weights or screws, giving the grid a heavy, stable footing on the desk.

```openscad
// Gridfinity weighted baseplate 3x2
//
// Source: vector76-gridfinity — MIT
// https://github.com/kennetek/gridfinity-rebuilt-openscad
// Generated from the library above, used under its license.

include <gridfinity-vector76/gridfinity_modules.scad>
use <gridfinity-vector76/gridfinity_baseplate.scad>

$fn = 32;

weighted_baseplate(3, 2);
```

#### 608 bearing pillow block

A printable pillow-block housing sized from the official ISO bearing table for the popular 608 skate bearing, with a press-fit pocket, a shoulder lip so the bearing seats square, and two mounting-bolt holes. Drop a real 608 bearing in and bolt it to a frame.

```openscad
// 608 bearing pillow block
//
// Source: OpenSCAD-hardware-database — BSD-2-Clause
// Generated from the library above, used under its license.

include <hardware-db/lib/_hardware.scad>
include <hardware-db/lib/_3d_print.scad>
include <hardware-db/lib/_functions.scad>

$fn = 96;

// --- look up the real 608 bearing dimensions from the ISO table ---
bb       = GetDataTable("608", BB);
bore     = bb[BBDia];        //  8 mm
od       = bb[BBOuterDia];   // 22 mm
bwidth   = bb[BBHeight];     //  7 mm

// --- look up an M4 mounting bolt clearance from the bolt table ---
m4       = GetDataTable("M4", Bolt);
bolt_clr = m4[BoltDia];      // ~4.1 mm clearance

wall     = 4;                // wall around the bearing pocket
lip      = 1.2;             // shoulder that stops the bearing
base_h   = 3;               // baseplate thickness
press    = 0.05;            // press-fit interference

block_d  = od + 2 * wall;
block_w  = block_d + 2 * (bolt_clr + wall);  // room for bolt ears

module pillow_block() {
    difference() {
        union() {
            // round bearing boss
            cylinder(d = block_d, h = bwidth + base_h);
            // mounting base slab
            translate([-block_w / 2, -block_d / 2, 0])
                cube([block_w, block_d, base_h]);
        }
        // bearing press-fit pocket (down from the top)
        translate([0, 0, base_h])
            cylinder(d = od - press, h = bwidth + 1);
        // shoulder bore the bearing rests on
        translate([0, 0, -0.1])
            cylinder(d = od - 2 * lip, h = base_h + bwidth + 1);
        // two mounting holes through the ears
        for (sx = [-1, 1])
            translate([sx * (block_w / 2 - wall / 2 - bolt_clr / 2), 0, -0.1])
                cylinder(d = bolt_clr, h = base_h + 1);
    }
}

pillow_block();
```

#### GT2 belt idler pulley blank

A smooth flanged idler-pulley blank sized to a GT2 timing belt from the official belt table, with a bore reamed for a 623 ball bearing pressed in from each side. The belt-running surface width matches the GT2 pitch so the belt tracks without rubbing. Press in two 623 bearings and run it on an M3 shaft.

```openscad
// GT2 belt idler pulley blank
//
// Source: OpenSCAD-hardware-database — BSD-2-Clause
// Generated from the library above, used under its license.

include <hardware-db/lib/_hardware.scad>
include <hardware-db/lib/_3d_print.scad>
include <hardware-db/lib/_functions.scad>

$fn = 120;

// --- GT2 belt facts from the belt table ---
belt      = GetDataTable("GT2", Belt);
pitch     = belt[BeltPitch];   // 2.0 mm
thick     = belt[BeltThick];   // 0.63 mm

// --- 623 bearing to seat in the bore ---
bb        = GetDataTable("623", BB);
bb_od     = bb[BBOuterDia];    // 10 mm
bb_w      = bb[BBHeight];      //  4 mm

belt_w    = 6;                 // belt-tracking surface width
flange_h  = 1.5;               // each flange height
flange_d_extra = 4;            // flange overhang past the belt surface
run_d     = 16;                // diameter of the belt running surface
press     = 0.05;

total_h   = belt_w + 2 * flange_h;
flange_d  = run_d + 2 * flange_d_extra + thick * 2;

module pulley() {
    difference() {
        union() {
            // bottom flange
            cylinder(d = flange_d, h = flange_h);
            // belt running surface
            translate([0, 0, flange_h])
                cylinder(d = run_d, h = belt_w);
            // top flange
            translate([0, 0, flange_h + belt_w])
                cylinder(d = flange_d, h = flange_h);
        }
        // bearing pocket pressed in from the bottom
        translate([0, 0, -0.1])
            cylinder(d = bb_od - press, h = bb_w + 0.1);
        // bearing pocket pressed in from the top
        translate([0, 0, total_h - bb_w])
            cylinder(d = bb_od - press, h = bb_w + 0.2);
        // through shaft bore (clearance between the two bearings)
        translate([0, 0, -0.1])
            cylinder(d = bb[BBDia] + 0.6, h = total_h + 0.2);
    }
}

pulley();
```

#### LM8UU linear bearing clamp

A saddle clamp that holds an LM8UU linear ball bearing, sized straight from the official linear-bearing table, with a cradle bore for the bearing, two M3 bolt ears to pinch it closed, and a flat mounting base. Snap an LM8UU in and bolt the carriage to your 3D-printer gantry.

```openscad
// LM8UU linear bearing clamp
//
// Source: OpenSCAD-hardware-database — BSD-2-Clause
// Generated from the library above, used under its license.

include <hardware-db/lib/_hardware.scad>
include <hardware-db/lib/_3d_print.scad>
include <hardware-db/lib/_functions.scad>

$fn = 96;

// --- LM8UU linear bearing from the linear-bearing table ---
lb       = GetDataTable("LM8", LB);
lb_od    = lb[LBOuterDia];   // 15 mm
lb_len   = lb[LBLength];     // 24 mm

// --- M3 clamp bolt clearance ---
m3       = GetDataTable("M3", Bolt);
bolt_clr = m3[BoltDia];      // ~3.1 mm

wall     = 3;                // wall around the bearing
base_h   = 4;               // mounting base thickness
gap      = 1.5;             // clamp slit so it can pinch closed
ear_w    = bolt_clr + 4;    // bolt ear width

body_w   = lb_od + 2 * wall;
body_h   = lb_od / 2 + wall;
total_w  = body_w + 2 * ear_w;

module clamp() {
    difference() {
        union() {
            // mounting base
            translate([-total_w / 2, -lb_len / 2, 0])
                cube([total_w, lb_len, base_h]);
            // half-round saddle body
            translate([0, 0, base_h])
                difference() {
                    translate([-body_w / 2, -lb_len / 2, 0])
                        cube([body_w, lb_len, body_h]);
                    translate([0, lb_len / 2 + 0.1, lb_od / 2 + wall - 0.0])
                        rotate([90, 0, 0])
                            cylinder(d = lb_od, h = lb_len + 0.2);
                }
        }
        // bearing cradle bore through the body
        translate([0, lb_len / 2 + 0.1, base_h + lb_od / 2 + wall])
            rotate([90, 0, 0])
                cylinder(d = lb_od, h = lb_len + 0.2);
        // clamp slit
        translate([-gap / 2, -lb_len / 2 - 0.1, base_h + lb_od / 2 + wall])
            cube([gap, lb_len + 0.2, body_h]);
        // two clamp bolt holes through the ears
        for (sx = [-1, 1])
            translate([sx * (body_w / 2 + ear_w / 2), 0, base_h + lb_od / 2])
                rotate([90, 0, 0])
                    translate([0, 0, -lb_len / 2 - 0.1])
                        cylinder(d = bolt_clr, h = lb_len + 0.2);
    }
}

clamp();
```

#### M8 captive-nut knob

A knurled hand knob with a captive hex pocket sized from the official DIN934 nut table for an M8 nut, plus a clearance bore for the M8 bolt shank. Drop a real M8 nut in the trapped pocket and you have a tool-free clamping knob for jigs and fixtures.

```openscad
// M8 captive-nut knob
//
// Source: OpenSCAD-hardware-database — BSD-2-Clause
// Generated from the library above, used under its license.

include <hardware-db/lib/_hardware.scad>
include <hardware-db/lib/_3d_print.scad>
include <hardware-db/lib/_functions.scad>

$fn = 120;

// --- M8 nut from the DIN934 nut table ---
nut       = GetDataTable("M8", Nut);
nut_af    = nut[NutOuterDia];   // ~14.5 mm across flats (acts as nut socket dia)
nut_h     = nut[NutHeight];     // ~6.3 mm
bolt_dia  = nut[NutDia];        //  8.1 mm shank clearance

knob_d    = 36;                 // grip diameter
knob_h    = 16;                 // grip height
grips     = 14;                 // number of finger scallops
scallop_d = 5;                  // scallop cutter diameter
floor_t   = 3;                  // floor under the nut pocket

module knob() {
    difference() {
        // main grip body
        cylinder(d = knob_d, h = knob_h);

        // finger scallops around the rim
        for (i = [0 : grips - 1])
            rotate([0, 0, i * 360 / grips])
                translate([knob_d / 2, 0, -0.1])
                    cylinder(d = scallop_d, h = knob_h + 0.2);

        // hex nut pocket (trapped from the bottom) — point-up hexagon
        translate([0, 0, floor_t])
            rotate([0, 0, 30])
                cylinder(d = nut_af / cos(30), h = nut_h + 0.5, $fn = 6);

        // bolt shank clearance all the way through
        translate([0, 0, -0.1])
            cylinder(d = bolt_dia + 0.4, h = knob_h + 0.2);
    }
}

knob();
```

#### NEMA17 stepper motor bracket

An L-shaped face-mount bracket generated from the official NEMA17 motor-mount table, with the correct 31 mm bolt circle, a center pilot bore for the boss, four M3 screw holes, and a slotted base so you can square the motor before tightening. Bolt a real NEMA17 stepper straight on.

```openscad
// NEMA17 stepper motor bracket
//
// Source: OpenSCAD-hardware-database — BSD-2-Clause
// Generated from the library above, used under its license.

include <hardware-db/lib/_hardware.scad>
include <hardware-db/lib/_3d_print.scad>
include <hardware-db/lib/_functions.scad>

$fn = 64;

// --- official NEMA17 mount geometry ---
mm        = GetDataTable("NEMA17", MotorMount);
size      = mm[MMSize];      // 42.3 mm flange
boss_d    = mm[MMCutDia];    // 22.1 mm pilot boss
bolt_span = mm[MMBolts];     // 31.0 mm bolt spacing (square)

// --- M3 screw clearance from the bolt table ---
m3        = GetDataTable("M3", Bolt);
screw_clr = m3[BoltDia];     // ~3.1 mm

plate     = 5;               // face plate thickness
margin    = 4;               // material around bolt circle
foot_len  = 22;              // base flange length
foot_h    = 4;               // base flange thickness

face = size + 2 * margin;
plate_h = face;              // how tall the vertical plate stands
web_h   = min(face * 0.6, foot_len);  // gusset web reach

// Coordinate model (all parts share the back-bottom edge so they fuse):
//   X = left/right (width)            : centered on 0
//   Y = depth out from the plate      : plate at front [0..plate], foot runs to +foot_len
//   Z = up                            : foot at [0..foot_h], plate rises to plate_h

module bracket() {
    difference() {
        union() {
            // vertical face plate: thin slab in Y, tall in Z
            translate([-face / 2, 0, 0])
                cube([face, plate, plate_h]);
            // horizontal foot: flat slab on the bed
            translate([-face / 2, 0, 0])
                cube([face, foot_len, foot_h]);
            // two triangular gusset webs joining plate to foot
            for (sx = [-1, 1])
                translate([sx * (face / 2) - (sx == 1 ? 4 : 0), plate, 0])
                    rotate([90, 0, 90])
                        linear_extrude(height = 4)
                            polygon([[0, 0], [web_h, 0], [0, web_h]]);
        }
        // ---- holes in the vertical plate (motor face) ----
        // center pilot bore for the motor boss
        translate([0, plate + 0.1, plate_h / 2])
            rotate([90, 0, 0])
                cylinder(d = boss_d + 0.4, h = plate + 0.2);
        // four mounting screw holes on the 31 mm bolt square
        for (sx = [-1, 1], sz = [-1, 1])
            translate([sx * bolt_span / 2, plate + 0.1, plate_h / 2 + sz * bolt_span / 2])
                rotate([90, 0, 0])
                    cylinder(d = screw_clr, h = plate + 0.2);
        // ---- adjustment slots in the foot ----
        for (sx = [-1, 1])
            translate([sx * face / 4, foot_len - 7, -0.1])
                hull()
                    for (dy = [-4, 4])
                        translate([0, dy, 0])
                            cylinder(d = screw_clr, h = foot_h + 0.2);
    }
}

bracket();
```

#### HSW Blank Baseplate

A flat blank plate on swappable wall plugs — a starting point to build your own accessory, glue parts to, or mount hardware. Pick the size and plug count.

```openscad
// HSW Blank Baseplate
//
// Source: honeycomb-wall blank baseplate
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across
connectors_x = 2;             // [1:8]
// Hex connector rows
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Plate] */
// Pad width (mm)
pad_width = 80;               // [30:200]
// Pad height (mm)
pad_height = 80;              // [30:200]
// Pad thickness added in front (mm)
pad_t = 4;                    // [2:12]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
PLATE = PLATE_T_DEFAULT;

module baseplate_pad() {
    // a flat pad welded onto the +Z plate face (the blank build surface)
    translate([-pad_width / 2, -pad_height / 2, PLATE - HSW_WELD])
        cube([pad_width, pad_height, pad_t + HSW_WELD]);
}
module baseplate() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    baseplate_pad();
}
hsw_display() baseplate();
```

#### HSW Bin

A parametric wall bin split into sections, with optional honeycomb-pattern panels on the front, back, sides, separators and bottom. Clips on with hex connectors.

```openscad
width = 100;
back_height = 20;
front_height = 15;
depth = 30;

sections = 3;
wall_thickness = 2;
separator_thickness = 1;

front_wall_honeycomb = false;
back_wall_honeycomb = false;
sides_honeycomb = false;
separators_honeycomb = true;
bottom_honeycomb = false;

//-----------------
/* [Honeycomb pattern] */ 

// diameter in mm
hexagon_size = 10;
// mm
hexagon_spacing = 1;

hex_radius = hexagon_size / 2;
hex_width = sqrt(3) * hex_radius;
raster_spacing = hex_width + hexagon_spacing;


//-----------------
/* [HSW connector] */ 

// inner distance between corners
connector_size = 15.47;
connector_depth = 13;
tolerance = 0.1;
hexagon_distance = 40.88;

/* functions*/
function hexagon(radius) = [
    for (i = [0:5])
        [radius * cos(i * 60), radius * sin(i * 60)]
];

module hsw_connector() {
    rotate([90, 0, 0])
    linear_extrude(height = connector_depth)
    offset(r = -tolerance)
    polygon(points = hexagon(connector_size / 2));
}

module connectors() {
    count = max(1, round(width / (hexagon_distance + connector_size / 2)));
    dist = (width - (count-1) * hexagon_distance) / 2;
    h = (connector_size/2 - tolerance)*sqrt(3)/2;
    
    translate([dist, connector_depth - wall_thickness, h])
    for (i = [1 : count]) {
        translate([(i-1) * hexagon_distance, 0, 0])
        hsw_connector();    
    }
    
    rotate([90, 0, 0])
    hex_panel(width, back_height, wall_thickness, !back_wall_honeycomb, true);
}

module hexagon_cutout() {
    circle(hex_radius, $fn=6);
}
 
module hex_grid(size_x, size_y, solid = false, frame = false) {
    difference(){
        square([size_x, size_y]);
        
        if (!solid) {
            for (x = [-hex_radius : raster_spacing - 1 : size_x + hex_radius]) {
                for (y = [-hex_radius : raster_spacing : size_y + hex_radius]) {
                    translate(
                        [x,
                        y + (((x / raster_spacing) % 2) * raster_spacing / 2)])
                        hexagon_cutout();
                }
            }
        }
    }
    
    if (frame) {
        difference() {
            square([size_x, size_y]);
            
            translate([wall_thickness, wall_thickness])
            square([size_x - wall_thickness * 2, size_y - wall_thickness * 2]);
        }
    }
}

module hex_panel(size_x, size_y, thickness, solid = false, frame = false) {
    linear_extrude(thickness)
    hex_grid(size_x, size_y, solid, frame);
}

module hex_side(thickness = wall_thickness, solid = false) {
    linear_extrude(thickness)
    hex_grid(depth, back_height, solid);
}

module side(thickness = wall_thickness, solid = false) {
    intersection() {
        translate([wall_thickness, wall_thickness, 0])
        rotate([90, 0, -90])
        hex_side(thickness, solid);
        
        rotate([0, 90, 0])
        linear_extrude(wall_thickness)
        polygon([[0, 0], [0, -depth], [-front_height, -depth], [-back_height, 0]]);
    }

    translate([wall_thickness-thickness, 0, 0])
    rotate([0,90,0])
    linear_extrude(thickness)
    difference() {
        polygon([[0, 0], [0, -depth], [-front_height, -depth], [-back_height, 0]]);

        offset(-wall_thickness)
        polygon([[0, 0], [0, -depth], [-front_height, -depth], [-back_height, 0]]);
    }
}


module tray() {
    // bottom
    translate([0, -depth, 0])
    hex_panel(width, depth, wall_thickness, !bottom_honeycomb, true);

    // front
    translate([0, wall_thickness - depth, 0])
    rotate([90, 0, 0])
    hex_panel(width, front_height, wall_thickness, !front_wall_honeycomb, true);
    
    // sides
    side(wall_thickness, !sides_honeycomb);
    
    translate([width - wall_thickness, 0, 0])
    side(wall_thickness, !sides_honeycomb);
}

module separators() {
    section_count = max(1, sections);
    count = max(section_count - 1, 1);
    for (i = [1 : count]) {
        translate([i * width / (count + 1), 0, 0])
        side(separator_thickness, !separators_honeycomb);
    }
}

/* MAIN */
connectors();
tray();
if (sections > 1) {
    separators();
}
```

#### HSW Brush / Marker Cup

A tall, narrow cup for paintbrushes, markers, files, or long slim tools — keeps them upright and visible. Optional drain. Swappable wall plugs.

```openscad
// HSW Brush / Marker Cup
//
// Source: honeycomb-wall tall brush cup
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across
connectors_x = 1;             // [1:8]
// Hex connector rows (2 = better for a tall cup)
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Cup] */
// Inner bore diameter (mm)
cup_inner_d = 36;             // [18:70]
// Side wall thickness (mm)
cup_wall = 2.4;               // [1.2:0.2:4]
// Cup height (mm)
cup_height = 85;              // [40:160]
// Base thickness (mm)
cup_base_t = 2.4;             // [1.2:0.2:5]
// Drain hole through the base
drain = true;
// Drain hole diameter (mm)
drain_d = 6;                  // [3:16]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module brush_cup_body() {
    cup_outer_d = cup_inner_d + 2 * cup_wall;
    cup_outer_r = cup_outer_d / 2;
    translate([0, 0, PLATE + cup_outer_r]) {
        difference() {
            rotate([-90, 0, 0]) cylinder(h = cup_height, d = cup_outer_d);
            translate([0, cup_base_t, 0]) rotate([-90, 0, 0])
                cylinder(h = cup_height - cup_base_t + eps, d = cup_inner_d);
            if (drain)
                translate([0, -eps, 0]) rotate([-90, 0, 0])
                    cylinder(h = cup_base_t + 2 * eps, d = drain_d);
        }
    }
    // web welding the cup back to the plate face
    web_w = cup_outer_d - 2;
    web_h = cup_height * 0.6;
    translate([-web_w / 2, 0, PLATE - HSW_WELD])
        cube([web_w, web_h, cup_outer_r + HSW_WELD]);
}
module brush_cup() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    brush_cup_body();
}
hsw_display() brush_cup();
```

#### HSW Cable Hook

A wide-throat hook for coiled cables, extension leads, lanyards, or straps — the broad rounded curve is gentle on cable jackets. Swappable wall plugs.

```openscad
// HSW Cable Hook
//
// Source: honeycomb-wall cable hook
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across
connectors_x = 1;             // [1:8]
// Hex connector rows (2 = strong cantilever)
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Hook] */
// Hook rod radius (mm)
rod_r = 6;                    // [3:0.5:12]
// How far the hook reaches out (mm)
reach = 30;                   // [15:80]
// Throat opening height (mm)
throat = 26;                  // [12:60]
// Upturned tip rise (mm)
rise = 12;                    // [4:30]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
PLATE = PLATE_T_DEFAULT;

module hook_path() {
    // wide gentle throat: out, sweep down to the throat bottom, finish with an upturn
    yb = -throat;                                  // throat bottom depth
    pts = [
        [0, 0,        PLATE - HSW_WELD],           // embedded in the plate
        [0, 0,        PLATE + 5],                  // out from the wall
        [0, yb * 0.4, PLATE + reach * 0.55],       // descend
        [0, yb,       PLATE + reach * 0.88],       // throat bottom
        [0, yb,       PLATE + reach],              // along the bottom
        [0, yb + rise, PLATE + reach - 2]          // upturned tip (retains the cable)
    ];
    for (i = [0 : len(pts) - 2])
        hull() {
            translate(pts[i])     sphere(r = rod_r, $fn = 28);
            translate(pts[i + 1]) sphere(r = rod_r, $fn = 28);
        }
}
module cable_hook() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    hook_path();
}
hsw_display() cable_hook();
```

#### HSW spring clip (snap-lock connector)

The bare snap-in spring connector that locks into a single hex hole - push it in and the barbs catch behind the wall lip.

```openscad
// ============================================================================
//  HSW spring clip — self-contained snap-lock connector for one wall cell.
//  The profile uses an inline rounded-polygon helper (round_corners, circle
//  method) so it needs no external libraries.
// ============================================================================
$fn = 48;

// ---- honeycomb-wall interface constants ----
HSWX_INSIDE       = 20;
HSWX_WALL         = 1.8;
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;     // 23.6
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);
HSWX_OD           = HSWX_SIDE * 2;
HSWX_DEPTH        = 8;
HSWX_LOCK_DEPTH   = 5;
HSWX_LOCK_LIP     = 1;
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;

// ---- plug constants ----
plug_base_thickness   = 1;
plug_base_lip         = 1;
plug_stud_thickness   = 1.7;
plug_stud_height      = HSWX_LOCK_DEPTH-2;
plug_spring_base      = 3;
plug_spring_depth     = 5;
plug_spring_thickness = 1.4;

// ============================================================================
//  Inline round_corners (circle method) — self-contained
// ============================================================================
function _unit(v) = v / norm(v);
function _approx(a,b) = abs(a-b) < 1e-9;
// angle (deg) at the middle point p1, between vectors (p0-p1) and (p2-p1)
function _vangle3(p0,p1,p2) =
    let(a=p0-p1, b=p2-p1)
    acos( max(-1, min(1, (a*b)/(norm(a)*norm(b)) )) );
// minor arc from 'start' to 'end' about center 'cp', N points
function _arcpts(N, cp, start, end) =
    let(
        a0 = atan2(start[1]-cp[1], start[0]-cp[0]),
        a1 = atan2(end[1]-cp[1],   end[0]-cp[0]),
        r  = norm(start-cp),
        d0 = a1 - a0,
        da = d0 - 360*floor((d0+180)/360)      // normalize to (-180,180]
    )
    [ for(j=[0:N-1]) cp + r*[cos(a0+da*j/(N-1)), sin(a0+da*j/(N-1))] ];
// one rounded corner (radius measure)
function _circlecorner(p0,p1,p2,r,fn) =
    let(
        ang  = _vangle3(p0,p1,p2)/2,
        d    = r/tan(ang),
        prev = _unit(p0-p1),
        next = _unit(p2-p1)
    )
    _approx(ang,90) ? [p1+prev*d, p1+next*d] :
    let(
        center = r/sin(ang)*_unit(prev+next) + p1,
        start  = p1+prev*d,
        end    = p1+next*d,
        N      = max(3, ceil((90-ang)/180*fn))
    )
    _arcpts(N, center, start, end);
// round_corners(circle) for a CLOSED path. pts=[[x,y]...], rds=[r...] (0=sharp)
function _round_corners(pts, rds, fn=90) =
    let(n = len(pts))
    [ for(i=[0:n-1]) each
        rds[i]==0 ? [pts[i]]
                  : _circlecorner(pts[(i-1+n)%n], pts[i], pts[(i+1)%n], rds[i], fn)
    ];

// ============================================================================
//  hsw_clip — profile outline + rounding
// ============================================================================
module hsw_clip(center=false, rotate=0, hexbase=0, rectbase=0, slop=0) {
    hswx_inside = HSWX_INSIDE - slop;
    // outline points
    pts = [
        [ -plug_base_lip,                              0 ],
        [           slop,                              0 ],
        [           slop,                HSWX_LOCK_DEPTH ],
        [ -HSWX_LOCK_LIP,  HSWX_LOCK_LIP+HSWX_LOCK_DEPTH ],
        [ -HSWX_LOCK_LIP,                     HSWX_DEPTH ],
        [ plug_stud_thickness,                HSWX_DEPTH ],
        [ plug_spring_base,                   HSWX_DEPTH ],
        [ hswx_inside/2,  HSWX_DEPTH-plug_spring_depth-0.5 ],
        [ hswx_inside-plug_spring_base,       HSWX_DEPTH ],
        [ hswx_inside-plug_stud_thickness,    HSWX_DEPTH ],
        [ hswx_inside,                        HSWX_DEPTH ],
        [ hswx_inside+HSWX_LOCK_LIP, HSWX_LOCK_DEPTH+HSWX_LOCK_LIP ],
        [ hswx_inside,                   HSWX_LOCK_DEPTH ],
        [ hswx_inside,                              0.1 ],
        [ hswx_inside+0.1,                           0 ],
        [ hswx_inside-plug_stud_thickness-0.5,       0 ],
        [ hswx_inside-plug_stud_thickness,         0.5 ],
        [ hswx_inside-plug_stud_thickness, HSWX_DEPTH-plug_spring_thickness-1.4 ],
        [ hswx_inside-plug_spring_base+.1, HSWX_DEPTH-plug_spring_thickness-0.1 ],
        [ HSWX_INSIDE/2,  HSWX_DEPTH-plug_spring_depth-plug_spring_thickness ],
        [ plug_spring_base, HSWX_DEPTH-plug_spring_thickness ],
        [ plug_stud_thickness,              HSWX_DEPTH/3 ],
        [ HSWX_INSIDE/3.4,                           0 ],
        [ plug_stud_thickness,                       0 ],
    ];
    // per-vertex rounding radii (0 = sharp)
    rds = [ 0, 0.1, 0, 0.5, 1.5, 0, 3.5, 5.0, 3.5, 0, 2.0, 0.5, 0,
            0.1, 0, 0, 1.0, 1.5, 1.0, 6.0, 2.0, 1.0, 0, 0 ];

    translate(center ? [0,0,0] : [HSWX_INSIDE/2, HSWX_INSIDE/4, 0])
    rotate([0,0,rotate])
        union() {
            translate([-HSWX_INSIDE/2, -HSWX_INSIDE/4, 0])
                rotate([-90,0,0])
                    linear_extrude(HSWX_INSIDE/2)
                        polygon( _round_corners(pts, rds, 90) );
        }
}

// render the bare connector for inspection
hsw_clip(center=true);
```

#### HSW Attach-to-wall insert (screw-through)

A latching insert with a countersunk screw hole through its base - screw any flat object onto the honeycomb wall. Tune holeDiameter / facetHeight / bottomThickness.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insertAttachToWall(4, facetHeight = 1, bottomThickness = 1.5);
```

#### HSW Attach-to-wall insert (empty, supportless)

The attach-to-wall insert with the centre bored out and a supportless bridge so it prints cleanly with no supports.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insertAttachToWallEmpty(3.5, facetHeight = 1, bottomThickness = 1.5, supportless = true);
```

#### HSW Hex plug

A plain hexagonal plug that press-fits into an empty insert (or a wall cell) to blank it off or anchor a part.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
hexagonPlug(decorations = false);
```

#### HSW Insert (solid latching)

A solid hexagonal insert that snaps into one honeycomb wall cell with sprung side latches and a flat decorated face - the base building block other attachments grow from.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insert();
```

#### HSW Insert base (friction, no latches)

The bare insert body without the spring latches - a friction-only building block for designs that add their own retention.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insertBase();
```

#### HSW Insert (empty / hollow)

A latching wall insert with its centre bored through - a hollow socket to pass a fastener, cable, or your own plug through.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insertEmpty();
```

#### HSW Insert (with mounting plate)

A latching wall insert capped by a solid plate face - a blank mounting pad to build your own accessory onto.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insert_with_plate();
```

#### HSW M10 nut insert

A wall insert with a captive M10 nut pocket and screw clearance for bolting accessories to the wall.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insertM10();
```

#### HSW M2.5 nut insert

A wall insert with a captive M2.5 nut pocket and screw clearance - bolt accessories straight to the wall. (Sister recipes cover M3-M10; call insertM3()..insertM10() for other sizes.)

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insertM2_5();
```

#### HSW M3 nut insert

A wall insert with a captive M3 nut pocket and screw clearance for bolting accessories to the wall.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insertM3();
```

#### HSW M4 nut insert

A wall insert with a captive M4 nut pocket and screw clearance for bolting accessories to the wall.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insertM4();
```

#### HSW M5 nut insert

A wall insert with a captive M5 nut pocket and screw clearance for bolting accessories to the wall.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insertM5();
```

#### HSW M6 nut insert

A wall insert with a captive M6 nut pocket and screw clearance for bolting accessories to the wall.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insertM6();
```

#### HSW M8 nut insert

A wall insert with a captive M8 nut pocket and screw clearance for bolting accessories to the wall.

```openscad
/* Library of hex wall bits */
/* updated */
$fn = 64;  // curve smoothness

/* Constants relating to the insert part */
INSERT_MAIN_OUTER_AF_DISTANCE = 19.7;
INSERT_MAIN_INNER_AF_DISTANCE = 13.4;
INSERT_LIP_AF_DISTANCE = 22.5;
INSERT_MAIN_OUTER_DIAMETER = af_to_diameter(INSERT_MAIN_OUTER_AF_DISTANCE);
INSERT_MAIN_INNER_DIAMETER = af_to_diameter(INSERT_MAIN_INNER_AF_DISTANCE);
INSERT_LIP_DIAMETER = af_to_diameter(INSERT_LIP_AF_DISTANCE);
INSERT_TOTAL_HEIGHT = 10;
INSERT_LIP_HEIGHT = 2.5;
INSERT_LIP_PROTRUSION = (INSERT_LIP_AF_DISTANCE - INSERT_MAIN_OUTER_AF_DISTANCE)/2;

/* Constants relating to the honeycomb itself */
HEXAGON_WIDTH = 20;
HEX_WALL_THICKNESS = 3.6;
HEX_SEPARATION = HEXAGON_WIDTH + HEX_WALL_THICKNESS;
HORIZONTAL_HEX_SEPARATION = HEX_SEPARATION * 2 / sqrt(3) + af_to_diameter(HEXAGON_WIDTH + HEX_WALL_THICKNESS)/2;

/* Constants relating to the plug that can go into an empty insert */
PLUG_HEXAGON_DIAMETER = 14.7;
PLUG_AF_SIZE = PLUG_HEXAGON_DIAMETER * sqrt(3) / 2;
PLUG_LENGTH = 13;

DECORATION_OUTER_DIAMETER = 21.362;
DECORATION_INNER_DIAMETER = 19.053;
DECORATION_DEPTH = 0.3;
DECORATEION_FACET_HEIGHT = 0.35;

tiny_distance = .001;
maxInnerDiameter = 16.5;

cellAf = 23.6;
cellD = af_to_diameter(cellAf);

// insert_empty();
// insert_with_plate();
// insert_horizontal();
// hexagon_plug();
// hexagon_plug_horizontal();
// ^ this items has no decorations for compatibility

//insert(); 		// same as insert_with_plate(); but no cut hexagon underneeze
//insertEmpty(); 	// same as insert_empty();
//insertM2_5();
//insertM3();
//insertM4();
//insertM5();
//insertM6();
//insertM8();
//insertM10();
//insertAttachToWall(5, facetHeight = 1, bottomThickness = 1.5, translateHole = [2.5,-3.5], decorations=false);
//insertAttachToWallEmpty(3.5, facetHeight = 2, bottomThickness = 3, withLatch = false, decorations = true, supportless = true);
//hexagonPlug();
//hexagonPlugHorizontal();

//makeInsertHorizontal() insertEmpty(); // <- any insert 


/*
grid(changeCellOrder=false) {
	row(decorations = false) {
		insertAttachToWallEmpty(4, decorations=false, withLatch=true, facetHeight = 1.6);
		insertEmpty(decorations=false);
	}
	row(decorations = false) {
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
		insertEmpty(decorations=false);
	}
}
//*/

module insert_empty(tolerance = 0, inner_tolerance = 0) insertEmpty(tolerance, inner_tolerance, decorations=false);

module insert_with_plate(tolerance = 0, inner_tolerance = 0) { 
	difference() {
		insert(tolerance, decorations=false);
		cutHexagon(inner_tolerance, INSERT_LIP_HEIGHT);
	}
}

module insert_horizontal(tolerance = 0, inner_tolerance = 0) {
	makeInsertHorizontal(tolerance) insert_with_plate(tolerance);
}

module hexagon_plug(tolerance = 0) {
    cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
}

module hexagon_plug_horizontal(tolerance = 0) {
    translate([0, 0, PLUG_AF_SIZE/2 - tolerance])
        rotate([90, 0, 0]) 
			hexagon_plug(tolerance);
}

module insert(tolerance = 0, decorations = true) {
	$fn = 6;
    WALL_SIDE_GAP_START_HEIGHT = 6.5;
    WALL_SIDE_GAP_WIDTH = 1;
    WALL_TOP_GAP_WIDTH = 0.8;
    WALL_GAP_LENGTH = 8;
    WALL_TOP_GAP_RADIUS = 8.25;
    TAB_STICKS_OUT_BY = 0.5;
    TAB_LENGTH = 2;


	difference() {
		union() {
			for (i=[0:5]) {
					rotate([0, 0, i*60]) {
					translate([0, INSERT_MAIN_OUTER_AF_DISTANCE/2 - tolerance, 0]) {
						hull() {
							translate([0, 0, WALL_SIDE_GAP_START_HEIGHT + WALL_SIDE_GAP_WIDTH + TAB_STICKS_OUT_BY]) {
								rotate([0,90,0]){
									cylinder(r=TAB_STICKS_OUT_BY, h=TAB_LENGTH, center=true, $fn=20);
								}
							}
							translate([0, 0, INSERT_TOTAL_HEIGHT - tiny_distance]) {
								rotate([0, 90, 0]) {
									cylinder(r=0.2, h=TAB_LENGTH, center=true, $fn=20);  // rounded retention-tab tip
								}
							}
						}
					}
				}
			}
			difference() {
				insertBase(tolerance, decorations);
				for (i=[0:5]) {
					rotate([0, 0, i*60]) {
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS - tolerance, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, WALL_TOP_GAP_WIDTH, 10]);
						}
						translate([-WALL_GAP_LENGTH/2, WALL_TOP_GAP_RADIUS, WALL_SIDE_GAP_START_HEIGHT]) {
							cube([WALL_GAP_LENGTH, 10, WALL_SIDE_GAP_WIDTH]);
						}
					}
				}
			}
		}
		cutFacetBottom();
	}
}

module insertEmpty(tolerance = 0, inner_tolerance = 0, decorations = true) {
	difference() {
		insert(tolerance, decorations);
		cutHexagon(inner_tolerance);
	}
}

module insertM2_5(tolerance=0, decorations=true) insertMX(2.5, 5.77, tolerance=tolerance, decorations=decorations);
module insertM3(tolerance=0, decorations=true)   insertMX(3, 6.35, tolerance=tolerance, decorations=decorations);
module insertM4(tolerance=0, decorations=true)   insertMX(4, 8.08, tolerance=tolerance, decorations=decorations);
module insertM5(tolerance=0, decorations=true)   insertMX(5, 9.24, tolerance=tolerance, decorations=decorations);
module insertM6(tolerance=0, decorations=true)   insertMX(6, 11.55, tolerance=tolerance, decorations=decorations);
module insertM8(tolerance=0, decorations=true)   insertMX(8, 15.01, tolerance=tolerance, decorations=decorations);
module insertM10(tolerance=0, decorations=true)  insertMX(10, 17.32, tolerance=tolerance, decorations=decorations);

function calculateFacetDiameter(holeD, facetHeight) = facetHeight * 2 + holeD;

module insertAttachToWall(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 1.5, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, // <- I think there is no need latch, but you can turn it on
	translateHole = [0,0], // <- if you drill hole not accurate, you can move it by [x, y],
	decorations = true
) {
	$fn=64;
	isTranslateHole = translateHole[0] != 0 || translateHole[1] != 0;
	spacer = 0.2;
	holeD = holeDiameter + spacer;
	outHoleDiameter = isTranslateHole ? maxInnerDiameter : holeD + 4;
	facetD = calculateFacetDiameter(holeD, facetHeight);

	difference() {
		if (withLatch)  
			insert(tolerance, decorations = decorations);
		 else  
			insertBase(tolerance, decorations = decorations);

		translate([0, 0, -bottomThickness]) cylinder(d = outHoleDiameter, h = INSERT_TOTAL_HEIGHT); 

		if (isTranslateHole) 
			translate([translateHole[0], translateHole[1], 0]) hole();
		else 
			hole();
	}

	module hole() {
		translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness - 0.1]) cylinder(d1 = facetD, d2 = holeD, h = facetHeight + 0.1); 
		cylinder(d = holeD, INSERT_TOTAL_HEIGHT + 0.1); 
	}
}

module insertAttachToWallEmpty(
	holeDiameter, 
	facetHeight = 0, 
	bottomThickness = 2, 
	tolerance = 0, 
	inner_tolerance = 0, 
	withLatch = false, 
	decorations = true,
    supportless = false,
) {
    layerThickness = 0.2;

	difference() {
		insertAttachToWall(holeDiameter, facetHeight, bottomThickness, tolerance, inner_tolerance, withLatch, decorations = decorations);
		cutHexagon(inner_tolerance, -bottomThickness);
	}
    if (supportless) {
        translate([0, 0, INSERT_TOTAL_HEIGHT - bottomThickness]) 
            difference() {
                cylinder(d = PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 2, $fn = 6, center = true);  
                cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight), layerThickness * 4], center = true);  
            }
        translate([0,0,INSERT_TOTAL_HEIGHT-bottomThickness]) 
            difference() {
                cylinder(d=PLUG_HEXAGON_DIAMETER * 1.1, h = layerThickness * 4, $fn = 6, center = true);  
                rotate(90, [0, 0, 1]) 
                    cube([PLUG_HEXAGON_DIAMETER + 1, calculateFacetDiameter(holeDiameter, facetHeight),layerThickness* 2 * 2 * 2], center = true);  
            }
    }
}

module hexagonPlug(tolerance=0, decorations=true) {
	if (decorations) {
		D = PLUG_HEXAGON_DIAMETER - tolerance*2;
		intersection() {
			cylinder(d=D, h=PLUG_LENGTH, $fn=6);
			translate([0,0,-0.75])
				cylinder(r1=PLUG_LENGTH + D/2, r2=0, h = PLUG_LENGTH + D/2, $fn=6); 
			translate([0,0,-D/2 + 0.75])
				cylinder(r2=PLUG_LENGTH + D/2, r1=0, h = PLUG_LENGTH + D/2, $fn=6); 
		}
	} else {
		cylinder(d=PLUG_HEXAGON_DIAMETER - tolerance*2, h=PLUG_LENGTH, $fn=6);
	}
}

module hexagonPlugHorizontal(tolerance=0, decorations=true) {
	translate([0,0, PLUG_AF_SIZE/2] )
	translate([0,0,-INSERT_MAIN_OUTER_AF_DISTANCE/2] )
	makeInsertHorizontal() hexagonPlug(tolerance, decorations);
}

module makeInsertHorizontal(tolerance=0) {
    large_distance=10;
    difference() {
        translate([0, 0, INSERT_MAIN_OUTER_AF_DISTANCE/2]) 
            rotate([90, 0, 0])
                children();
        translate([-large_distance, -INSERT_TOTAL_HEIGHT - 0.1, -large_distance + tolerance]) 
            cube([large_distance*2, INSERT_TOTAL_HEIGHT + 0.2, large_distance]);
    }
}

module grid(rows=[], changeCellOrder = false, maxRowLength = 10) {
	noRowsInfo = len(rows) == 0;
	upCutRowCount = noRowsInfo ? maxRowLength : rows[0];
	downCutRowCount = noRowsInfo ? maxRowLength : rows[len(rows)-1];
	
	assert( !(!noRowsInfo && len(rows) != $children), 
		"rows info if specified must describe all rows! Example: grid(rows=[1,2]) { row() { insert(); } row() { insert(); insert(); } }");

	difference() {
		for (i = [0:$children-1]) {
			tY = cellOffsetY(i, changeCellOrder);
			translate([(cellD - cellD / 4) * i, tY, 0]) children(i);
		}
		for (i = [0:$children-1]) {

			tY = cellOffsetY(i, changeCellOrder); 
			translate([(cellD - cellD / 4) * i, tY  - cellAf, 0]) connectorCutting();

			if (!noRowsInfo) {
				itemsInRow = rows[i];
				for (k=[0:maxRowLength-1]){
					translate([(cellD - cellD / 4) * i, tY  + (k + itemsInRow) * cellAf, 0]) connectorCutting();
				}
			}
		}

		tYUp = cellOffsetY($children, changeCellOrder);
		translate([(cellD - cellD / 4) * $children, -tYUp, 0]){
			for (j=[0:downCutRowCount-1]) {
				translate([0, cellAf * j, 0]) connectorCutting();
			}
		}
		tYDown = cellOffsetY(-1, changeCellOrder);
		translate([-(cellD - cellD / 4), -tYDown, 0])
			for (k=[0:upCutRowCount-1]) {
				translate([0, cellAf * k, 0]) connectorCutting();
			}
	}
}

module row(connections=true, decorations = true) {
	if ($children > 0) {
		difference() {
			for (i = [0:$children-1]) 
				translate([0, cellAf * i, 0]) {
					if (connections) connector(false, decorations);
					children(i);
				}
			translate([(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
			translate([0, cellAf * $children, 0]) connectorCutting();
			translate([-(cellD - cellD / 4) , cellAf * $children - cellAf/2, 0]) connectorCutting();
		}
	}
}

// utils modules section ----------------------------------------------------

module insertBase(tolerance=0, decorations=true) {
    $fn = 6;
    difference() {
        union() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER-tolerance*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=INSERT_LIP_DIAMETER, h=INSERT_LIP_HEIGHT);
        }
		if (decorations) {
			translate([0,0,-0.1])
				union() {
					difference() {
						cylinder(d = DECORATION_OUTER_DIAMETER, h = DECORATION_DEPTH + 0.1);
						cylinder(d = DECORATION_INNER_DIAMETER, h = DECORATION_DEPTH + 0.1);
					}
				}

			translate([0,0,DECORATEION_FACET_HEIGHT]) cutFacet(INSERT_LIP_DIAMETER, inverse=true);
		}
        translate([0,0,INSERT_LIP_HEIGHT])
        rotate(90,[0,0,1])
        difference() {
            cylinder(d=INSERT_MAIN_OUTER_DIAMETER*2, h=INSERT_TOTAL_HEIGHT);
            cylinder(d=af_to_diameter(INSERT_MAIN_OUTER_DIAMETER-tolerance*2) - 0.12 * 2, h=INSERT_TOTAL_HEIGHT);
        }
        cutFacetBottom();
    }
}

module cutFacet(diameter, inverse=false) {
	h = diameter / 2 * tan(45);
	z = inverse ? -h : 0;
	translate([0,0,z])
		difference() {
			cylinder(d=diameter*2, h = h);
			if (inverse)
				cylinder(d2=diameter, d1=0, h = h);
			else
				cylinder(d1=diameter, d2=0, h = h);
		}
}

module cutFacetBottom() {
	translate([0,0,INSERT_TOTAL_HEIGHT-DECORATEION_FACET_HEIGHT])
		cutFacet(INSERT_MAIN_OUTER_DIAMETER);
}

module cutHexagon(inner_tolerance, indent = 0) {
	// remove preview artifacts
	z = indent == 0 ? -0.5 : indent;
	addH = indent == 0 ? 1 : 0;
	translate([0,0,z]) 
		cylinder(d=INSERT_MAIN_INNER_DIAMETER + inner_tolerance*2, h=INSERT_TOTAL_HEIGHT + addH, $fn=6);
}

module insertMX(diameter,  nutWidth, tolerance = 0, decorations = true) {
	spacer = 0.2;
	holeD = min(maxInnerDiameter, diameter + spacer);
	facet = holeD / 8;
	facetH = 0.3;
	nutW = min(maxInnerDiameter, nutWidth + spacer);
	aboveNutW = min(maxInnerDiameter, nutW + 6);

	difference() {
		insert(tolerance, decorations = decorations);
		translate([0,0,-0.1]) // remove preview artifacts
			cylinder(d1 = holeD + facet * 2, d2 = holeD, h = facetH + 0.1);
		cylinder(d = holeD, h = INSERT_TOTAL_HEIGHT);
		translate([0, 0, 4])
		cylinder(d = nutW, h = INSERT_TOTAL_HEIGHT, $fn=6);
		translate([0, 0, 7])
		cylinder(d = aboveNutW, h = INSERT_TOTAL_HEIGHT + 0.1, $fn=6);
	}
}

module connector(forCutting=false, decorations = true) {
	connectorW = (cellAf-INSERT_LIP_AF_DISTANCE);
	connectorH = decorations ? INSERT_LIP_HEIGHT - DECORATEION_FACET_HEIGHT : INSERT_LIP_HEIGHT;
	connectorL = INSERT_LIP_DIAMETER/2;
	cuttingTranslateZ = forCutting ? -2 : 0;
	cuttingScaleZ = forCutting ? 2 : 1;

	translate([0,0,cuttingTranslateZ])
		scale([1,1,cuttingScaleZ]) 
			for (a=[0:60:300])
			rotate(a,[0,0,1])
				translate([-connectorL/2, -INSERT_LIP_AF_DISTANCE/2 - connectorW, decorations ? DECORATEION_FACET_HEIGHT : 0]) {
				points = [
					[0, addCutting(connectorW)],
					[connectorL, addCutting(connectorW)],
					[connectorL, 0],
					[0, 0],
					[-(connectorW*sin(60)), connectorW/2]
				];
				linear_extrude(height = connectorH) polygon(points);
			}

	function addCutting(value) = forCutting ? value + 0.1 : value;
}

module connectorCutting() connector(true, true);
function cellOrder(i, change) = change ? i % 2 == 0 : i % 2 != 0;
function cellOffsetY(i, change) = cellOrder(i, change) ? 0 : cellAf / 2;

/* converts an 'across flats' dimension to a maximum diameter — useful for openScad */
function af_to_diameter(af) = af / sqrt(3) * 2;
function diameter_to_af(diameter) = diameter * sqrt(3) / 2;

// ---- part ----
insertM8();
```

#### HSW Deep Bin

A tall, fully-enclosed bin for long parts, bottles, brushes, or rolled items, on swappable wall plugs. Open top, full-height walls.

```openscad
// HSW Deep Bin
//
// Source: honeycomb-wall deep bin
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across
connectors_x = 2;             // [1:8]
// Hex connector rows (taller = more)
connectors_y = 3;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Bin] */
// Inside width (mm)
bin_width = 55;               // [25:160]
// Inside depth out from the wall (mm)
bin_depth = 55;               // [20:120]
// Inside height (mm)
bin_height = 95;              // [40:200]
// Wall thickness (mm)
wall = 2.4;                   // [1.2:0.2:4]
// Floor thickness (mm)
floor_t = 2.6;                // [1.2:0.2:5]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module deep_bin() {
    ow = bin_width + 2 * wall;
    od = bin_depth + 2 * wall;
    oh = bin_height + floor_t;
    z0 = PLATE - HSW_WELD;
    y0 = -oh / 2;                       // centre vertically so the back wall covers the plate
    difference() {
        translate([-ow / 2, y0, z0]) cube([ow, oh, od + HSW_WELD]);
        translate([-bin_width / 2, y0 + floor_t, PLATE + wall])
            cube([bin_width, bin_height + eps, bin_depth + eps]);   // cavity, open top
    }
}
module deep_parts_bin() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    deep_bin();
}
hsw_display() deep_parts_bin();
```

#### HSW Divided Tray

A shallow tray split into a grid of compartments for sorting screws, beads, SMD parts, or findings. Choose the number of columns and rows. Swappable wall plugs.

```openscad
// HSW Divided Tray
//
// Source: honeycomb-wall divided sorting tray
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across (a wide tray wants 3+)
connectors_x = 3;             // [1:8]
// Hex connector rows
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Tray] */
// Inside width (mm)
tray_width = 130;             // [40:250]
// Inside depth out from the wall (mm)
tray_depth = 55;              // [25:120]
// Inside height (mm)
tray_height = 28;             // [12:80]
// Compartment columns
cols = 4;                     // [1:10]
// Compartment rows
rows = 2;                     // [1:6]
// Outer wall thickness (mm)
wall = 2.4;                   // [1.2:0.2:4]
// Divider thickness (mm)
divider_t = 1.6;             // [1:0.2:3]
// Floor thickness (mm)
floor_t = 2.4;               // [1.2:0.2:5]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module divided_tray() {
    ow = tray_width + 2 * wall;
    od = tray_depth + 2 * wall;
    oh = tray_height + floor_t;
    z0 = PLATE - HSW_WELD;
    y0 = -oh / 2;
    difference() {
        translate([-ow / 2, y0, z0]) cube([ow, oh, od + HSW_WELD]);
        // hollow the inside (open top)
        translate([-tray_width / 2, y0 + floor_t, PLATE + wall])
            cube([tray_width, tray_height + eps, tray_depth + eps]);
    }
    // dividers (thin walls) rising from the floor, welded to the side walls
    for (c = [1 : cols - 1])
        translate([-tray_width / 2 + c * tray_width / cols - divider_t / 2, y0 + floor_t - eps, PLATE + wall])
            cube([divider_t, tray_height, tray_depth]);
    for (r = [1 : rows - 1])
        translate([-tray_width / 2, y0 + floor_t - eps, PLATE + wall + r * tray_depth / rows - divider_t / 2])
            cube([tray_width, tray_height, divider_t]);
}
module divided_tray_part() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    divided_tray();
}
hsw_display() divided_tray_part();
```

#### HSW Drill-Bit Block

A block with a grid of graduated holes to organise drill bits, end mills, or small round tools by size. Swappable wall plugs.

```openscad
// HSW Drill-Bit Block
//
// Source: honeycomb-wall drill-bit block
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across
connectors_x = 2;             // [1:8]
// Hex connector rows
connectors_y = 1;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Block] */
// Holes across (X)
cols = 6;                     // [1:12]
// Hole rows (depth)
rows = 3;                     // [1:6]
// Smallest bore diameter (mm)
bore_min = 3;                 // [1:10]
// Largest bore diameter (mm)
bore_max = 9;                 // [2:16]
// Spacing between holes (mm)
pitch = 13;                   // [6:25]
// Bore depth (mm)
bore_depth = 22;             // [6:55]
// Solid base under the bores (mm)
base_t = 5;                  // [2:15]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module drill_block() {
    spanx = (cols - 1) * pitch;
    spany = (rows - 1) * pitch;
    bw = spanx + bore_max + 8;        // X width
    bd = spany + bore_max + 8;        // Z depth out from wall
    bh = bore_depth + base_t;         // Y height
    z0 = PLATE - HSW_WELD;
    y0 = -bh / 2;                     // centre on the plate
    zc = PLATE + (bd - spany) / 2;    // front margin for the first row
    difference() {
        translate([-bw / 2, y0, z0]) cube([bw, bh, bd + HSW_WELD]);
        for (cx = [0 : cols - 1])
            for (cy = [0 : rows - 1]) {
                d = bore_min + (cols > 1 ? (bore_max - bore_min) * cx / (cols - 1) : 0);
                translate([-spanx / 2 + cx * pitch, y0 + bh + eps, zc + cy * pitch])
                    rotate([90, 0, 0]) cylinder(h = bore_depth, d = d);   // vertical bore, open top
            }
    }
}
module drill_bit_block() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    drill_block();
}
hsw_display() drill_bit_block();
```

#### HSW honeycomb wall (grid panel)

The HSW panel itself - a staggered grid of 20 mm hex sockets with the snap-lock lip, sized by columns and rows. Everything else clips into it.

```openscad
// HSW grid — fast slab-minus-lipped-hex-holes build.
// Renders any size in ~seconds (one difference instead of a union of many
// struts), with the standard honeycomb-wall snap-lock profile. Self-contained.

$fn = 48;
cols = 3;
rows = 3;
depth = 8;

HSWX_INSIDE  = 20;
HSWX_WALL    = 1.8;
HSWX_OUTSIDE = HSWX_INSIDE + HSWX_WALL*2;   // 23.6
R_out        = HSWX_OUTSIDE / sqrt(3);      // outer circumradius 13.626
PITCH_X      = 1.5 * R_out;                 // 20.44 flat-top column pitch
PITCH_Y      = HSWX_OUTSIDE;                // 23.6  row pitch
STAG_Y       = HSWX_OUTSIDE / 2;            // 11.8  alternate-column stagger

// flat-top hexagon, flat-to-flat = af, as a 2D shape
module hexF(af) { circle(r = af/sqrt(3), $fn = 6); }

// one lipped hex socket: front mouth at z=0, bore into -z, lock step at 5.1-6mm.
// profile (the standard wall-hole inverse): 21mm chamfered mouth -> 20mm bore
// (front 5.1mm) -> taper 20->22 (depth 5.1-6) -> 22mm pocket to the back.
module hsw_hole(d = depth) {
    eps = 0.02;
    // mouth chamfer: 21mm at z=0 down to 20mm at z=-0.5
    translate([0,0,-0.5]) linear_extrude(0.5+eps, scale = 21/20) hexF(20);
    // front bore: 20mm, z=-0.5 .. -5.1
    translate([0,0,-5.1]) linear_extrude(4.6+eps) hexF(20);
    // lock taper: 22mm at z=-6 up to 20mm at z=-5.1 (the catch step)
    translate([0,0,-6]) linear_extrude(0.9+eps, scale = 20/22) hexF(22);
    // back pocket: 22mm, z=-6 .. -d
    if (d > 6) translate([0,0,-d]) linear_extrude(d-6+eps) hexF(22);
}

module hsw_grid(cols = 3, rows = 3, d = depth) {
    spanX = (cols-1) * PITCH_X;
    spanY = (rows-1) * PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + STAG_Y;
    difference() {
        translate([0, 0, -d/2]) cube([slabW, slabD, d], center = true);
        for (c = [0:cols-1]) {
            stag = (c % 2 == 1) ? STAG_Y : 0;
            for (r = [0:rows-1]) {
                x = c*PITCH_X - spanX/2;
                y = r*PITCH_Y - spanY/2 + stag - STAG_Y/2;
                translate([x, y, 0]) hsw_hole(d);
            }
        }
    }
}

hsw_grid(cols, rows);
```

#### HSW Hex-Key Holder

A slim block with a row of slots that hold Allen / hex keys (or small flat tools) upright and sorted by size. Swappable wall plugs.

```openscad
// HSW Hex-Key Holder
//
// Source: honeycomb-wall hex key holder
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across
connectors_x = 2;             // [1:8]
// Hex connector rows
connectors_y = 1;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Holder] */
// Number of slots
slots = 8;                    // [1:16]
// Slot width (mm)
slot_w = 3;                   // [1.5:0.5:8]
// Slot length across the block (mm)
slot_len = 14;               // [6:30]
// Spacing between slots (mm)
spacing = 8;                 // [5:20]
// Slot depth down into the block (mm)
slot_depth = 16;             // [6:40]
// Solid base under the slots (mm)
base_t = 5;                  // [2:14]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module hex_key_holder_body() {
    span = (slots - 1) * spacing;
    bw = span + slot_w + 10;          // X width
    bd = slot_len + 8;                // Z depth out from wall
    bh = slot_depth + base_t;         // Y height
    z0 = PLATE - HSW_WELD;
    y0 = -bh / 2;
    difference() {
        translate([-bw / 2, y0, z0]) cube([bw, bh, bd + HSW_WELD]);
        // row of slots cut from the top (each a thin rectangular pocket)
        for (i = [0 : slots - 1])
            translate([-span / 2 + i * spacing - slot_w / 2, y0 + bh - slot_depth, PLATE + (bd - slot_len) / 2])
                cube([slot_w, slot_depth + eps, slot_len]);
    }
}
module hex_key_holder() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    hex_key_holder_body();
}
hsw_display() hex_key_holder();
```

#### HSW Insert (rounded, configurable)

A smoothly-rounded honeycomb-wall insert with optional center hole, side latch tabs, front hexagon emboss and a flat-side variant.

```openscad
// Generates insert-empty, insert-full and a flat-sided variation of both, with or without the front hexagon emboss.
// Made to make it easy to include in other OpenSCAD models.

verticalSpacing = 23.6;
horizontalSpacing = 40.88;



module hws_insert(centerHole=false, lateralTabs=true, frontDecoration=false,flatSide=true) {
    //Parameters to change in case you deviated from the standard Honeycomb Wall
    hexHeight = 19.8;
    insideHexHeight = 13.40;
    insertDepth = 7.67;
    //Parameters you should not change unless you know what you're doing.
    frontDepth = 2; 
    frontExtension = 1.5;
    gapVert = 1;
    gapHor = 0.8;
    gapLen = 8;
    gapVertDepth = 3.5;
    gapHorDepth = 1.6;
    bumpHeight = 0.5;
    bumpWidth = 2;
    bumpLen = gapVertDepth - gapHor - bumpHeight*2;
    bumpAng = 20;
    //insert assembly using other modules
    difference() {
        union() {
            insert(hexHeight,insertDepth,frontDepth,frontExtension);
            if(lateralTabs) tabBumps(bumpHeight=bumpHeight,hexHeight=hexHeight,insertDepth=insertDepth,frontDepth=frontDepth,bumpLen=bumpLen,bumpAng=bumpAng);
        }
        if (lateralTabs) tabsCutout(hexHeight=hexHeight,gapHorDepth=gapHorDepth,insertDepth=insertDepth,frontDepth=frontDepth,gapLen=gapLen,gapVert=gapVert,gapVertDepth=gapVertDepth,gapHor=gapHor, flatSide=flatSide);
        if (centerHole) insertHole(insideHexHeight=insideHexHeight,insertDepth=insertDepth,frontDepth=frontDepth);
        if (frontDecoration) hexCutout(innerHexHeight=insideHexHeight+1.55);
        if (flatSide) sideFlattener(hexHeight=hexHeight, fullInsertDepth=insertDepth+frontDepth);
    }
}


module hws_insert_n_vert(centerHole=false, lateralTabs=true, frontDecoration=false,flatSide=false,n=2,blanks=1) {
    ycopies(verticalSpacing*(1+blanks),n=n) hws_insert(centerHole=centerHole, lateralTabs=lateralTabs, frontDecoration=frontDecoration,flatSide=flatSide);
}

module hws_insert_n_hor(centerHole=false, lateralTabs=true, frontDecoration=false,flatSide=false,n=2,blanks=0) {
    xcopies(horizontalSpacing*(1+blanks),n=n) hws_insert(centerHole=centerHole, lateralTabs=lateralTabs, frontDecoration=frontDecoration,flatSide=flatSide);
}

module hws_insert_rail(length=verticalSpacing+20,width=19.7,n=1,depth=2){
    hws_insert_n_hor(centerHole=false, lateralTabs=true, frontDecoration=false,flatSide=false,n=n,blanks=0);
    prismoid(size1=[length,width+3], size2=[length,width], h=depth, anchor=TOP);
}
//Auxiliary modules. Coded separately for readability.
module insert(hexHeight,insertDepth,frontDepth,frontExtension) {
    union() {
        //insert
        rounded_prism(hexagon(d=2/sqrt(3)*hexHeight), h=insertDepth+frontDepth, anchor=BOTTOM, joint_top=0.25, joint_sides=.2, splinesteps=1);
        //insert front edge
        rounded_prism(hexagon(d=2/sqrt(3)*(hexHeight+frontExtension)), h=frontDepth, anchor=BOTTOM, joint_bot=.3, joint_sides=.1, splinesteps=1);
    }
}

module insertHole(insideHexHeight,insertDepth,frontDepth) {
    down(0.05)rounded_prism(hexagon(d=2/sqrt(3)*(insideHexHeight)), h=insertDepth+frontDepth+0.1, anchor=BOTTOM, joint_top=-0.25, joint_bot=-0.25, splinesteps=1);
}

module tabsCutout(hexHeight,gapHorDepth,insertDepth,frontDepth,gapLen=8,gapVert,gapVertDepth,gapHor,flatSide=false) {
    difference() {
        rot_copies(n=6, v=UP, delta=[0,hexHeight/2-gapHorDepth,0]) union() {
            up(insertDepth+frontDepth+.1) cuboid([gapLen, gapVert, gapVertDepth+.1], anchor=TOP-BACK);
            up(insertDepth+frontDepth-gapVertDepth+gapHor) cuboid([gapLen, gapHorDepth+.1, gapHor], anchor=TOP-BACK);
        }
        if(flatSide) back(hexHeight/2-gapHorDepth-.1) up(insertDepth+frontDepth+.2) cuboid([gapLen+1,gapVertDepth,insertDepth], anchor=TOP-BACK);
    }
}

module tabBumps(bumpHeight,hexHeight,insertDepth,frontDepth,bumpLen,bumpAng) {
    leftshift=bumpHeight;
    rot_copies(n= 6, v=UP, delta=[0,hexHeight/2,0]) up(insertDepth+frontDepth-bumpLen) rotate([90,0,90]) right_half() left(leftshift) rounded_prism(teardrop2d(r=bumpHeight*2, ang=bumpAng, cap_h=bumpLen+sqrt((2*bumpHeight)^2-leftshift^2), $fn=36), h=bumpLen);
}
module hexCutout(innerHexHeight,cutDepth=0.4,lineWidth=1) {
    tube(h=cutDepth, od=2/sqrt(3)*(innerHexHeight+lineWidth),id=2/sqrt(3)*(innerHexHeight), anchor=BOTTOM, $fn=6);
}
module sideFlattener(hexHeight, fullInsertDepth) {
    down(0.2) back(hexHeight/2) cuboid([hexHeight*2, hexHeight, fullInsertDepth*1.5], anchor=BOTTOM-BACK);
}




// Component testing lines. Uncomment to view individual element with default size

//insert(hexHeight=20,insertDepth=7.67,frontDepth=2,frontExtension=1);
//tabBumps(bumpHeight=0.5,hexHeight=20,insertDepth=7.67,frontDepth=2,bumpLen=1.7,bumpAng=20);
//tabsCutout(hexHeight=20,gapHorDepth=1.6,insertDepth=7.67,frontDepth=2,gapLen=8,gapVert=1,gapVertDepth=3.5,gapHor=0.8,flatSide=true);
//color("red") insertHole();
//hexCutout(innerHexHeight=13.40+1.55);
//sideFlattener(hexHeight = 20, fullInsertDepth=7.67+2);

// Final module testing lines. Uncomment to view how different options generate different versions of the insert.
//hws_insert();
//hws_insert(flatSide=false);
//hws_insert(flatSide=false, centerHole=true);
//hws_insert(flatSide=false, centerHole=true, frontDecoration=true);

//hws_insert_n_vert(blanks=1);
//hws_insert_n_hor();
//hws_insert_rail();

// ---- render a decorated, center-holed insert ----
hws_insert(centerHole=true, frontDecoration=true, flatSide=false);
```

#### HSW J-Hook

A strong J-hook for hanging cables, bags, headphones, or tools — the arm reaches out and curves up into a cradle so things stay put. Swappable wall plugs.

```openscad
// HSW J-Hook
//
// Source: honeycomb-wall J-hook
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across
connectors_x = 1;             // [1:8]
// Hex connector rows (2 = strong cantilever)
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Hook] */
// Hook rod radius (mm)
rod_r = 5;                    // [2.5:0.5:10]
// How far the hook reaches out (mm)
reach = 34;                   // [15:80]
// Upturned tip height (mm)
rise = 18;                    // [6:40]
// Cradle droop (mm)
droop = 7;                    // [0:20]
// Height up the wall (mm)
hook_y = 0;                   // [-25:30]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
PLATE = PLATE_T_DEFAULT;

module hook_path() {
    // J path in the X=0 plane: out along +Z, droop, then curve up +Y into a cradle.
    pts = [
        [0, hook_y,             PLATE - HSW_WELD],   // embedded in the plate (welds)
        [0, hook_y,             PLATE + 3],
        [0, hook_y - droop * 0.5, PLATE + reach * 0.45],
        [0, hook_y - droop,     PLATE + reach * 0.80],
        [0, hook_y - droop * 0.4, PLATE + reach],
        [0, hook_y + rise * 0.5, PLATE + reach - 1],
        [0, hook_y + rise,      PLATE + reach - 5]    // upturned tip
    ];
    for (i = [0 : len(pts) - 2])
        hull() {
            translate(pts[i])     sphere(r = rod_r, $fn = 28);
            translate(pts[i + 1]) sphere(r = rod_r, $fn = 28);
        }
}
module j_hook() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    hook_path();
}
hsw_display() j_hook();
```

#### HSW friction mount (plug + back-plate)

A back-plate carrying a lattice of friction hex plugs - the simple, render-safe foundation that accessories build on.

```openscad
// HSW simple friction mount — the shared, render-safe foundation accessories
// build on. A back-plate (front, +Z) carries plug(s) on the -Z side that press
// into the 20 mm hex holes. Millimetre-native. Self-contained.
$fn = 48;

HSW_PITCH_X = 20.4382;   // column pitch (flat-top honeycomb)
HSW_PITCH_Y = 23.6;      // row pitch
HSW_STAG_Y  = 11.8;      // alternate-column stagger

// one hex plug: presses into a single 20 mm hole, 'fit' = friction clearance.
module hsw_plug(fit = 0.35, len = 7) {
    af = 20 - 2 * fit;                 // flat-to-flat, slightly under 20
    translate([0, 0, -len])
        cylinder(h = len, d = af / cos(30), $fn = 6);   // hex prism into -Z
}

// a back-plate (front face at +Z) carrying an nx×ny plug lattice on -Z.
module hsw_mount(nx = 1, ny = 1, plate_t = 3, fit = 0.35) {
    spanX = (nx - 1) * HSW_PITCH_X;
    spanY = (ny - 1) * HSW_PITCH_Y;
    w = spanX + 24;
    d = spanY + 24;
    translate([0, 0, plate_t / 2]) cube([w, d, plate_t], center = true);   // plate
    for (c = [0 : nx - 1]) {
        stag = (c % 2 == 1) ? HSW_STAG_Y : 0;
        for (r = [0 : ny - 1])
            translate([c * HSW_PITCH_X - spanX / 2,
                       r * HSW_PITCH_Y - spanY / 2 + stag - HSW_STAG_Y / 2, 0])
                hsw_plug(fit);
    }
}

// demo: a 2×2 mount (the accessory body would union onto the +Z plate face)
hsw_mount(2, 2);
```

#### HSW Open Parts Bin

An open-top parts bin with a lowered front wall for easy scooping, on swappable wall plugs. Good for screws, clips, and small components.

```openscad
// HSW Open Parts Bin
//
// Source: honeycomb-wall open parts bin
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across (wider = more)
connectors_x = 2;             // [1:8]
// Hex connector rows (more = heavier loads)
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Bin] */
// Inside width (mm)
bin_width = 80;               // [30:200]
// Inside depth out from the wall (mm)
bin_depth = 45;               // [20:120]
// Back wall height (mm)
bin_height = 50;              // [20:120]
// Front wall height — lower for scooping (mm)
front_height = 28;            // [8:120]
// Wall thickness (mm)
wall = 2.4;                   // [1.2:0.2:4]
// Floor thickness (mm)
floor_t = 2.4;                // [1.2:0.2:5]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module open_bin() {
    ow = bin_width + 2 * wall;          // outer width  (X)
    od = bin_depth + 2 * wall;          // outer depth  (Z, out from wall)
    z0 = PLATE - HSW_WELD;              // weld the back wall into the plate
    y0 = -bin_height / 2;               // centre the bin vertically so the back wall covers the plate
    difference() {
        // outer block rising up the wall (+Y), open at the top
        translate([-ow / 2, y0, z0])
            cube([ow, bin_height, od + HSW_WELD]);
        // inner cavity (leaves walls + floor, open top)
        translate([-bin_width / 2, y0 + floor_t, PLATE + wall])
            cube([bin_width, bin_height, bin_depth + eps]);
        // lower the front wall (the far +Z face) for scooping access
        translate([-bin_width / 2, y0 + front_height, PLATE + bin_depth + wall - eps])
            cube([bin_width, bin_height, wall + 2 * eps]);
    }
}
module open_parts_bin() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    open_bin();
}
hsw_display() open_parts_bin();
```

#### HSW Pen / Pencil Cup

A round, slightly back-tilted pen and pencil cup with an optional drain hole. Pick how it clips to the wall: friction peg, bare peg (into a latch), latch insert, or spring clip.

```openscad
// HSW Pen / Pencil Cup
//
// Source: honeycomb-wall pen cup
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across (wider/heavier = more)
connectors_x = 1;             // [1:8]
// Hex connector rows (2 = cantilever)
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Cup] */
// Inner bore diameter
cup_inner_d = 40;         // [20:80]
// Side wall thickness
cup_wall = 2.4;           // [1.2:0.2:4]
// Tube height (base to rim)
cup_height = 70;          // [30:120]
// Closed-base thickness
cup_base_t = 2.4;         // [1.2:0.2:5]
// Lean toward the wall (degrees)
back_tilt = 6;            // [0:15]
// Drain hole through the base
drain = true;
// Drain hole diameter
drain_d = 6;              // [3:12]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
web_embed   = 2.0;
gap_fix     = 0.02;
cup_outer_d = cup_inner_d + 2 * cup_wall;
cup_outer_r = cup_outer_d / 2;
PLATE       = PLATE_T_DEFAULT;

module cup_body() {
    difference() {
        rotate([-90, 0, 0]) cylinder(h = cup_height, d = cup_outer_d);
        translate([0, cup_base_t, 0]) rotate([-90, 0, 0])
            cylinder(h = cup_height - cup_base_t + gap_fix, d = cup_inner_d);
        if (drain)
            translate([0, -gap_fix, 0]) rotate([-90, 0, 0])
                cylinder(h = cup_base_t + 2 * gap_fix, d = drain_d);
    }
}
module connect_web() {
    web_w = cup_outer_d - 1;
    web_h = cup_height * 0.55;
    z0 = PLATE - web_embed;
    z1 = PLATE + cup_outer_r;
    translate([-web_w / 2, 0, z0]) cube([web_w, web_h, z1 - z0]);
}
module pen_cup() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    translate([0, 0, PLATE]) rotate([back_tilt, 0, 0]) translate([0, 0, -PLATE]) {
        translate([0, 0, PLATE + cup_outer_r - 0.5]) cup_body();
        connect_web();
    }
}
hsw_display() pen_cup();
```

#### HSW Phone / Tablet Shelf

A small ledge that holds a phone or tablet leaning against the wall, with a front lip to stop it sliding and a cable slot through the deck. Swappable wall plugs.

```openscad
// HSW Phone / Tablet Shelf
//
// Source: honeycomb-wall phone shelf
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across
connectors_x = 2;             // [1:8]
// Hex connector rows
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Shelf] */
// Ledge width (mm)
ledge_width = 85;             // [40:160]
// Ledge depth out from the wall (mm)
ledge_depth = 24;            // [15:60]
// Ledge thickness (mm)
ledge_t = 5;                 // [3:12]
// Front lip height (mm)
lip_h = 14;                  // [6:30]
// Lip thickness (mm)
lip_t = 4;                   // [2:8]
// Cable slot width (mm)
cable_w = 18;                // [0:50]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module phone_shelf_body() {
    z0 = PLATE - HSW_WELD;
    y0 = -(ledge_t) / 2 - 8;      // ledge near the lower part of the plate
    difference() {
        union() {
            // ledge deck (top surface at y0 + ledge_t)
            translate([-ledge_width / 2, y0, z0]) cube([ledge_width, ledge_t, ledge_depth + HSW_WELD]);
            // front lip rising up at the far edge
            translate([-ledge_width / 2, y0, PLATE + ledge_depth - lip_t]) cube([ledge_width, ledge_t + lip_h, lip_t]);
        }
        // cable pass-through slot through the deck near the wall
        if (cable_w > 0)
            translate([-cable_w / 2, y0 - eps, PLATE + 3]) cube([cable_w, ledge_t + 2 * eps, 7]);
    }
}
module phone_shelf() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    phone_shelf_body();
}
hsw_display() phone_shelf();
```

#### HSW Plier / Cutter Rack

A row of wide slots that hold pliers, cutters, and snips by the handle, tips hanging down. Choose how many. Swappable wall plugs.

```openscad
// HSW Plier / Cutter Rack
//
// Source: honeycomb-wall plier rack
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across (a wide rack wants 3+)
connectors_x = 3;             // [1:8]
// Hex connector rows
connectors_y = 1;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Rack] */
// Number of tool slots
slots = 4;                    // [1:8]
// Slot width — fits the plier head (mm)
slot_w = 14;                  // [6:30]
// Slot length across the shelf (mm)
slot_len = 24;               // [10:50]
// Spacing between slots (mm)
spacing = 30;                // [16:60]
// Shelf thickness (mm)
shelf_t = 10;                // [5:20]
// Shelf height up the wall (mm)
shelf_y = 0;                 // [-25:30]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module plier_rack() {
    span = (slots - 1) * spacing;
    w  = span + slot_len + 12;
    bd = slot_len + 12;
    difference() {
        translate([-w / 2, shelf_y - shelf_t / 2, PLATE - HSW_WELD])
            cube([w, shelf_t, bd + HSW_WELD]);
        // wide vertical slots (through Y) the plier head drops through, handle rests on top
        for (i = [0 : slots - 1])
            translate([-span / 2 + i * spacing - slot_w / 2, shelf_y - shelf_t / 2 - eps, PLATE + (bd - slot_len) / 2])
                cube([slot_w, shelf_t + 2 * eps, slot_len]);
    }
}
module plier_rack_part() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    plier_rack();
}
hsw_display() plier_rack_part();
```

#### HSW Rod / Pillar Holder

A deep round socket that holds a rod, dowel, pillar, umbrella, or long tool upright. Optional drain hole in the base. Swappable wall plugs.

```openscad
// HSW Rod / Pillar Holder
//
// Source: honeycomb-wall vertical rod holder
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across
connectors_x = 1;             // [1:8]
// Hex connector rows (2 = strong for a tall rod)
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Socket] */
// Bore diameter (mm)
bore_d = 24;                  // [8:60]
// Wall thickness (mm)
wall = 3;                     // [1.5:0.5:6]
// Socket depth (mm)
depth = 65;                   // [25:140]
// Base thickness (mm)
base_t = 4;                   // [2:12]
// Drain hole through the base
drain = true;
// Drain hole diameter (mm)
drain_d = 6;                  // [3:16]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module rod_socket() {
    od = bore_d + 2 * wall;
    or = od / 2;
    // vertical tube (opens up +Y), lifted so it rests just off the plate
    translate([0, 0, PLATE + or]) {
        difference() {
            rotate([-90, 0, 0]) cylinder(h = depth, d = od);
            translate([0, base_t, 0]) rotate([-90, 0, 0]) cylinder(h = depth - base_t + eps, d = bore_d);
            if (drain) translate([0, -eps, 0]) rotate([-90, 0, 0]) cylinder(h = base_t + 2 * eps, d = drain_d);
        }
    }
    // web welding the socket back to the plate
    web_w = od - 2;
    web_h = depth * 0.6;
    translate([-web_w / 2, 0, PLATE - HSW_WELD]) cube([web_w, web_h, or + HSW_WELD]);
}
module rod_holder() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    rod_socket();
}
hsw_display() rod_holder();
```

#### HSW Scalpel / Craft-Knife Holder

Holds a round knife handle with internal retention lips and a side slot for spare blades; clips on with a hex plug.

```openscad
THICKNESS = 1.0;

outer_diameter = 9.0;
inner_diameter = 7.86;
holder_height = 50;

cutout_width = 1.5;
blades_holder_width = 3.0;
cutout_height = 30;
blade_width = 8.1;

lip_width = 1.5;

chamfer = 1;

$fn=200;

/* [Hidden] */
PLUG_HEXAGON_DIAMETER = 15.3;
PLUG_LENGTH = 10;

module hexagon_plug(){
    cylinder(d=PLUG_HEXAGON_DIAMETER, h=PLUG_LENGTH, $fn=6, center=true);
}

module housing() {
    xw = max(outer_diameter + THICKNESS * 2, PLUG_HEXAGON_DIAMETER + THICKNESS * 2) + chamfer*2;
    yw = outer_diameter + THICKNESS * 2 + chamfer*2;
    zw = holder_height + THICKNESS * 2 + chamfer*2;
    difference() {
        cuboid([xw, yw, zw], chamfer=chamfer);
        translate([0, 0, THICKNESS]) tube(ir = 0, or=outer_diameter / 2, h=holder_height + THICKNESS*2 * chamfer*2, center=true);
    }
    translate([0, yw/2 + PLUG_LENGTH/2, zw/2 - PLUG_LENGTH/2 - chamfer*2 - THICKNESS]) rotate([90, 0, 0]) hexagon_plug();

    translate([0, -yw/2 - blades_holder_width / 2 - THICKNESS + chamfer, -zw / 2 + cutout_height / 2]) {
        difference() {
            cuboid([xw, blades_holder_width + THICKNESS * 2 + chamfer * 2, cutout_height], chamfer=chamfer);
            translate([0, 0, THICKNESS]) cuboid([blade_width, blades_holder_width, cutout_height]);
        }
    }
}


module lip() {
    width = outer_diameter - inner_diameter;
    translate([outer_diameter / 2 + width / 2, 0, (cutout_height) / 2 - 2]) {
        scale([width, lip_width, 1])
        tube(ir=0, or=1, holder_height - cutout_height);
    }
}

module lips() {
    intersection() {
        union() {
            rotate([0, 0, 30]) lip();
            rotate([0, 0, -30]) lip();

            rotate([0, 0, 210]) lip();
            rotate([0, 0, -210]) lip();
        }
        tube(ir = 0, or=inner_diameter / 2 + THICKNESS, holder_height + THICKNESS);
    }
}

module cutout() {
    intersection() {

        translate([0, 0, -holder_height / 2 + cutout_height / 2]) tube(ir = 0, or=outer_diameter / 2 + THICKNESS, cutout_height);

        union() {
            translate([outer_diameter / 2 + cutout_width / 2, 0, 0]) cuboid([outer_diameter, outer_diameter*2, holder_height + THICKNESS]);
            translate([-outer_diameter / 2 - cutout_width / 2, 0, 0]) cuboid([outer_diameter, outer_diameter*2, holder_height + THICKNESS]);
        }
    }
}

difference() {
    union() {
        cutout();
        lips();
        housing();
    }
}
```

#### HSW Screwdriver Rack

A wall shelf with a row of graduated holes that hold screwdrivers (or any shaft tool) shaft-down with the handle resting on top. Pick how it clips to the wall: friction peg, bare peg (into a latch), latch insert, or spring clip; grid or honeycomb support.

```openscad
// HSW Screwdriver Rack
//
// Source: honeycomb-wall screwdriver rack
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across (a wide rack wants 3+)
connectors_x = 3;             // [1:8]
// Hex connector rows (1 = single line)
connectors_y = 1;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Rack] */
// Number of tool holes
holes = 5;                    // [1:12]
// Smallest hole diameter (mm)
hole_min_d = 5;               // [3:20]
// Largest hole diameter (mm)
hole_max_d = 11;              // [3:30]
// Spacing between hole centres (mm)
hole_spacing = 22;            // [12:40]
// How far the shelf projects from the wall (mm)
shelf_depth = 28;             // [15:60]
// Shelf thickness (mm)
shelf_t = 7;                  // [4:14]
// Shelf height up the wall (mm)
shelf_y = 0;                  // [-25:30]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module rack_shelf() {
    span = (holes - 1) * hole_spacing;
    w    = span + hole_max_d + 8;
    difference() {
        // flat shelf: wide in X, deep in +Z, thin in Y, welded back into the plate
        translate([-w / 2, shelf_y - shelf_t / 2, PLATE - HSW_WELD])
            cube([w, shelf_t, shelf_depth + HSW_WELD]);
        // graduated vertical bores (along Y), front-biased so a handle rests on top
        for (i = [0 : holes - 1]) {
            d = hole_min_d + (holes > 1 ? (hole_max_d - hole_min_d) * i / (holes - 1) : 0);
            translate([-span / 2 + i * hole_spacing, shelf_y + shelf_t / 2 + eps, PLATE + shelf_depth * 0.55])
                rotate([90, 0, 0])
                    cylinder(h = shelf_t + 2 * eps, d = d);
        }
    }
}
module screwdriver_rack() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    rack_shelf();
}
hsw_display() screwdriver_rack();
```

#### HSW Shelf

A flat shelf with a raised front lip to stop things sliding off, plus an angled support gusset underneath. Swappable wall plugs.

```openscad
// HSW Shelf
//
// Source: honeycomb-wall shelf
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across (a wide shelf wants 3+)
connectors_x = 3;             // [1:8]
// Hex connector rows
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Shelf] */
// Shelf width (mm)
shelf_width = 120;            // [40:250]
// Shelf depth out from the wall (mm)
shelf_depth = 45;             // [20:100]
// Shelf thickness (mm)
shelf_t = 4;                  // [2:10]
// Front lip height (mm)
lip_h = 9;                    // [0:25]
// Front lip thickness (mm)
lip_t = 3;                    // [2:6]
// Support gusset depth (mm)
gusset = 28;                  // [0:80]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
PLATE = PLATE_T_DEFAULT;

module shelf_body() {
    z0 = PLATE - HSW_WELD;
    // flat deck: wide X, thin Y, deep +Z. Top surface at Y=0.
    translate([-shelf_width / 2, -shelf_t, z0])
        cube([shelf_width, shelf_t, shelf_depth + HSW_WELD]);
    // raised front lip at the far +Z edge
    if (lip_h > 0)
        translate([-shelf_width / 2, -shelf_t, PLATE + shelf_depth - lip_t])
            cube([shelf_width, shelf_t + lip_h, lip_t]);
    // angled support gusset under the deck (triangular prism spanning the width).
    // +0.5 in Y overlaps it into the deck so the union is one clean shell.
    if (gusset > 0)
        translate([shelf_width / 2, -shelf_t + 0.5, z0])
            rotate([0, -90, 0])
                linear_extrude(height = shelf_width)
                    polygon([[0, 0], [0, -gusset], [shelf_depth + HSW_WELD, 0]]);
}
module shelf() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    shelf_body();
}
hsw_display() shelf();
```

#### HSW Test-Tube / Marker Rack

A row of equal round bores to hold test tubes, markers, pens, or small bottles upright. Swappable wall plugs.

```openscad
// HSW Test-Tube / Marker Rack
//
// Source: honeycomb-wall test-tube rack
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across (wider = more)
connectors_x = 3;             // [1:8]
// Hex connector rows
connectors_y = 1;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Rack] */
// Number of bores
bores = 5;                    // [1:12]
// Bore diameter (mm)
bore_d = 18;                  // [6:40]
// Spacing between bores (mm)
spacing = 26;                 // [12:50]
// Bore depth (mm)
bore_depth = 30;             // [10:80]
// Solid base under the bores (mm)
base_t = 4;                  // [2:12]
// Wall thickness around bores (mm)
wall = 3;                    // [1.5:0.5:6]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module tube_rack() {
    span = (bores - 1) * spacing;
    bw = span + bore_d + 2 * wall;
    bd = bore_d + 2 * wall;           // Z depth out from wall
    bh = bore_depth + base_t;         // Y height
    z0 = PLATE - HSW_WELD;
    y0 = -bh / 2;
    difference() {
        translate([-bw / 2, y0, z0]) cube([bw, bh, bd + HSW_WELD]);
        for (i = [0 : bores - 1])
            translate([-span / 2 + i * spacing, y0 + bh + eps, PLATE + bd / 2])
                rotate([90, 0, 0]) cylinder(h = bore_depth, d = bore_d);   // vertical bore, open top
    }
}
module test_tube_rack() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    tube_rack();
}
hsw_display() test_tube_rack();
```

#### HSW Wide Tool Cup

A wide, flat-based cup for pliers, brushes, markers, or shop tools, held out from the wall on swappable plugs. Optional drain hole in the base.

```openscad
// HSW Wide Tool Cup
//
// Source: honeycomb-wall wide tool cup
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across
connectors_x = 2;             // [1:8]
// Hex connector rows (2 = better for a heavy cup)
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Cup] */
// Inner bore diameter (mm)
cup_inner_d = 60;             // [25:120]
// Side wall thickness (mm)
cup_wall = 2.6;               // [1.2:0.2:4]
// Cup height (mm)
cup_height = 55;              // [25:120]
// Base thickness (mm)
cup_base_t = 2.6;             // [1.2:0.2:5]
// Drain hole through the base
drain = true;
// Drain hole diameter (mm)
drain_d = 8;                  // [3:20]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module tool_cup_body() {
    cup_outer_d = cup_inner_d + 2 * cup_wall;
    cup_outer_r = cup_outer_d / 2;
    // the cup is a vertical tube (opens up +Y), lifted so it rests just off the plate
    translate([0, 0, PLATE + cup_outer_r]) {
        difference() {
            rotate([-90, 0, 0]) cylinder(h = cup_height, d = cup_outer_d);
            translate([0, cup_base_t, 0]) rotate([-90, 0, 0])
                cylinder(h = cup_height - cup_base_t + eps, d = cup_inner_d);
            if (drain)
                translate([0, -eps, 0]) rotate([-90, 0, 0])
                    cylinder(h = cup_base_t + 2 * eps, d = drain_d);
        }
    }
    // web welding the cup back to the plate face (one fused shell)
    web_w = cup_outer_d - 2;
    web_h = cup_height * 0.55;
    translate([-web_w / 2, 0, PLATE - HSW_WELD])
        cube([web_w, web_h, cup_outer_r + HSW_WELD]);
}
module tool_cup() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    tool_cup_body();
}
hsw_display() tool_cup();
```

#### HSW Tube Holder (diamond lattice)

A row of round tube holders with decorative diamond-lattice walls and a bottom plate, on a hex-connector strip. Set square_size = 0 for solid (faster) walls.

```openscad
// number of tubes
tube_count = 4;
inner_diameter = 50;
// thickness of tube wall
wall_thickness = 2;
// total tube lenght in mm
height = 80;

// hole size in mm, set to 0 for solid walls
square_size = 8;
// grid thickness in mm
grid_thickness = 3;

// bottom plate
bottom_plate = true;

// additional connector for stronger connection
wider_connection = false;

// taller back wall
taller_connection = false;

inner_radius = inner_diameter/2;
outer_radius = inner_radius + wall_thickness;


//-----------------
/* [HSW connector] */ 

// inner distance between corners
connector_size = 15.47;
connector_depth = 13;
tolerance = 0.1;
hexagon_distance = 40.88;

$fn = 100;

/* functions*/
module diamond_tube() {
	square_diagonal = sqrt(square_size * square_size * 2);
	spacing = square_diagonal + grid_thickness;
	outer_circumference = outer_radius * 2 * PI;
	steps = round(outer_circumference / spacing);
	angle_step = 360 / steps;

	difference() {
		cylinder(h = height, r = outer_radius);

		cylinder(h = height + 1, r = inner_radius);

		// Square pattern
		union() {
			for (pattern = [0 : 1]) {
				for (z = [0 : square_diagonal + grid_thickness : height]) {
					for (angle = [angle_step/2 * pattern : angle_step : 360]) {
						rotate([0, 0, angle])
							translate([inner_radius + (outer_radius - inner_radius) / 2, 0, z + spacing /2 * pattern])
							rotate([0, 90])
							rotate([0, 0, 45])
							linear_extrude(height=wall_thickness*3, center=true)
							square([square_size, square_size], center = true);
					}
				}
			}
		}
	}

	difference() {
		union() {
			translate([0, 0, height - grid_thickness])
				cylinder(h = grid_thickness, r = outer_radius);

			cylinder(h = grid_thickness, r = outer_radius);
		}

		translate([0,0, -1])
			cylinder(h = height + 2, r = inner_radius);
	}

	if (bottom_plate) {
		cylinder(h = wall_thickness, r = outer_radius);
	}
}

function hexagon(radius) = [
    for (i = [0:5])
        [radius * cos(i * 60), radius * sin(i * 60)]
];

module hsw_connector() {
    rotate([90, 0, 0])
    linear_extrude(height = connector_depth)
    offset(r = -tolerance)
    polygon(points = hexagon(connector_size / 2));
}

module connectors() {
    tubes_width = tube_count * (inner_diameter + wall_thickness) + wall_thickness;

    count = max(1, round((wider_connection ? 1 : 0) + tubes_width / (hexagon_distance + connector_size / 2)));
    strip_width = (count - 0.5) * hexagon_distance;

    real_width = strip_width;
    
    dist = (real_width - (count-1) * hexagon_distance) / 2;
    h = (connector_size/2 - tolerance)*sqrt(3)/2;    
    diff = (tubes_width - strip_width) / 2;
    
    translate([-outer_radius + diff, 0, 0])
    union() {
        translate([dist, connector_depth, h])
        for (i = [1 : count]) {
            translate([(i-1) * hexagon_distance, 0, 0])
            hsw_connector();    
        }

        cube([real_width, wall_thickness, connector_size * (taller_connection ? 2 : 1)], center = false);
    }
}

/* MAIN */
connectors();
for (i = [1 : tube_count]) {
//    translate([(i-1) * (outer_radius * 2), wall_thickness-outer_radius, 0])
    translate([(i-1) * (inner_radius * 2 + wall_thickness), wall_thickness-outer_radius, 0])
    diamond_tube();
}
```

#### HSW U-Saddle Holder

A saddle block with a rounded groove that cradles round handles, cans, spray bottles, or power-tool bodies. Swappable wall plugs.

```openscad
// HSW U-Saddle Holder
//
// Source: honeycomb-wall U saddle holder
// Generated from the library above, used under its license.

/* [Wall mount] */
// How it attaches to the wall
connector_type = "peg";       // [peg:Friction peg (into wall), barepeg:Bare peg (into a latch), latch:Latch insert (into wall), clip:Spring clip (into wall)]
// Plug support style
support_style = "grid";       // [grid:Grid (aligned rows + columns), honeycomb:Honeycomb (staggered, denser)]
// Rotate the plug pattern (to match a wall mounted sideways)
connector_rotation = 0;       // [0:0 degrees, 90:90 degrees]
// Hex connectors across
connectors_x = 2;             // [1:8]
// Hex connector rows (2 = strong cantilever)
connectors_y = 2;             // [1:4]
// Fit tweak: + looser / - tighter
clearance = 0;                // [-0.3:0.05:0.5]

/* [Saddle] */
// Cradle diameter — fits the item (mm)
cradle_d = 32;                // [12:90]
// How far the saddle reaches out (mm)
reach = 38;                   // [18:90]
// Wall thickness around the groove (mm)
wall = 4;                     // [2:0.5:8]
// Front drop — how far the front lip rises to retain the item (mm)
front_lip = 10;              // [0:30]

// ============================================================================
//  HSW CONNECTOR TEMPLATE — the single canonical, self-contained connector kit
//  every HSW example inlines VERBATIM so plug spacing + sizing stay identical.
//  OpenSCAD 2021.01-safe. Self-contained: no include/use, no external libraries.
//
//  THE ONE RULE: plugs (hsw_mount) and wall holes (hsw_grid) are placed by the
//  SAME lattice functions hsw_cx()/hsw_cy(), so plug(c,r) == hole(c,r) for ANY
//  count by construction — verified 0.0000 mm deviation for 1..5 in each axis.
//
//  Frame: +Z = wall-normal (out of the wall). Connectors point into -Z (into the
//  wall); the back-plate front face sits at +Z = plate_t; accessory bodies build
//  on the +Z face. UP the wall = +Y, X = horizontal.
//
//  CUSTOMIZER: everything below is in the Hidden group so a product recipe that
//  inlines this template exposes ONLY its own dimensions + connectors_x/y +
//  connector_type + one clearance tweak. The HSW internals are NEVER customer-tunable.
// ============================================================================
/* [Hidden] */
$fn = 48;

// ---- Core hex interface (the 20mm hole a connector plugs into) -------------
HSWX_INSIDE       = 20;                              // hex hole flat-to-flat
HSWX_WALL         = 1.8;                             // shared cell wall thickness
HSWX_OUTSIDE      = HSWX_INSIDE + HSWX_WALL*2;       // 23.6  outside flat-to-flat
HSWX_SIDE         = HSWX_OUTSIDE / sqrt(3);          // ~13.6255 outer side length
HSWX_DEPTH        = 8;                               // panel / plug depth (Std Duty)
HSWX_LOCK_DEPTH   = 5;                               // depth where the lock step starts
HSWX_LOCK_LIP     = 1;                               // 1mm internal lip the barb catches
HSWX_LOCK_CHAMFER = HSWX_LOCK_LIP;                   // 1mm @ 45deg lead-in

// ---- Verified honeycomb lattice (flat-topped hexes, column tiling) ---------
HSW_PITCH_X       = 1.5 * HSWX_SIDE;                 // ~20.4382 column-to-column
HSW_PITCH_Y       = HSWX_OUTSIDE;                    // 23.6    row-to-row in a column
HSW_STAGGER_Y     = HSWX_OUTSIDE / 2;               // 11.8    alternate-column offset

// ---- Shared connector tunables ---------------------------------------------
PEG_FIT_DEFAULT   = 0.35;                            // bare-peg friction clearance
PEG_LEN_DEFAULT   = 7;                               // bare-peg depth into wall (< HSWX_DEPTH)
PLATE_T_DEFAULT   = 3;                               // back-plate thickness (welds depend on it)
HSW_WELD          = 0.6;                              // connector overlap INTO the plate (one fused solid)

// Across-flats -> circumscribed diameter (a $fn=6 cylinder lands on those flats).
// af/sqrt(3)*2 is IDENTICAL to af/cos(30); ONE form library-wide.
function hsw_af_to_d(af) = af / sqrt(3) * 2;

// ============================================================================
//  THE LATTICE — the single source of truth for WHERE connectors/holes go.
//  Bounding-box-centred on the origin for ANY nx/ny (incl. the single-column
//  case): the staggered odd columns only exist when nx>1, so the y-centring of
//  the cluster is conditional on nx>1. hsw_mount AND hsw_grid both call these.
// ============================================================================
function hsw_max_stag(nx) = (nx > 1) ? HSW_STAGGER_Y : 0;     // odd col only exists if nx>1
function hsw_cx(c, nx)    = c*HSW_PITCH_X - (nx-1)*HSW_PITCH_X/2;
function hsw_cy(c, r, nx, ny) =
    r*HSW_PITCH_Y
    - (ny-1)*HSW_PITCH_Y/2
    + ((c % 2 == 1) ? HSW_STAGGER_Y : 0)            // real alternate-column stagger
    - hsw_max_stag(nx)/2;                            // centre the cluster (cond. on nx>1)

// straight=true strip: every-OTHER column, no stagger (the legacy 40.88 strip).
// Use 2*HSW_PITCH_X (=40.8764), NEVER the rounded 40.88 literal.
function hsw_cx_straight(c, nx) = c*(2*HSW_PITCH_X) - (nx-1)*(2*HSW_PITCH_X)/2;

// ============================================================================
//  CONNECTOR 1 — hsw_peg : bare friction press-peg (the de-facto canonical peg).
//  $fn=6 hex prism, across-flats = HSWX_INSIDE - 2*fit, into -Z. Press fit only.
// ============================================================================
module hsw_peg(fit = PEG_FIT_DEFAULT, len = PEG_LEN_DEFAULT) {
    af = HSWX_INSIDE - 2*fit;                        // flat-to-flat, slightly under 20
    // hex prism from z=+HSW_WELD (buried INTO the plate) down to z=-len (into wall),
    // so the peg fuses with any plate occupying z>=0 into ONE solid (no coincident-face seam).
    translate([0, 0, -len])
        cylinder(h = len + HSW_WELD, d = hsw_af_to_d(af), $fn = 6);
}

// ============================================================================
//  CONNECTOR 1b — hsw_bare_peg : the THIN CORE peg (hex-plug-library hexagon_plug)
//  that plugs into a SEPARATE latch insert pre-snapped in the wall — NOT the grid
//  directly. Verbatim dims: 14.7 across-corners, 13 deep. Into -Z, welds the plate.
// ============================================================================
BAREPEG_D   = 14.7;     // across-corners ($fn=6) — source PLUG_HEXAGON_DIAMETER
BAREPEG_LEN = 13;       // source PLUG_LENGTH
module hsw_bare_peg(fit = 0) {
    translate([0, 0, -BAREPEG_LEN])
        cylinder(h = BAREPEG_LEN + HSW_WELD, d = BAREPEG_D - 2*fit, $fn = 6);
}

// ============================================================================
//  hsw_mount(nx, ny, type, plate_t, fit, straight)
//  ONE back-plate (front face +Z = plate_t) carrying an nx*ny connector lattice
//  on -Z, placed by hsw_cx/hsw_cy. type dispatches the per-cell connector.
// ============================================================================
// straight=true  -> GRID support  : pegs on an aligned rectangular grid
//                                    (40.8764 horiz x 23.6 vert, every-other
//                                    honeycomb column -> all land in real holes).
// straight=false -> HONEYCOMB support: pegs follow the staggered hex lattice
//                                    (20.4382 column pitch, odd columns staggered).
module hsw_mount(nx = 1, ny = 1, type = "peg", plate_t = PLATE_T_DEFAULT,
                 clearance = 0, straight = false, margin = 3, crot = 0) {
    // clamp counts so a user 0 (or negative) never yields an empty / garbage mount
    gnx = max(1, nx);
    gny = max(1, ny);
    // per-type base fit (peg=press 0.35, clip=snap 0.15, latch/barepeg=0 lip-tuned)
    // + the product's single user clearance tweak (+ looser / - tighter).
    pfit = (type == "clip" ? CLIP_FIT_DEFAULT
          : (type == "latch" || type == "barepeg") ? 0
          : PEG_FIT_DEFAULT) + clearance;
    // plate footprint spans the lattice + margin. GRID has no stagger; only the
    // staggered honeycomb extends Y by the odd-column stagger.
    spanX = straight ? (gnx-1)*(2*HSW_PITCH_X) : (gnx-1)*HSW_PITCH_X;
    spanY = (gny-1)*HSW_PITCH_Y;
    w = spanX + HSWX_OUTSIDE + 2*margin;
    d = spanY + HSWX_OUTSIDE + (straight ? 0 : hsw_max_stag(gnx)) + 2*margin;
    // crot rotates the PLUG PATTERN (plate + connectors, together so plugs stay on
    // the plate) about the wall normal so the accessory can match a wall installed
    // rotated 90deg. The body is built separately by the recipe and is NOT rotated
    // (plug layout only — body fixed).
    rotate([0, 0, crot]) {
        translate([0, 0, plate_t/2]) cube([w, d, plate_t], center = true);   // plate
        for (c = [0 : gnx-1])
            for (r = [0 : gny-1]) {
                x = straight ? hsw_cx_straight(c, gnx) : hsw_cx(c, gnx);
                y = straight ? (r*HSW_PITCH_Y - spanY/2) : hsw_cy(c, r, gnx, gny);
                translate([x, y, 0]) hsw_connector(type, pfit);
            }
    }
}

// ============================================================================
//  CONNECTOR 2 — hsw_latch_insert : snap-tab latching insert (vanilla port of
//  hex-plug-library / hsw-custom-insert, sh1-verified). Built front-face(lip) at
//  z=0 with body in +Z, then flipped to -Z and welded into the plate.
// ============================================================================
INSERT_BODY_FLATS = 19.7;   INSERT_LIP_FLATS = 22.5;   INSERT_TOTAL_H = 10;
INSERT_LIP_H = 2.5;         INSERT_SNAP_H = 6.5;       INSERT_TAB_OUT = 0.5;
INSERT_TAB_LEN = 2;         INSERT_SLOT_LEN = 8;       INSERT_SLOT_W = 0.8;
INSERT_SLOT_SIDE = 1;       INSERT_SLOT_RAD = 8.25;

module _hsw_bevel_cut(diameter) {
    h = diameter/2;                                     // tan(45)==1
    difference() { cylinder(d = diameter*2, h = h); cylinder(d1 = diameter, d2 = 0, h = h); }
}
module _hsw_insert_body(fit) {
    bodyD = hsw_af_to_d(INSERT_BODY_FLATS - 2*fit);
    lipD  = hsw_af_to_d(INSERT_LIP_FLATS);
    difference() {
        union() {
            cylinder(d = bodyD, h = INSERT_TOTAL_H, $fn = 6);   // main plug
            cylinder(d = lipD,  h = INSERT_LIP_H,  $fn = 6);    // overhang lip
        }
        translate([0,0,INSERT_TOTAL_H - 0.35]) _hsw_bevel_cut(bodyD);   // soften top edge
    }
}
module hsw_latch_insert(fit = 0) {
    translate([0,0,HSW_WELD]) rotate([180,0,0])
    union() {
        // six snap tabs (non-degenerate tip r=0.2, tops kept below the bevel)
        for (i=[0:5]) rotate([0,0,i*60]) translate([0, INSERT_BODY_FLATS/2, 0])
            hull() {
                translate([0,0,INSERT_SNAP_H + INSERT_SLOT_SIDE + INSERT_TAB_OUT])
                    rotate([0,90,0]) cylinder(r = INSERT_TAB_OUT, h = INSERT_TAB_LEN, center=true, $fn=20);
                translate([0,0,INSERT_TOTAL_H - 0.6])
                    rotate([0,90,0]) cylinder(r = 0.2, h = INSERT_TAB_LEN, center=true, $fn=20);
            }
        // body with flex slots so each tab deflects on insertion
        difference() {
            _hsw_insert_body(fit);
            for (i=[0:5]) rotate([0,0,i*60]) {
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_SLOT_W, INSERT_TOTAL_H]);
                translate([-INSERT_SLOT_LEN/2, INSERT_SLOT_RAD, INSERT_SNAP_H]) cube([INSERT_SLOT_LEN, INSERT_TOTAL_H, INSERT_SLOT_SIDE]);
            }
        }
    }
}

// ============================================================================
//  CONNECTOR 3 — hsw_clip : spring snap clip (from hsw-lib-core, sh1-verified).
//  Two cantilever arms catch the 1mm lip at 5mm depth. Mouth at z=0 into -Z.
// ============================================================================
CLIP_FIT_DEFAULT = 0.15;
CLIP_AF  = HSWX_INSIDE;      // 20 plug across-flats target
CLIP_LEN = HSWX_DEPTH;       // 8  plug length into the wall
SPRING_WALL = 1.6;           // cantilever arm wall (>=1.4 to flex)
SPRING_SLOT = 2.2;           // central relief slot width
module hsw_clip(fit = CLIP_FIT_DEFAULT) {
    af = CLIP_AF - 2*fit;  L = CLIP_LEN;
    barb = HSWX_LOCK_LIP;  locz = HSWX_LOCK_DEPTH;  cham = HSWX_LOCK_CHAMFER;  halfX = af/2;
    translate([0,0,HSW_WELD])
    difference() {
        union() {
            translate([0,0,-L/2]) linear_extrude(height = L, center = true) hsw_hex2d(af);   // hex plug
            for (sx=[-1,1]) translate([sx*halfX,0,-locz]) rotate([90,0,0])                    // outward barbs
                linear_extrude(height = af*0.62, center = true)
                    polygon([[0,0],[sx*barb,0],[sx*barb,cham],[0,cham+barb]]);
            translate([0,0,0.6/2]) linear_extrude(height = 0.6, center = true) hsw_hex2d(af + 1.2);  // mouth flange
        }
        translate([0,0,-L/2-0.5]) cube([SPRING_SLOT, af+4, L+2-1.2], center = true);          // central relief slot
        for (sx=[-1,1]) translate([sx*(halfX-SPRING_WALL-0.3),0,-L/2-1]) cube([1.4, af*0.5, L], center=true);  // arm hollow
    }
}

// per-cell connector dispatch:
//   peg     = friction peg, jams the bare grid hole (one-part)
//   barepeg = thin core peg, plugs into a SEPARATE latch pre-snapped in the wall
//   latch   = snap-tab insert, snaps into the bare grid hole
//   clip    = spring clip, snaps into the bare grid hole
module hsw_connector(type = "peg", fit = PEG_FIT_DEFAULT) {
    if      (type == "latch")   hsw_latch_insert(fit);
    else if (type == "clip")    hsw_clip(fit);
    else if (type == "barepeg") hsw_bare_peg(fit);
    else                        hsw_peg(fit);
}

// ============================================================================
//  hsw_grid(nx, ny, depth) — the FEMALE wall panel. Holes placed by the SAME
//  hsw_cx/hsw_cy so a hsw_mount(nx,ny) co-registers EXACTLY (proof artifact).
//  Lipped hex socket: 20mm bore, 1mm catch lip at 5mm depth.
// ============================================================================
module hsw_hex2d(af) { rotate([0,0,90]) circle(r = af/sqrt(3), $fn = 6); }

module hsw_hex_hole(depth) {
    afMouth = HSWX_INSIDE + 2*HSWX_LOCK_CHAMFER;
    afFull  = HSWX_INSIDE;
    afLip   = HSWX_INSIDE - 2*HSWX_LOCK_LIP;
    translate([0,0,-HSWX_LOCK_CHAMFER/2 + 0.01])
        linear_extrude(HSWX_LOCK_CHAMFER + 0.02, center=true, scale = afFull/afMouth) hsw_hex2d(afMouth);
    translate([0,0,-HSWX_LOCK_DEPTH/2])
        linear_extrude(HSWX_LOCK_DEPTH + 0.02, center=true) hsw_hex2d(afFull);
    translate([0,0,-HSWX_LOCK_DEPTH])
        linear_extrude(0.6, center=true) hsw_hex2d(afLip);
    backLen = depth - HSWX_LOCK_DEPTH;
    if (backLen > 0.1)
        translate([0,0,-(HSWX_LOCK_DEPTH + backLen/2)])
            linear_extrude(backLen + 0.4, center=true) hsw_hex2d(afFull);
}

module hsw_grid(nx = 3, ny = 3, depth = HSWX_DEPTH) {
    spanX = (nx-1)*HSW_PITCH_X;
    spanY = (ny-1)*HSW_PITCH_Y;
    slabW = spanX + HSWX_OUTSIDE;
    slabD = spanY + HSWX_OUTSIDE + hsw_max_stag(nx);
    difference() {
        translate([0, -hsw_max_stag(nx)/2, -depth/2]) cube([slabW, slabD, depth], center = true);
        for (c = [0 : nx-1])
            for (r = [0 : ny-1])
                translate([hsw_cx(c, nx), hsw_cy(c, r, nx, ny), 0]) hsw_hex_hole(depth);
    }
}

// ============================================================================
//  hsw_display() — canonical AnimBox display orientation. The geometry is built
//  in the OpenSCAD print frame (scad +Z = wall normal, +Y = up the wall). The
//  The OpenSCAD->AnimBox import is IDENTITY (verified by bbox: cube([10,20,40])
//  imports to world [10,20,40]). The wall-frame is already the display frame:
//  up-the-wall (+Y) = world UP, along-the-wall (+X) = world X, wall normal (+Z)
//  = toward the viewer (the body projects out of the wall). So hsw_display() is a
//  pass-through; it stays as the single place to adjust display orientation if the
//  import convention ever changes. Wrap every recipe's top call: hsw_display() my_part();
// ============================================================================
module hsw_display() { children(); }

// ---- product body (hidden internals) ----
eps   = 0.05;
PLATE = PLATE_T_DEFAULT;

module saddle_block() {
    bw  = cradle_d + 2 * wall;             // X width
    bh  = cradle_d + wall + front_lip;     // Y height
    z0  = PLATE - HSW_WELD;
    y0  = -bh / 2;
    ych = y0 + bh - cradle_d / 2 - wall;   // channel centre, near the top
    mouth = cradle_d * 0.62;               // retaining mouth (narrower than the item)
    difference() {
        translate([-bw / 2, y0, z0]) cube([bw, bh, reach + HSW_WELD]);
        // cradle channel: a cylinder along Z (front-to-back) the item rests in
        translate([0, ych, z0 - eps]) cylinder(h = reach + HSW_WELD + 2 * eps, d = cradle_d);
        // top mouth so the item snaps in (narrower than the channel -> retains it)
        translate([-mouth / 2, ych, z0 - eps]) cube([mouth, bh, reach + HSW_WELD + 2 * eps]);
    }
}
module u_hook() {
    hsw_mount(connectors_x, connectors_y, type = connector_type, clearance = clearance,
              straight = (support_style == "grid"), crot = connector_rotation);
    saddle_block();
}
hsw_display() u_hook();
```

#### 2D booleans

Shows union(), difference(), and intersection() applied to 2D circles and squares before a single extrude, so you can see each boolean result as a separate printed tile.

```openscad
// 2D booleans

/* 2D booleans - union / difference / intersection shown as three side-by-side tiles. */
r         = 18;   // circle radius
sq_side   = 28;   // square side length
thickness =  4;   // extrusion thickness
spacing   = 70;   // horizontal gap between tiles
$fn = 48;

// Helper: a circle and a square overlapping at their centres.
// The circle is offset so roughly half of it overlaps the square.
offset_x = sq_side * 0.3;  // how much to shift the circle right

module circle_shape()  { translate([offset_x, 0]) circle(r = r); }
module square_shape()  { translate([-sq_side / 2, -sq_side / 2]) square(sq_side); }

// --- Tile 1: union (all filled area) ---
translate([-spacing, 0, 0])
    linear_extrude(height = thickness)
        union() { circle_shape(); square_shape(); }

// --- Tile 2: difference (square minus circle) ---
translate([0, 0, 0])
    linear_extrude(height = thickness)
        difference() { square_shape(); circle_shape(); }

// --- Tile 3: intersection (only the overlapping region) ---
translate([spacing, 0, 0])
    linear_extrude(height = thickness)
        intersection() { circle_shape(); square_shape(); }
```

#### circle(), square(), polygon()

Lays the three core 2D primitives side by side and extrudes each to a thin slab so the result is a printable 3D object you can examine from every angle.

```openscad
// circle(), square(), polygon()

/* OpenSCAD 2D primitives are flat - they need linear_extrude()
   to become solid 3D geometry.
   circle(r)  - disc centred at the origin
   square(s)  - rectangle; center=true to straddle the origin
   polygon(p) - arbitrary shape from a list of [x,y] points      */

thick = 4;    // extrusion thickness (mm)
size  = 24;   // nominal size for each shape
gap   = 12;   // spacing between shapes
$fn   = 48;

// Circle - disc of given radius
color("steelblue")
    linear_extrude(height = thick)
        circle(r = size / 2);

// Square - centred so it sits at the same visual baseline as the circle
translate([size + gap, -size / 2, 0])
    color("tomato")
        linear_extrude(height = thick)
            square([size, size]);

// Polygon - a simple arrow-head shape
arrow = [
    [0,  0],
    [size * 0.6,  size / 2],
    [size * 0.3,  size / 2],
    [size * 0.3,  size],
    [-size * 0.3, size],
    [-size * 0.3, size / 2],
    [-size * 0.6, size / 2]
];

translate([(size + gap) * 2, -size / 2, 0])
    color("gold")
        linear_extrude(height = thick)
            polygon(arrow);
```

#### combining booleans

Shows how nesting union() inside difference() builds a simple mounting bracket from basic shapes. A vertical post and horizontal foot are joined first, then a bolt hole and slot are cut out.

```openscad
// combining booleans

/* Nested boolean example - a simple L-bracket. All dimensions in mm. */
foot_w    = 60;   // foot (horizontal part) width
foot_d    = 20;   // foot depth
foot_h    = 5;    // foot thickness
post_w    = 10;   // post (vertical part) width
post_h    = 50;   // post height
bolt_d    = 5;    // diameter of bolt holes
slot_w    = 6;    // slot width on the post
slot_len  = 20;   // slot length on the post
$fn = 32;

// Step 1: unite the two solid bodies
module bracket_solid() {
    union() {
        // Horizontal foot
        cube([foot_w, foot_d, foot_h]);

        // Vertical post rising from the left end of the foot
        translate([0, 0, foot_h])
            cube([post_w, foot_d, post_h]);
    }
}

// Step 2: subtract holes and a slot from the solid
difference() {
    bracket_solid();

    // Two bolt holes in the foot, evenly spaced
    for (x = [foot_w * 0.25, foot_w * 0.75])
        translate([x, foot_d / 2, -1])
            cylinder(d = bolt_d, h = foot_h + 2);

    // An adjustment slot centred in the post
    translate([post_w / 2, foot_d / 2, foot_h + (post_h - slot_len) / 2])
        rotate([90, 0, 0])
            // Slot = hull of two cylinders
            hull() {
                translate([0,  slot_len / 2 - slot_w / 2, 0]) cylinder(d = slot_w, h = foot_d + 2, center = true);
                translate([0, -slot_len / 2 + slot_w / 2, 0]) cylinder(d = slot_w, h = foot_d + 2, center = true);
            }
}
```

#### children() in a module

Defines a module that places whatever geometry you pass into it at evenly-spaced positions around a ring, using children() and a for loop with rotate().

```openscad
// children() in a module

/* children() lets a module receive geometry from its caller,
   just like a function receives arguments.
   The ring_array module below rotates its child N times around Z,
   each time moving it out to a chosen radius first. */

count  = 6;     // number of copies in the ring
radius = 35;    // ring radius (mm)
peg_d  = 8;     // diameter of each peg
peg_h  = 15;    // height of each peg
$fn    = 32;

// Module: arrange children() in a circular ring
module ring_array(n, r) {
    for (i = [0 : n - 1]) {
        angle = 360 / n * i;
        rotate([0, 0, angle])
            translate([r, 0, 0])
                children();
    }
}

// A thin base plate so the pegs have something to stand on
cylinder(h = 3, d = (radius + peg_d) * 2 + 4, $fn = 64);

// Pass a cylinder peg as the child geometry
color("steelblue")
    ring_array(count, radius)
        cylinder(h = peg_h, d = peg_d);
```

#### circle() and square()

Shows the two core 2D primitives - circle() and square() - combined with 2D union and difference to form a rounded-corner key-tag shape, then extruded into a printable part.

```openscad
// circle() and square()

/* circle() and square() - core 2D shapes, combined then extruded. */
tag_w     = 50;   // key-tag body width
tag_h     = 28;   // key-tag body height
thickness =  4;   // extrusion thickness
hole_r    =  4;   // hang-hole radius
corner_r  =  6;   // rounded corner radius (approximated with circles at hull)
$fn = 48;

// Build the 2D key-tag outline:
//   1. hull() of four corner circles => rounded rectangle body
//   2. union with a circle at the top for the hang-lug
//   3. difference to punch the hang hole
module key_tag_2d() {
    difference() {
        union() {
            // Rounded rectangle body via hull of corner discs
            hull()
                for (x = [corner_r, tag_w - corner_r], y = [corner_r, tag_h - corner_r])
                    translate([x, y]) circle(r = corner_r);
            // Hang lug: a circle centred above the tag
            translate([tag_w / 2, tag_h + hole_r * 1.5])
                circle(r = hole_r * 1.8);
        }
        // Punch the hang hole
        translate([tag_w / 2, tag_h + hole_r * 1.5])
            circle(r = hole_r);
    }
}

// Extrude the finished 2D shape into a solid tag.
linear_extrude(height = thickness)
    key_tag_2d();
```

#### color()

Demonstrates color() with named CSS colors and an RGBA alpha value. Three spheres overlap: two use named colors and the third is translucent so you can see through it.

```openscad
// color()

/* color() sets the display colour of its child.
   Use a named colour string: "red", "steelblue", "gold", etc.
   Or pass [r, g, b, a] with values from 0 to 1; alpha < 1 = translucent.
   Note: colour is a preview/render hint - it has no effect on STL export. */

r   = 14;   // sphere radius (mm)
$fn = 40;

// Named colour - solid red
color("crimson")
    translate([-r, 0, r])
        sphere(r = r);

// Named colour - solid teal
color("steelblue")
    translate([r, 0, r])
        sphere(r = r);

// RGBA - semi-transparent gold sphere overlapping both
color([1, 0.84, 0, 0.4])
    translate([0, 0, r * 1.8])
        sphere(r = r);
```

#### cube()

Shows the two common forms of cube(): an uncentered one sitting on the origin and a centered one floating above it. Tune size and spacing to explore the difference.

```openscad
// cube()

/* cube() - uncentered vs centered.
   The uncentered cube grows into +X +Y +Z from the origin.
   With center=true the cube straddles the origin on all three axes. */

side    = 20;   // cube side length (mm)
gap     = 8;    // space between the two cubes
lift    = 30;   // how high the centered cube floats above the floor

// Uncentered cube: corner at origin, fills +X +Y +Z quadrant
cube([side, side, side], center = false);

// Centered cube: placed above so both are visible in the same view
translate([0, 0, lift])
    cube([side, side, side], center = true);
```

#### cylinder() and cones

Shows three forms of cylinder() standing side by side: a plain cylinder, a sharp cone (r2=0), and a truncated cone (r1 not equal r2). Tune the radii and height freely.

```openscad
// cylinder() and cones

/* cylinder() supports r/d for uniform radius and r1,r2 (or d1,d2)
   for tapered shapes.  r2=0 makes a pointed cone. */

h    = 30;   // height of all three shapes (mm)
r1   = 12;   // base radius
r2   = 6;    // top radius for the truncated cone
gap  = 10;   // spacing between shapes
$fn  = 40;

// Plain cylinder - equal top and bottom radius
translate([-r1 * 2 - gap, 0, 0])
    cylinder(h = h, r = r1);

// Sharp cone - top radius shrinks to zero
cylinder(h = h, r1 = r1, r2 = 0);

// Truncated cone - different radii at each end
translate([r1 * 2 + gap, 0, 0])
    cylinder(h = h, r1 = r1, r2 = r2);
```

#### difference()

Shows how difference() carves one shape out of another. A cylinder is subtracted from a solid cube to create a round through-hole - the foundation of almost every functional part.

```openscad
// difference()

/* difference() example - subtracting a cylinder from a cube. All dimensions in mm. */
box_w    = 50;   // cube width (X)
box_d    = 50;   // cube depth (Y)
box_h    = 20;   // cube height (Z)
hole_d   = 18;   // diameter of the hole
wall_min = 5;    // minimum wall thickness around the hole (informational)
$fn = 48;

// difference(): the FIRST child is the base solid;
// all remaining children are subtracted from it.
difference() {
    // Base solid - a plain cube
    cube([box_w, box_d, box_h]);

    // The cylinder to remove - centred in XY, reaching full depth (+1 mm each end avoids z-fighting)
    translate([box_w / 2, box_d / 2, -1])
        cylinder(d = hole_d, h = box_h + 2);
}
```

#### difference() with a hole pattern

Shows how difference() combined with a for loop removes a grid of holes from a flat plate. Tune the grid spacing and hole size to create your own custom perforation pattern.

```openscad
// difference() with a hole pattern

/* difference() with a for-loop hole grid. All dimensions in mm. */
plate_w  = 80;   // plate width  (X)
plate_d  = 60;   // plate depth  (Y)
plate_h  = 4;    // plate thickness
margin   = 10;   // clear border around the hole grid
spacing  = 14;   // centre-to-centre distance between holes
hole_d   = 7;    // diameter of each hole
$fn = 32;

difference() {
    // Base plate
    cube([plate_w, plate_d, plate_h]);

    // Grid of holes: for loops generate a list of children for difference()
    for (x = [margin : spacing : plate_w - margin],
         y = [margin : spacing : plate_d - margin])
        translate([x, y, -1])
            cylinder(d = hole_d, h = plate_h + 2);
}
```

#### extrude a computed polygon

Shows how a list comprehension builds the [x, y] points for a wavy disc outline, which polygon() then assembles and linear_extrude() lifts into a printable coaster or badge.

```openscad
// extrude a computed polygon

/* Computed polygon via list comprehension - wavy disc coaster or badge. */
base_r   = 35;   // average radius of the disc
waves    = 12;   // number of wave peaks around the edge
wave_amp =  5;   // amplitude of each wave (mm)
height   =  4;   // extrusion height
steps    = 120;  // polygon resolution (points around the circle)
$fn = 64;

// List comprehension builds one [x, y] point per step.
// The radius oscillates between (base_r - wave_amp) and (base_r + wave_amp).
wavy_pts = [
    for (i = [0 : steps - 1])
        let(
            angle = 360 * i / steps,
            r     = base_r + wave_amp * sin(waves * angle)
        )
        [r * cos(angle), r * sin(angle)]
];

// polygon() accepts our computed points directly.
linear_extrude(height = height)
    polygon(points = wavy_pts);
```

#### $fn, $fa, $fs

Shows three cylinders of the same size rendered with different facet controls - coarse explicit $fn, fine explicit $fn, and angle-driven $fa - so you can see exactly how each setting shapes round geometry.

```openscad
// $fn, $fa, $fs

/* OpenSCAD uses three special variables to control curve resolution:
     $fn - fixed segment count (overrides $fa and $fs when > 0)
     $fa - minimum angle per segment in degrees (default 12)
     $fs - minimum segment length in mm (default 2)
   When $fn = 0 OpenSCAD picks the segment count from $fa and $fs.
   A larger $fn or smaller $fa/$fs = smoother curves = slower render. */

d  = 30;   // cylinder diameter (mm)
h  = 20;   // cylinder height (mm)
sp = 50;   // spacing between cylinders

// Coarse: only 6 segments - very angular, renders instantly
color("tomato")
    cylinder(h = h, d = d, $fn = 6);

// Fine: 48 segments - nearly smooth, good for most prints
translate([sp, 0, 0])
    color("steelblue")
        cylinder(h = h, d = d, $fn = 48);

// Auto from $fa: 10 degrees per segment = 36 segments for a full circle
// ($fs = 1 keeps the segment limit from kicking in for this diameter)
translate([sp * 2, 0, 0])
    color("gold")
        cylinder(h = h, d = d, $fn = 0, $fa = 10, $fs = 1);
```

#### for loop

A row of cylindrical pillars that grow taller from left to right, showing how a for loop iterates over a range and how loop variable drives both position and size.

```openscad
// for loop

/* A row of pillars of increasing height - demonstrating the for loop.
   Each pillar index i controls both the x position and the height.
   Great first example for anyone learning parametric loops. */

pillar_count  = 8;      // number of pillars in the row
pillar_d      = 8;      // pillar diameter (mm)
spacing       = 14;     // centre-to-centre spacing (mm)
min_height    = 10;     // shortest pillar height (mm)
height_step   = 8;      // how much taller each successive pillar gets (mm)
$fn = 32;

for (i = [0 : pillar_count - 1]) {
    h = min_height + i * height_step;   // height grows with each step
    translate([i * spacing, 0, 0])
        cylinder(d = pillar_d, h = h);
}
```

#### for + rotate ring

A ring of evenly spaced round pegs arranged in a circle using a for loop combined with rotate. Tune the number of pegs, ring radius, and peg size to explore radial symmetry.

```openscad
// for + rotate ring

/* Ring of pegs - demonstrating for + rotate to place objects in a circle.
   The loop variable a is the angle step, derived from the total peg count.
   Rotating a translated peg sweeps it around the origin. */

peg_count  = 10;   // how many pegs in the ring
ring_r     = 30;   // radius from centre to peg centre (mm)
peg_d      = 7;    // peg diameter (mm)
peg_h      = 18;   // peg height (mm)
base_h     = 3;    // thin base disk height (mm)
base_d     = ring_r * 2 + peg_d + 4;  // base diameter sized to hold all pegs
$fn = 40;

// flat base disk so the part is printable without support
cylinder(d = base_d, h = base_h);

// place one peg per iteration, rotating it around the Z axis
for (a = [0 : 360 / peg_count : 359]) {
    rotate([0, 0, a])
        translate([ring_r, 0, base_h])
            cylinder(d = peg_d, h = peg_h);
}
```

#### defining a function

A decorative arch whose keystone positions are computed by a user-defined function that evaluates a parametric circle. Shows how functions return values that drive geometry, keeping the math out of the module calls.

```openscad
// defining a function

/* Arch with mathematically placed keystones - demonstrating user functions.
   arch_pt(angle, r) returns [x, y] on a circle of radius r.
   The for loop calls it to position each voussoir (arch block) correctly. */

arch_r      = 40;    // arch inner radius (mm)
arch_thick  = 10;    // radial thickness of each keystone block (mm)
block_span  = 22;    // angular span of each block (degrees)
num_blocks  = 7;     // number of arch keystones (use an odd number for symmetry)
extrude_d   = 8;     // depth of the arch (mm)
$fn = 48;

// user function: a point on a circle of radius r at the given angle (degrees)
function arch_pt(angle, r) = [r * cos(angle), r * sin(angle)];

// place each keystone as a hull between two arc segments
for (i = [0 : num_blocks - 1]) {
    // centre angle of this block, spread across 180 degrees
    a_mid = 180 * (i + 0.5) / num_blocks;
    a0    = a_mid - block_span / 2;
    a1    = a_mid + block_span / 2;

    inner = arch_r;
    outer = arch_r + arch_thick;

    linear_extrude(height = extrude_d)
        polygon([
            arch_pt(a0, inner),
            arch_pt(a1, inner),
            arch_pt(a1, outer),
            arch_pt(a0, outer)
        ]);
}

// two rectangular piers at the base
translate([-arch_r - arch_thick, -arch_thick * 1.5, 0])
    cube([arch_thick, arch_thick * 1.5, extrude_d]);
translate([arch_r, -arch_thick * 1.5, 0])
    cube([arch_thick, arch_thick * 1.5, extrude_d]);
```

#### hull() in 2D

Shows how hull() wraps a convex skin around a set of 2D circles to create a smooth rounded slot outline. The result is extruded into a printable plate with a slotted hole.

```openscad
// hull() in 2D

/* hull() in 2D - rounded slot plate. All dimensions in mm. */
plate_w    = 80;   // plate overall width
plate_d    = 50;   // plate overall depth
plate_h    = 4;    // plate thickness
slot_len   = 40;   // centre-to-centre length of the slot
slot_r     = 6;    // slot end radius (half the slot width)
corner_r   = 4;    // plate corner rounding radius
$fn = 48;

module rounded_plate(w, d, h, r) {
    // A plate with rounded corners using hull() around four corner cylinders
    linear_extrude(h)
        hull() for (x = [r, w - r], y = [r, d - r])
            translate([x, y]) circle(r = r);
}

module slot_2d(len, r) {
    // A rounded slot: hull() of two circles gives a stadium / discorectangle
    hull() {
        translate([-len / 2, 0]) circle(r = r);
        translate([ len / 2, 0]) circle(r = r);
    }
}

difference() {
    rounded_plate(plate_w, plate_d, plate_h, corner_r);

    // Slot centred in the plate, punched through with +1 mm clearance
    translate([plate_w / 2, plate_d / 2, -1])
        linear_extrude(plate_h + 2)
            slot_2d(slot_len, slot_r);
}
```

#### hull() in 3D

Shows how hull() wraps a convex skin around spheres placed at the corners of a box to produce a solid with smoothly rounded edges and corners. Tune the box size and corner radius freely.

```openscad
// hull() in 3D

/* hull() in 3D - a box with fully rounded corners and edges. All dimensions in mm. */
box_w  = 60;   // outer width  (X)
box_d  = 40;   // outer depth  (Y)
box_h  = 25;   // outer height (Z)
r      = 5;    // corner sphere radius (determines how round the edges are)
$fn = 40;

// Place one sphere at each of the 8 corners of the (r-inset) bounding box.
// hull() wraps them all - the result is a box where every edge and corner
// is a smooth arc of radius r.
hull() for (x = [r, box_w - r], y = [r, box_d - r], z = [r, box_h - r])
    translate([x, y, z]) sphere(r = r);
```

#### intersection()

Shows how intersection() keeps only the volume shared by all children. A cube and a sphere overlap to produce a rounded cube-corner gem shape - great for learning Boolean logic.

```openscad
// intersection()

/* intersection() example - only the shared volume survives. All dimensions in mm. */
cube_size  = 45;   // side length of the cube
sphere_d   = 55;   // diameter of the sphere (should be larger than cube for a visible effect)
$fn = 64;

// intersection() keeps only geometry that is INSIDE every child at once.
// The result here is a cube with every face pushed inward by the sphere surface.
intersection() {
    // A centred cube
    translate([-cube_size / 2, -cube_size / 2, -cube_size / 2])
        cube([cube_size, cube_size, cube_size]);

    // A sphere centred at the same origin - rounds all eight corners of the cube
    sphere(d = sphere_d);
}
```

#### intersection_for()

A faceted gem-like solid created by intersecting several rotated slabs using intersection_for(). Each iteration rotates a flat slab by an equal angle step, and the intersection keeps only the shared core.

```openscad
// intersection_for()

/* Faceted gem via intersection_for() - each rotated slab clips the result.
   intersection_for() is like a for loop but AND-s every child together
   instead of union-ing them, producing the overlapping core region. */

slab_count = 7;     // number of rotated slabs (more = rounder gem)
slab_w     = 50;    // slab width (mm)
slab_d     = 50;    // slab depth (mm)
slab_h     = 14;    // slab thickness - controls how "tall" the gem is (mm)
$fn = 32;

intersection_for (i = [0 : slab_count - 1]) {
    angle = i * (180 / slab_count);   // spread slabs evenly through 180 degrees
    rotate([0, angle, 0])
        cube([slab_w, slab_d, slab_h], center = true);
}
```

#### let() and assignment

A set of nested hexagonal tiles whose size and position are computed with let() expressions, showing how to name intermediate values inside a comprehension without polluting the outer scope.

```openscad
// let() and assignment

/* Hexagonal tile grid using let() to name intermediate values.
   let(var = expr, ...) inside a list comprehension keeps each calculation
   readable and avoids repeating the same sub-expression multiple times. */

tile_count  = 4;     // tiles per row and column
hex_r       = 10;    // hexagon circumradius (mm)
gap         = 2;     // gap between tiles (mm)
tile_h      = 3;     // extrude height of each tile (mm)
$fn = 6;             // 6 sides makes cylinder() a hexagonal prism

// generate [x, y, scale] for each tile using let() inside the comprehension
tiles = [
    for (c = [0 : tile_count - 1], r = [0 : tile_count - 1])
        let(
            col_offset = (r % 2 == 0) ? 0 : hex_r * 0.866,   // offset odd rows for hex packing
            x          = c * (hex_r * 1.732 + gap) + col_offset,
            y          = r * (hex_r * 1.5   + gap),
            scale_f    = 0.7 + 0.3 * ((c + r) % 3) / 2       // vary size for visual interest
        )
        [x, y, scale_f]
];

// place a hexagonal prism at each computed position
for (t = tiles) {
    translate([t[0], t[1], 0])
        cylinder(r = hex_r * t[2], h = tile_h);
}
```

#### linear_extrude()

Shows how linear_extrude() lifts a flat 2D star polygon straight up into a 3D prism. Tune the star size, point count, and height.

```openscad
// linear_extrude()

/* linear_extrude() - turns a 2D polygon into a 3D solid by pulling it upward. */
outer_r  = 30;   // outer tip radius of the star
inner_r  = 14;   // inner notch radius
points_n = 6;    // number of star points
height   = 20;   // extrusion height
$fn = 64;

// Build the star outline as a polygon from alternating outer/inner vertices.
module star2d(n, ro, ri) {
    step = 360 / n;
    pts = [
        for (i = [0 : n - 1])
            for (pt = [
                [ro * cos(i * step),          ro * sin(i * step)],
                [ri * cos(i * step + step/2), ri * sin(i * step + step/2)]
            ]) pt
    ];
    polygon(pts);
}

// Extrude the star upward - this is the key call being taught.
linear_extrude(height = height)
    star2d(points_n, outer_r, inner_r);
```

#### linear_extrude(scale=)

Shows how scale= in linear_extrude() shrinks the cross-section as it rises, turning a hexagonal base into a tapered obelisk. Tune base size, height, and tip scale.

```openscad
// linear_extrude(scale=)

/* linear_extrude(scale=) - shrinks (or grows) the cross-section from bottom to top. */
base_r   = 28;   // circumradius of the hexagonal base
height   = 50;   // total height of the obelisk
tip_scale = 0.1; // fraction of the base size at the very top (0.0 = true point)
sides    = 6;    // number of polygon sides
$fn = 64;

// A regular polygon at the base.
module base_profile(r, n) {
    circle(r = r, $fn = n);
}

// Extrude with scale - the cross-section tapers to tip_scale * original at the top.
linear_extrude(
    height = height,
    scale  = tip_scale   // key parameter: shrink top to a near-point
)
    base_profile(base_r, sides);
```

#### linear_extrude(twist=)

Shows how adding a twist= angle to linear_extrude() spirals a square profile into a decorative twisted column. Adjust height, twist degrees, and profile size.

```openscad
// linear_extrude(twist=)

/* linear_extrude(twist=) - rotates the cross-section as it rises to create a spiral. */
col_size  = 16;   // side length of the square cross-section
col_height = 60;  // total column height
twist_deg = 120;  // total twist in degrees over the height
taper     = 0.85; // slight width reduction at top (scale factor)
$fn = 48;

// A square with notched corners for a more interesting silhouette.
module cross_section(s) {
    r = s * 0.12; // small corner radius expressed as square fraction
    offset(r = r) offset(r = -r)
        square([s, s], center = true);
}

// linear_extrude with twist - the teaching moment.
linear_extrude(
    height = col_height,
    twist  = twist_deg,
    slices = 60,          // more slices = smoother spiral
    scale  = taper
)
    cross_section(col_size);
```

#### list comprehension with condition

A sparse grid of pillars where each pillar is placed only when a mathematical condition holds, built with a list comprehension containing an if filter. Shows how to selectively include items in a generated list.

```openscad
// list comprehension with condition

/* Sparse pillar grid using a list comprehension with an if condition.
   The comprehension produces only the [x, y] positions where the
   sum of grid indices is NOT divisible by 3, giving a pattern with gaps. */

cols     = 7;    // columns in the grid
rows     = 7;    // rows in the grid
spacing  = 14;   // centre-to-centre spacing (mm)
pillar_d = 7;    // pillar diameter (mm)
pillar_h = 20;   // pillar height (mm)
$fn = 24;

// build a list of [x, y] positions satisfying the condition
positions = [
    for (c = [0 : cols - 1], r = [0 : rows - 1])
        if ((c + r) % 3 != 0)        // skip every position where c+r is a multiple of 3
            [c * spacing, r * spacing]
];

// place a pillar at each position in the filtered list
for (pos = positions)
    translate([pos[0], pos[1], 0])
        cylinder(d = pillar_d, h = pillar_h);
```

#### list comprehension

A smooth wave-profile wall built by generating polygon outline points with a list comprehension and then extruding the result. Shows how to build point arrays mathematically rather than typing each coordinate by hand.

```openscad
// list comprehension

/* Wave-profile wall - demonstrating list comprehension to generate polygon points.
   A list comprehension [ for (var = range) expression ] builds the point list
   for a sine wave cross-section, which is then swept with linear_extrude. */

steps      = 60;    // number of polygon segments (more = smoother wave)
wave_w     = 80;    // total width of one wave cycle (mm)
amplitude  = 8;     // wave peak-to-trough half-height (mm)
mid_h      = 20;    // centre height of the wave profile (mm)
wall_thick = 4;     // wall depth (extrude distance, mm)

// build the closed polygon outline: top wave then bottom straight edge
wave_pts = [
    // top edge: sine wave from left to right
    for (i = [0 : steps])
        [i * wave_w / steps,
         mid_h + amplitude * sin(i * 360 / steps)],
    // bottom-right and bottom-left corners to close the shape
    [wave_w, 0],
    [0,      0]
];

linear_extrude(height = wall_thick)
    polygon(wave_pts);
```

#### minkowski() in 2D

Shows how minkowski() of a 2D square with a circle inflates and rounds the outline uniformly. The resulting smooth 2D profile is extruded into a thick printable pad with softened edges.

```openscad
// minkowski() in 2D

/* minkowski() in 2D - a rounded rectangle profile extruded into a pad. All dimensions in mm. */
sq_w  = 50;   // inner rectangle width  (final outer = sq_w + 2*r)
sq_d  = 30;   // inner rectangle depth
r     = 6;    // fillet radius - the circle radius used in minkowski
pad_h = 8;    // extrusion height of the finished pad
$fn = 48;

// minkowski() in 2D: sweeping a square through a circle inflates every edge outward
// by r and turns every sharp corner into a circular arc - identical to offset(r=r)
// but expressed as a Minkowski sum, which generalises to non-circular sweepers.
linear_extrude(height = pad_h)
    minkowski() {
        square([sq_w, sq_d]);   // base 2D shape
        circle(r = r);          // circle adds the rounded offset
    }
```

#### minkowski() rounding

Shows how minkowski() of a box with a small sphere rounds every edge and corner simultaneously. The technique generalises to any shape. Note that the overall size grows by the sphere radius on each side.

```openscad
// minkowski() rounding

/* minkowski() rounding - a box with all edges and corners rounded. All dimensions in mm. */
box_w  = 50;   // inner box width  before rounding (final = box_w + 2*r)
box_d  = 35;   // inner box depth  before rounding
box_h  = 20;   // inner box height before rounding
r      = 4;    // rounding radius applied to ALL edges and corners
$fn = 32;

// minkowski() sweeps every point of the first child through every point of the second.
// A box swept through a sphere produces a box whose corners become spherical and
// whose edges become cylindrical - a perfect all-direction fillet.
minkowski() {
    cube([box_w, box_d, box_h]);   // base shape (un-rounded)
    sphere(r = r);                 // rounding element - radius = fillet size
}
```

#### mirror()

Creates an asymmetric L-shaped bracket and then reflects it across the Y-Z plane using mirror(), producing a matching left-hand part from a single definition.

```openscad
// mirror()

/* mirror([nx, ny, nz]) reflects geometry through the plane whose
   normal is [nx, ny, nz].  mirror([1,0,0]) reflects across the Y-Z
   plane, turning a right-hand part into a left-hand part. */

thick  = 5;    // material thickness (mm)
foot_w = 30;   // horizontal leg length
foot_h = 8;    // horizontal leg height
wall_h = 25;   // vertical leg height
gap    = 6;    // gap between original and mirrored copy

// Helper module - asymmetric L-bracket
module l_bracket() {
    // Horizontal foot
    cube([foot_w, thick, foot_h]);
    // Vertical wall at the left end of the foot
    cube([thick, thick, wall_h]);
}

// Original bracket
color("steelblue")
    l_bracket();

// Mirrored bracket - reflected across X=0 plane, then shifted right
translate([gap, 0, 0])
    color("tomato")
        mirror([1, 0, 0])
            l_bracket();
```

#### defining a module

A parametric rounded peg module defined once and called multiple times with different sizes. Shows how modules encapsulate reusable geometry so you can place many variations without repeating code.

```openscad
// defining a module

/* Rounded peg module - demonstrating module definition and reuse.
   The module takes diameter, height, and tip-rounding as parameters.
   Calling it several times with different values shows why modules matter. */

$fn = 40;

// --- module definition ---
module rounded_peg(d, h, tip_r = 2) {
    // cylindrical body
    cylinder(d = d, h = h - tip_r);
    // rounded dome on top
    translate([0, 0, h - tip_r])
        sphere(r = d / 2);
}

// --- place several pegs in a row with varying dimensions ---
rounded_peg(d = 8,  h = 20, tip_r = 4);

translate([14, 0, 0])
    rounded_peg(d = 10, h = 30, tip_r = 5);

translate([28, 0, 0])
    rounded_peg(d = 12, h = 15, tip_r = 3);

translate([46, 0, 0])
    rounded_peg(d = 6,  h = 25, tip_r = 3);
```

#### module default args

A shelf bracket module with sensible default dimensions so it works with no arguments at all, but can be customised with named-argument calls. Demonstrates how default values reduce boilerplate and how named arguments improve readability.

```openscad
// module default args

/* Shelf bracket with default arguments - demonstrating named-argument calls.
   Call bracket() for a standard bracket, or override any individual
   dimension by name, e.g. bracket(arm = 60, thick = 4). */

module bracket(arm = 50, rise = 40, thick = 3, gusset = true) {
    // horizontal arm
    cube([arm, thick, thick]);
    // vertical rise
    cube([thick, thick, rise]);
    // diagonal gusset (only when requested)
    if (gusset) {
        rotate([0, 0, 0])
        translate([thick, 0, thick])
            rotate([0, 45, 0])
                cube([rise * 0.7, thick, thick]);
    }
}

// call with all defaults
bracket();

// call with a named override - longer arm, no gusset
translate([0, 20, 0])
    bracket(arm = 70, gusset = false);

// call with several named overrides
translate([0, 40, 0])
    bracket(arm = 40, rise = 30, thick = 5, gusset = true);
```

#### multmatrix() shear

Uses multmatrix() to apply a shear transform to a cube, leaning its top face in the X direction - a distortion that cannot be achieved with rotate() or scale() alone.

```openscad
// multmatrix() shear

/* multmatrix(m) applies a full 4x4 homogeneous matrix to its child.
   This unlocks shear, which tilts one axis relative to another.
   The matrix below shears X by a fraction of Z:
     X' = X + shear * Z,  Y' = Y,  Z' = Z
   So the top face slides sideways while the base stays put. */

side  = 20;   // cube side length (mm)
shear = 0.5;  // how many mm of X shift per mm of Z height

// Shear matrix: identity with shear coefficient in row 0, col 2
shear_matrix = [
    [1, 0, shear, 0],
    [0, 1, 0,     0],
    [0, 0, 1,     0],
    [0, 0, 0,     1]
];

// Reference cube - no transform
color("silver")
    cube([side, side, side]);

// Sheared cube placed to the right so both are visible
translate([side * 2, 0, 0])
    color("steelblue")
        multmatrix(shear_matrix)
            cube([side, side, side]);
```

#### nested for

A checkerboard grid of short square pillars built with two nested for loops, one for columns and one for rows. Shows how to iterate over two axes at once and skip alternating cells.

```openscad
// nested for

/* Checkerboard grid of pillars - demonstrating nested for loops.
   The outer loop walks columns (x), the inner loop walks rows (y).
   A modulo test skips every other cell to create the checkerboard pattern. */

cols     = 6;    // number of columns
rows     = 6;    // number of rows
cell     = 14;   // grid cell size (mm)
pillar_w = 9;    // pillar side length (mm)
pillar_h = 12;   // pillar height (mm)

for (c = [0 : cols - 1]) {
    for (r = [0 : rows - 1]) {
        // place a pillar only on "white" squares of the checkerboard
        if ((c + r) % 2 == 0) {
            translate([c * cell, r * cell, 0])
                cube([pillar_w, pillar_w, pillar_h]);
        }
    }
}
```

#### offset() delta + chamfer

Shows how offset(delta=, chamfer=true) shifts a 2D outline by a fixed amount and adds sharp 45-degree chamfers at corners instead of arcs. An outer and inset shell are stacked to show the contrast.

```openscad
// offset() delta + chamfer

/* offset(delta=, chamfer=) - sharp inset and outset chamfered outlines. All dimensions in mm. */
base_w  = 60;   // base rectangle width
base_d  = 45;   // base rectangle depth
delta   = 5;    // offset amount (positive = outward, negative = inward)
h_outer = 6;    // height of outer extruded ring
h_inner = 10;   // height of inner extruded post
$fn = 32;

module base_2d() {
    // A simple rectangle - the shape we will offset
    square([base_w, base_d]);
}

// --- Outer chamfered ring ---
// offset(delta=+d, chamfer=true) grows the outline and chamfers convex corners at 45 deg.
// Subtracting the original leaves only the chamfered border ring.
linear_extrude(height = h_outer)
    difference() {
        offset(delta = delta, chamfer = true) base_2d();
        base_2d();
    }

// --- Inner chamfered post ---
// offset(delta=-d, chamfer=true) shrinks the outline inward with chamfered corners.
translate([0, 0, h_outer])
    linear_extrude(height = h_inner)
        offset(delta = -delta, chamfer = true) base_2d();
```

#### offset() rounded

Shows how offset(r=) inflates a 2D polygon outward and smoothly rounds every concave and convex corner. An L-shaped profile is offset then extruded into a printable bracket tile.

```openscad
// offset() rounded

/* offset(r=) rounding - an L-shape inflated with smooth corners. All dimensions in mm. */
arm_w  = 12;   // width of each arm of the L
arm_l  = 40;   // length of each arm
r      = 4;    // rounding radius (positive = outward + rounded corners)
h      = 6;    // extrusion height
$fn = 48;

// The raw L-shape as a polygon (two rectangles forming an L)
module l_shape_2d(aw, al) {
    polygon([
        [0,  0],
        [al, 0],
        [al, aw],
        [aw, aw],
        [aw, al],
        [0,  al]
    ]);
}

// offset(r=) with a positive radius rounds ALL corners (concave ones become fillets,
// convex ones become circular arcs) and inflates the outline by r.
linear_extrude(height = h)
    offset(r = r)
        l_shape_2d(arm_w, arm_l);
```

#### polygon() from points

Shows how polygon() takes an explicit list of [x, y] points to define any custom shape - here a bold right-pointing arrow - then extrudes it into a printable marker.

```openscad
// polygon() from points

/* polygon() with explicit points - a bold arrow head extruded into a flat marker. */
arrow_w   = 60;   // total width (left edge to arrow tip)
arrow_h   = 40;   // total height (top to bottom of the shaft + head)
shaft_h   = 16;   // height of the rectangular shaft
head_inset = 20;  // how far the arrowhead base is from the left edge
thickness  =  5;  // extrusion depth

// Explicit point list that traces the arrow outline counter-clockwise.
// The shape: a rectangle shaft on the left + a triangle head on the right.
arrow_pts = [
    [0,           (arrow_h - shaft_h) / 2],          // shaft bottom-left
    [head_inset,  (arrow_h - shaft_h) / 2],          // shaft bottom-right / head base-lower
    [head_inset,  0],                                 // head bottom corner
    [arrow_w,     arrow_h / 2],                       // arrow tip
    [head_inset,  arrow_h],                           // head top corner
    [head_inset,  (arrow_h + shaft_h) / 2],           // head base-upper
    [0,           (arrow_h + shaft_h) / 2]            // shaft top-left
];

// polygon() - the focus of this recipe.
linear_extrude(height = thickness)
    polygon(points = arrow_pts);
```

#### polyhedron()

Builds a square pyramid by hand using polyhedron() with a list of 5 corner points and 5 faces. A great introduction to defining arbitrary closed solids vertex by vertex.

```openscad
// polyhedron()

/* polyhedron() - square pyramid from scratch.
   Points are listed as [x, y, z] coordinates.
   Each face is a list of point indices ordered so the outward
   normal follows the right-hand rule (counter-clockwise when
   viewed from outside). */

base = 30;   // square base side length (mm)
apex = 25;   // pyramid height (mm)

half = base / 2;

// 5 vertices: 4 base corners + 1 apex
pts = [
    [-half, -half, 0],   // 0 - front-left
    [ half, -half, 0],   // 1 - front-right
    [ half,  half, 0],   // 2 - back-right
    [-half,  half, 0],   // 3 - back-left
    [    0,     0, apex] // 4 - apex
];

// 5 faces: 1 square base (split into 2 triangles) + 4 triangular sides
faces = [
    [0, 3, 2, 1],   // base (winding viewed from below)
    [0, 1, 4],      // front face
    [1, 2, 4],      // right face
    [2, 3, 4],      // back face
    [3, 0, 4]       // left face
];

polyhedron(points = pts, faces = faces);
```

#### projection()

Shows how projection() collapses a 3D object down to a flat 2D silhouette in the XY plane, which can then be re-extruded into a thin shadow-stamp. Tune the source shape and stamp thickness.

```openscad
// projection()

/* projection() - flatten a 3D solid to a 2D outline, then re-extrude as a stamp. */
stamp_thickness = 2.5;  // how thick the re-extruded stamp plate is
cone_r   = 20;          // base radius of the source cone
cone_h   = 30;          // height of the source cone
$fn = 48;

// The 3D shape whose silhouette we want.
// We use it only as input to projection(); it is NOT rendered itself.
module source_shape() {
    // A cone with a small sphere on top - interesting shadow outline.
    cylinder(h = cone_h, r1 = cone_r, r2 = 4);
    translate([0, 0, cone_h]) sphere(r = 6);
}

// projection(cut = false) casts a shadow of the 3D shape down onto Z = 0.
// linear_extrude then lifts that 2D footprint into a flat stamp.
linear_extrude(height = stamp_thickness)
    projection(cut = false)   // false = true silhouette (not a cross-section cut)
        source_shape();
```

#### ranges [a:step:b]

A staircase of rectangular steps generated with a stepped range loop. Each iteration advances by the step size in both height and depth, showing how the three-argument range controls spacing between loop values.

```openscad
// ranges [a:step:b]

/* Staircase built with a stepped range - demonstrating [start : step : end].
   The loop variable z also drives the y position so each step sits on top
   of the previous one, forming a proper rising stair. */

step_count  = 8;     // total number of steps
step_rise   = 10;    // vertical rise per step (mm)
step_run    = 15;    // horizontal depth per step (mm)
step_width  = 40;    // width of the staircase (mm)

// [0 : step_rise : (step_count-1)*step_rise] steps by step_rise each iteration
for (z = [0 : step_rise : (step_count - 1) * step_rise]) {
    step_index = z / step_rise;           // which step number we are on
    translate([0, step_index * step_run, 0])
        cube([step_width, step_run, z + step_rise]);
}
```

#### recursive module

A branching fractal tree built with a recursive module. Each call draws one branch segment, then calls itself twice at a smaller scale and angle. The 2D tree outline is extruded to a thin slab so it is printable.

```openscad
// recursive module

/* Branching tree - demonstrating a recursive module.
   branch() calls itself twice with smaller dimensions and opposite angles,
   stopping when the branch becomes shorter than min_len.
   A 2D shape is built and then extruded once at the top level. */

trunk_len   = 30;    // length of the main trunk (mm)
trunk_w     = 5;     // width of the main trunk (mm)
depth       = 3;     // extrude depth for the printable plaque (mm)
split_angle = 28;    // angle each child branch fans outward (degrees)
shrink      = 0.65;  // child branch length as a fraction of parent length
min_len     = 4;     // stop recursing when branch length drops below this (mm)

// 2D recursive branch - uses square() so it works inside linear_extrude
module branch2d(len, w) {
    square([w, len]);
    if (len * shrink > min_len) {
        child_len = len * shrink;
        child_w   = max(1, w * shrink);
        // left child
        translate([w / 2, len, 0])
            rotate(split_angle)
                translate([-child_w / 2, 0, 0])
                    branch2d(child_len, child_w);
        // right child
        translate([w / 2, len, 0])
            rotate(-split_angle)
                translate([-child_w / 2, 0, 0])
                    branch2d(child_len, child_w);
    }
}

linear_extrude(height = depth)
    translate([-trunk_w / 2, 0])
        branch2d(trunk_len, trunk_w);
```

#### #, %, ! modifiers note

Demonstrates a solid composed part (a box with a lid) alongside a semi-transparent reference bounding volume drawn with the % modifier. The final render is always the solid geometry only.

```openscad
// #, %, ! modifiers note

/* Modifier demo - % shows a ghost reference box; the real model renders solid.
   OpenSCAD modifiers:
     %  background (grey ghost, not in final render or export)
     #  highlight   (shown in pink, IS included in geometry)
     !  root        (render ONLY this child, ignoring everything else)
     *  disable     (ignore this child entirely)
   Only % is used here so the part stays clean and printable.
   All dimensions in mm. */
box_w   = 50;   // main box width
box_d   = 35;   // main box depth
box_h   = 30;   // main box height
wall    = 2.5;  // wall thickness
lid_h   = 8;    // lid height
gap     = 0.3;  // clearance between box lip and lid
$fn = 32;

// --- Hollow box body ---
module box_body() {
    difference() {
        cube([box_w, box_d, box_h]);
        translate([wall, wall, wall])
            cube([box_w - 2*wall, box_d - 2*wall, box_h]);  // open top
    }
}

// --- Snap-fit lid ---
module lid() {
    difference() {
        cube([box_w, box_d, lid_h]);
        // Inner pocket that drops over the box lip
        translate([wall + gap, wall + gap, wall])
            cube([box_w - 2*(wall+gap), box_d - 2*(wall+gap), lid_h]);
    }
}

// Real geometry - box body and lid sitting side by side
box_body();
translate([box_w + 10, 0, 0]) lid();

// % ghost - a transparent bounding envelope showing the assembled footprint.
// This is NOT exported or printed; it is a visual reference only.
%translate([-2, -2, 0])
    cube([box_w * 2 + 10 + 4, box_d + 4, box_h + 4]);
```

#### rotate()

Shows three elongated bars, each tipped 45 degrees around a different axis using rotate(), so the effect of rotating around X, Y, and Z is immediately visible.

```openscad
// rotate()

/* rotate([x_deg, y_deg, z_deg]) spins its child around the world axes.
   Each bar below is the same shape but rotated about one axis only.
   Positive angles follow the right-hand rule. */

bar_w  = 6;    // bar cross-section (mm)
bar_l  = 40;   // bar length (mm)
angle  = 45;   // rotation angle to apply (degrees)
spread = 60;   // spacing between the three examples

// Unrotated reference bar (grey in color-aware viewers)
color("silver")
    cube([bar_w, bar_w, bar_l]);

// Rotated 45 degrees around X - bar tilts in the Y-Z plane
translate([spread, 0, 0])
    rotate([angle, 0, 0])
        cube([bar_w, bar_w, bar_l]);

// Rotated 45 degrees around Y - bar tilts in the X-Z plane
translate([spread * 2, 0, 0])
    rotate([0, angle, 0])
        cube([bar_w, bar_w, bar_l]);

// Rotated 45 degrees around Z - bar tips sideways in the X-Y plane
translate([spread * 3, 0, 0])
    rotate([0, 0, angle])
        cube([bar_w, bar_w, bar_l]);
```

#### rotate_extrude()

Shows how rotate_extrude() sweeps a 2D profile all the way around the Z axis to make a solid of revolution - here a chunky ring with a D-shaped cross-section.

```openscad
// rotate_extrude()

/* rotate_extrude() - revolves a 2D profile 360 degrees around the Z axis. */
ring_r    = 24;  // distance from Z axis to centre of the cross-section
cs_width  = 10;  // cross-section width (radial)
cs_height = 14;  // cross-section height (axial)
corner_r  = 3;   // rounding on the cross-section corners
$fn = 64;

// A rounded rectangle cross-section sitting to the right of the Z axis.
// Important: every point of the profile must have x >= 0 for rotate_extrude.
module cross_section() {
    translate([ring_r - cs_width / 2, -cs_height / 2, 0])
        offset(r = corner_r) offset(r = -corner_r)
            square([cs_width, cs_height]);
}

// rotate_extrude sweeps the profile all the way around - the key call.
rotate_extrude($fn = 64)
    cross_section();
```

#### rotate_extrude(angle=)

Shows how the angle= parameter stops a rotate_extrude() part-way around, creating a C-shaped arc solid. Adjust the sweep angle and cross-section to explore partial revolves.

```openscad
// rotate_extrude(angle=)

/* rotate_extrude(angle=) - partial revolution; here a 270-degree C-channel arc. */
sweep_angle = 270;  // degrees of revolution (try 180 for a half-pipe, 360 for a full ring)
ring_r      = 22;   // distance from Z axis to cross-section centre
cs_w        = 9;    // cross-section width (radial direction)
cs_h        = 12;   // cross-section height (axial direction)
$fn = 64;

// L-shaped channel cross-section - gives the arc structural interest.
module channel_profile() {
    wall = 2.0; // channel wall thickness
    translate([ring_r - cs_w / 2, -cs_h / 2, 0]) {
        // outer rectangle
        square([cs_w, cs_h]);
        // subtract the inner hollow to make a U-channel
        translate([wall, wall, 0])
            square([cs_w - wall, cs_h - wall]);
    }
}

// Partial rotate_extrude - angle= is the feature being demonstrated.
rotate_extrude(angle = sweep_angle, $fn = 64)
    difference() {
        translate([ring_r - cs_w / 2, -cs_h / 2, 0]) square([cs_w, cs_h]);
        translate([ring_r - cs_w / 2 + 2, -cs_h / 2 + 2, 0]) square([cs_w - 2, cs_h - 2]);
    }
```

#### scale() and resize()

Compares scale() and resize() applied to the same sphere. scale() multiplies the current size by a factor while resize() sets an exact target size in millimetres.

```openscad
// scale() and resize()

/* scale() multiplies coordinates by a factor vector [sx, sy, sz].
   resize() sets the bounding box to an absolute [x, y, z] size.
   Both can stretch non-uniformly - very useful for turning a sphere
   into an ellipsoid without defining new geometry. */

base_r  = 12;   // radius of the original sphere (mm)
$fn     = 40;
spacing = 50;   // gap between examples

// Original sphere for reference
color("silver")
    sphere(r = base_r);

// scale() - multiply each axis: 1x on X, 2x on Y, 0.5x on Z
translate([spacing, 0, 0])
    color("steelblue")
        scale([1, 2, 0.5])
            sphere(r = base_r);

// resize() - force the bounding box to exactly 50 x 20 x 30 mm
translate([spacing * 2.5, 0, 0])
    color("tomato")
        resize([50, 20, 30])
            sphere(r = base_r);
```

#### sphere()

Places two spheres of the same radius side by side to show how $fn controls smoothness - the left sphere is coarse and blocky while the right one is nearly smooth.

```openscad
// sphere()

/* sphere() - facet count comparison.
   $fn sets the number of longitude AND latitude segments.
   Low $fn = fast but faceted; high $fn = smooth but slower to render. */

r   = 15;    // sphere radius (mm)
gap = 10;    // gap between the two spheres

// Coarse sphere - clearly faceted, renders instantly
translate([-r - gap / 2, 0, r])
    sphere(r = r, $fn = 8);

// Smooth sphere - 48 segments, much rounder
translate([ r + gap / 2, 0, r])
    sphere(r = r, $fn = 48);
```

#### text() 2D

Shows how text() creates a 2D letter outline that linear_extrude() lifts into raised 3D lettering on a name-badge plate. Change the label, font size, and thickness to customise.

```openscad
// text() 2D

/* text() as 2D letters raised above a badge plate. */
label      = "OpenSCAD";  // the text string (change me!)
font_size  = 11;           // character height in mm
raise      =  3;           // how tall the raised letters are above the plate
plate_w    = 90;           // badge plate width
plate_d    = 28;           // badge plate depth
plate_h    =  3;           // badge plate base thickness

// Base plate - ensures non-empty geometry even if the font system is unavailable.
cube([plate_w, plate_d, plate_h], center = true);

// Raised lettering on top of the plate.
// text() defines the 2D outline; linear_extrude lifts it above the plate surface.
translate([0, 0, plate_h / 2])
    linear_extrude(height = raise)
        text(
            label,
            size   = font_size,
            halign = "center",
            valign = "center"
        );
```

#### engraving text

Shows how to engrave text into a flat plate using difference() - the text shape is subtracted from the plate solid. Tune the plate size, engraving depth, and label.

```openscad
// engraving text

/* Engraving text into a plate with difference(). */
plate_w   = 80;   // plate width
plate_d   = 40;   // plate depth (Y)
plate_h   =  6;   // plate total thickness
engrave_d =  1.5; // how deep the text is cut in
font_size = 10;   // character height
label     = "MADE WITH";  // first line of text
label2    = "OPENSCAD";   // second line of text
font_name = "Liberation Sans:style=Bold";
$fn = 32;

difference() {
    // The solid plate
    cube([plate_w, plate_d, plate_h], center = true);

    // Engrave first line - translate up from plate surface by (plate_h/2 - engrave_d)
    translate([0, 5, plate_h / 2 - engrave_d])
        linear_extrude(height = engrave_d + 0.1)   // +0.1 ensures clean boolean cut
            text(label,  size = font_size, font = font_name, halign = "center", valign = "center");

    // Engrave second line below the first
    translate([0, -8, plate_h / 2 - engrave_d])
        linear_extrude(height = engrave_d + 0.1)
            text(label2, size = font_size, font = font_name, halign = "center", valign = "center");
}
```

#### translate()

Places four identical cubes at different positions using translate() to show how moving along X, Y, and Z works independently and in combination.

```openscad
// translate()

/* translate([x, y, z]) shifts its child geometry in space.
   Here the same 15 mm cube appears at four different locations
   so you can see how each axis affects placement. */

s   = 15;   // cube side (mm)
gap = 8;    // extra space between cubes

// Reference cube at the origin
cube([s, s, s]);

// Shifted along +X
translate([s + gap, 0, 0])
    cube([s, s, s]);

// Shifted along +Y
translate([0, s + gap, 0])
    cube([s, s, s]);

// Shifted along +Z
translate([0, 0, s + gap])
    cube([s, s, s]);
```

#### union()

Shows how union() merges two overlapping solids into a single seamless body. Adjust the box size, cylinder radius, and overlap to explore how the shapes combine.

```openscad
// union()

/* union() example - two overlapping shapes become one solid. All dimensions in mm. */
box_w   = 40;   // box width (X)
box_d   = 30;   // box depth (Y)
box_h   = 20;   // box height (Z)
cyl_r   = 12;   // cylinder radius
cyl_h   = 35;   // cylinder height
overlap = 10;   // how far the cylinder overlaps into the box
$fn = 48;

// union() is actually the default when you list children side by side,
// but writing it explicitly makes the intent crystal-clear.
union() {
    // A rounded-end horizontal bar
    cube([box_w, box_d, box_h]);

    // A vertical cylinder that overlaps the box - together they become one solid
    translate([box_w - overlap, box_d / 2, 0])
        cylinder(r = cyl_r, h = cyl_h);
}
```

#### OpenBeam 1515 corner frame

An L-shaped corner made from two lengths of OpenBeam 15x15 t-slot extrusion meeting at a right angle, the kind of joint used to build the edges of a printer or enclosure frame. Set the length of each leg independently.

```openscad
// OpenBeam 1515 corner frame
//
// Source: openscad-extrusions — MIT
// Generated from the library above, used under its license.

use <openscad-extrusions/OpenBeam/1515.scad>

leg_a = 90;   // horizontal leg length in mm
leg_b = 70;   // vertical leg length in mm
$fn = 48;

// Horizontal rail lying along +X.
rotate([0, 90, 0])
    OpenBeam_1515(leg_a);

// Vertical rail rising in +Z, butted to the end of the horizontal rail.
translate([leg_a, 0, 0])
    OpenBeam_1515(leg_b);
```

#### OpenBeam 1515 cube frame

A full open box frame built from twelve lengths of OpenBeam 15x15 t-slot extrusion, the skeleton of an enclosure, light box, or display cube. Set the inner edge size and all twelve rails are placed automatically.

```openscad
// OpenBeam 1515 cube frame
//
// Source: openscad-extrusions — MIT
// Generated from the library above, used under its license.

use <openscad-extrusions/OpenBeam/1515.scad>

edge = 100;   // outer cube edge length in mm
$fn = 32;

bar = 15;             // 1515 profile width in mm
span = edge - 2 * bar; // clear rail length between corner posts
ov = 0.6;             // overlap so rails fuse into the posts

// Four vertical posts at the corners.
for (x = [0, span + bar], y = [0, span + bar])
    translate([x, y, 0])
        OpenBeam_1515(edge);

// Bottom and top horizontal rails along X (overlapped into both posts).
for (z = [0, span + bar], y = [0, span + bar])
    translate([bar - ov, y, z + bar / 2])
        rotate([0, 90, 0])
            OpenBeam_1515(span + 2 * ov);

// Bottom and top horizontal rails along Y (overlapped into both posts).
for (z = [0, span + bar], x = [0, span + bar])
    translate([x, bar - ov, z + bar / 2])
        rotate([-90, 0, 0])
            OpenBeam_1515(span + 2 * ov);
```

#### OpenBeam 1515 extrusion post

A single length of OpenBeam 15x15 aluminium-style t-slot extrusion with four open channels and a center bore, ready to use as a frame rail or leg. Adjust the length to suit your build.

```openscad
// OpenBeam 1515 extrusion post
//
// Source: openscad-extrusions — MIT
// Generated from the library above, used under its license.

use <openscad-extrusions/OpenBeam/1515.scad>

length = 80;   // post length in mm
$fn = 48;

OpenBeam_1515(length);
```

#### OpenBeam 1515 parallel rail bed

A row of evenly spaced parallel OpenBeam 15x15 t-slot rails, like the gantry or bed support of a 3D printer or CNC. Choose how many rails, how far apart they sit, and how long each one runs.

```openscad
// OpenBeam 1515 parallel rail bed
//
// Source: openscad-extrusions — MIT
// Generated from the library above, used under its license.

use <openscad-extrusions/OpenBeam/1515.scad>

rail_count = 4;    // number of parallel rails
spacing    = 40;   // center-to-center spacing in mm
length     = 120;  // rail length in mm
$fn = 40;

for (i = [0 : rail_count - 1])
    translate([0, i * spacing, 0])
        rotate([0, 90, 0])
            OpenBeam_1515(length);
```

#### $children

Number of module-instantiation children passed to the current module.

```openscad
module row() { for (i = [0:$children-1]) translate([i*10, 0, 0]) children(i); } row() { cube(3); sphere(2); } // $children = 2
```

#### $fa

Minimum facet angle in degrees (default 12).

```openscad
$fa = 5; sphere(5); // min facet angle in degrees (smaller = smoother)
```

#### $fn

Fixed facet count for curved surfaces (overrides $fa/$fs when > 0).

```openscad
$fn = 64; sphere(5); // global facet count for the smooth sphere
```

#### $fs

Minimum facet size in mm (default 2).

```openscad
$fs = 0.5; sphere(5); // min facet size in mm (smaller = smoother)
```

#### $preview

True during F5 preview, false during an F6 render.

```openscad
color($preview ? "red" : "green") cube(5); // red in F5 preview, green in F6 render
```

#### $t

Animation time, 0–1, advanced by the animation FPS/steps.

```openscad
rotate([0, 0, 360 * $t]) cube(5); // $t animates 0 to 1 over the timeline
```

#### $vpd

Viewport camera distance.

```openscad
cube(5); echo("camera distance:", $vpd); // $vpd = viewport camera distance
```

#### $vpf

Viewport field of view in degrees.

```openscad
cube(5); echo("field of view:", $vpf); // $vpf = viewport FOV (deg)
```

#### $vpr

Viewport rotation `[x, y, z]` in degrees.

```openscad
cube(5); echo("viewport rotation:", $vpr); // $vpr = [x,y,z] deg
```

#### $vpt

Viewport translation (camera target) `[x, y, z]`.

```openscad
cube(5); echo("viewport target:", $vpt); // $vpt = camera target [x,y,z]
```

#### abs(x)

Absolute value of `x`.

```openscad
cube(abs(-7.5)); // abs(-7.5) = 7.5 -> a 7.5mm cube
```

#### acos(x)

Arc cosine, in degrees.

```openscad
rotate([0, 0, acos(0.5)]) cube([20, 3, 3]); // acos(0.5) = 60 deg
```

#### asin(x)

Arc sine, in degrees.

```openscad
rotate([0, 0, asin(0.5)]) cube([20, 3, 3]); // asin(0.5) = 30 deg
```

#### assert(condition, message = undef)

Abort with `message` when `condition` is false (statement or expression form).

```openscad
assert(1 + 1 == 2, "math is broken"); cube(5); // assert passes -> cube renders
```

#### atan(x)

Arc tangent, in degrees.

```openscad
rotate([0, 0, atan(1)]) cube([20, 3, 3]); // atan(1) = 45 deg
```

#### atan2(y, x)

Two-argument arc tangent of `y/x`, in degrees, quadrant-aware.

```openscad
rotate([0, 0, atan2(1, -1)]) cube([20, 3, 3]); // atan2(1,-1) = 135 deg (quadrant-aware)
```

#### ceil(x)

Smallest integer not less than `x`.

```openscad
cube(ceil(7.2)); // ceil(7.2) = 8
```

#### children(index = undef)

Instantiate the module-instantiation children — all, an `index`, or a range.

```openscad
module move_up(z) { translate([0, 0, z]) children(); } move_up(10) cube(3); // children() = the passed child
```

#### chr(code | vector)

Unicode code point(s) → string.

```openscad
c = chr(65); echo("chr(65) =", c); cube(ord(c) / 8); // chr(65) -> "A"; ord -> 65 -> ~8mm
```

#### circle(r | d, $fn)

2D circle by radius `r` or diameter `d`.

```openscad
linear_extrude(5) circle(d = 12, $fn = 64); // 2D circle extruded to a 3D disc
```

#### color(c, alpha = 1)

Tint children. `c` is `[r, g, b]`/`[r, g, b, a]` (0–1) or a CSS color name string.

```openscad
color("steelblue", alpha = 0.5) cube(10); // tint + 50% transparency
```

#### concat(a, b, ...)

Concatenate vectors/values into a single flat vector.

```openscad
for (s = concat([3, 6], [9])) translate([s, 0, 0]) cube(2); // concat -> [3,6,9]
```

#### cos(deg)

Cosine of an angle in degrees.

```openscad
cube([10, 10, 20 * cos(60)]); // cos(60) = 0.5 (degrees) -> height 10
```

#### cross(a, b)

Cross product of two 3D (or 2D scalar) vectors.

```openscad
c = cross([1, 0, 0], [0, 1, 0]); translate(10 * c) cube(3); // cross = [0,0,1] -> moved up 10
```

#### cube(size, center = false)

Axis-aligned box. `size` is a scalar or `[x, y, z]`; `center` toggles origin-centered.

```openscad
cube([10, 20, 15], center = true); // centered box, x/y/z sizes
```

#### cylinder(h, r | d, center = false)

Cylinder/cone. Use `r`/`d` for uniform radius, or `r1`+`r2` (`d1`+`d2`) for a cone.

```openscad
cylinder(h = 20, r1 = 8, r2 = 0, $fn = 48); // cone: base radius 8 up to a point
```

#### difference()

Subtract all later children from the first child.

```openscad
difference() { cube(10, center = true); sphere(6); } // box minus sphere
```

#### echo(a, b, ...)

Print the arguments to the console (also usable as a module statement).

```openscad
echo("debug:", 1 + 2); cube(5); // echo prints "debug:, 3"; cube gives geometry
```

#### exp(x)

e raised to the power `x`.

```openscad
cube(exp(2)); // exp(2) ~ 7.39mm
```

#### floor(x)

Largest integer not greater than `x`.

```openscad
cube(floor(7.8)); // floor(7.8) = 7
```

#### hull()

Convex hull of all children (2D or 3D).

```openscad
hull() { cylinder(h = 2, r = 2); translate([20, 0, 0]) cylinder(h = 2, r = 5); } // 3D convex hull = tapered slab
```

#### import(file, convexity = 1)

Import an external STL/OFF/DXF/SVG/AMF file as geometry.

```openscad
cube([10, 10, 2]); // import("part.stl", convexity = 5) loads external STL/OFF/SVG geometry (needs a real file; placeholder cube shown)
```

#### intersection()

Keep only the volume common to all children.

```openscad
intersection() { cube(10, center = true); sphere(6.5); } // keep only the overlap
```

#### is_bool(value)

True when `value` is a boolean.

```openscad
if (is_bool(false)) cube(8); // is_bool = true
```

#### is_function(value)

True when `value` is a function literal (2019.05+).

```openscad
f = function(x) x * 2; if (is_function(f)) cube(8); // is_function = true (2019.05+)
```

#### is_list(value)

True when `value` is a vector/list.

```openscad
if (is_list([1, 2])) cube(8); // is_list = true
```

#### is_num(value)

True when `value` is a number.

```openscad
if (is_num(3.14)) sphere(5); // is_num = true
```

#### is_string(value)

True when `value` is a string.

```openscad
if (is_string("hi")) cube(8); // is_string = true
```

#### is_undef(value)

True when `value` is `undef`.

```openscad
if (is_undef(undef)) cube(8); // is_undef(undef) = true -> cube renders
```

#### len(value)

Length of a vector or string.

```openscad
cube(len([10, 20, 30]) * 4); // len = 3 -> 12mm
```

#### linear_extrude(height, center = false, twist = 0, scale = 1)

Extrude a 2D child along Z, optionally twisting and scaling.

```openscad
linear_extrude(height = 20, twist = 360) square(5); // 2D square -> twisted 3D prism
```

#### ln(x)

Natural logarithm of `x`.

```openscad
cube(ln(20.0855)); // ln(20.09) ~ 3
```

#### log(x)

Base-10 logarithm of `x`.

```openscad
cube(log(1000) * 3); // log(1000) = 3 -> 9mm
```

#### lookup(key, table)

Linear-interpolated lookup of `key` in a `[[k, v], ...]` table.

```openscad
cube(lookup(2.5, [[0, 4], [5, 14]])); // linear-interpolated lookup -> 9
```

#### max(a, b, ...) | max(vector)

Maximum of the arguments or of a single vector argument.

```openscad
cube(max([4, 9, 6])); // max = 9 (from a vector)
```

#### min(a, b, ...) | min(vector)

Minimum of the arguments or of a single vector argument.

```openscad
cube(min(12, 9, 15)); // min = 9
```

#### minkowski(convexity = 1)

Minkowski sum of the children — e.g. round edges by summing with a sphere.

```openscad
minkowski() { cube([10, 10, 2]); cylinder(r = 2, h = 1); } // round the box edges
```

#### mirror(v)

Mirror children across the plane through the origin with normal `v`.

```openscad
mirror([1, 0, 0]) translate([5, 0, 0]) cube(3); // reflect across the YZ plane
```

#### multmatrix(m)

Apply a 4×3 or 4×4 affine transform matrix `m` to children.

```openscad
multmatrix([[1,0,0,5],[0,1,0,0],[0,0,1,0],[0,0,0,1]]) cube(8); // 4x4 affine: translate +5 in X
```

#### norm(v)

Euclidean length (magnitude) of vector `v`.

```openscad
cube(norm([3, 4])); // norm = 5 (vector length)
```

#### offset(r | delta, chamfer = false)

Grow/shrink a 2D child by radius `r` (rounded) or `delta` (sharp/chamfered).

```openscad
linear_extrude(3) offset(r = 2) square(10); // grow the 2D outline by 2 (rounded), then extrude
```

#### ord(char)

First character of a string → its Unicode code point.

```openscad
cube(ord("A") / 8); // ord("A") = 65 -> ~8mm
```

#### parent_module(index)

Name of the module `index` levels up the instantiation stack.

```openscad
module brick() { cube(5); echo(parent_module(0)); } brick(); // echoes "brick" + renders a cube
```

#### polygon(points, paths = undef, convexity = 1)

2D polygon from a `points` list; optional `paths` define multiple/holed contours.

```openscad
linear_extrude(6) polygon(points = [[0,0],[10,0],[5,8]]); // 2D triangle extruded to a 3D prism
```

#### polyhedron(points, faces, convexity = 1)

Arbitrary 3D solid from a vertex list `points` and a `faces` index list.

```openscad
polyhedron(points = [[0,0,0],[10,0,0],[5,8,0],[5,4,9]], faces = [[0,1,2],[0,1,3],[1,2,3],[2,0,3]]); // a tetrahedron
```

#### pow(base, exp)

`base` raised to the power `exp`.

```openscad
cube(pow(2, 5) / 4); // pow(2,5) = 32 -> 8mm
```

#### projection(cut = false)

Project a 3D child to 2D — the outline (`cut = false`) or a Z=0 slice (`cut = true`).

```openscad
linear_extrude(2) projection(cut = true) translate([0, 0, -2]) sphere(5); // slice a 3D sphere at Z=0 to a 2D outline, then re-extrude
```

#### rands(min, max, count, seed = undef)

Vector of `count` random numbers in `[min, max)`; `seed` makes it deterministic.

```openscad
for (r = rands(2, 8, 4, 42)) translate([r * 3, 0, 0]) cube(r); // 4 seeded randoms in [2,8)
```

#### render(convexity = 1)

Force a full CGAL render of children (resolves preview artifacts at a cost).

```openscad
render(convexity = 3) difference() { cube(10); sphere(6); } // force a CGAL render of the result
```

#### resize(newsize, auto = false)

Resize children to an absolute `newsize` bounding box. A 0 axis is unchanged (or auto-scaled).

```openscad
resize([30, 10, 0]) sphere(5); // fit into a 30x10 box, Z unchanged
```

#### rotate(a, v = undef)

Rotate children. `a` is per-axis degrees `[x, y, z]`, or an angle about axis `v`.

```openscad
rotate([0, 0, 45]) cube([10, 2, 2]); // rotate 45 deg about Z
```

#### rotate_extrude(angle = 360, $fn)

Revolve a 2D child around the Z axis (the child must lie in +X).

```openscad
rotate_extrude(angle = 270, $fn = 64) translate([10, 0]) circle(2); // revolve into a torus arc
```

#### round(x)

Round `x` to the nearest integer.

```openscad
cube(round(7.5)); // round(7.5) = 8
```

#### scale(v)

Scale children by `v` (scalar or `[x, y, z]`).

```openscad
scale([2, 1, 0.5]) sphere(5); // non-uniform scale (ellipsoid)
```

#### search(match, list, num_returns_per_match = 1)

Find matches of `match` within a string/vector; returns index(es).

```openscad
idx = search("b", "abc"); cube((idx[0] + 1) * 4); // search -> [1] -> 8mm
```

#### sign(x)

Sign of `x`: -1, 0, or 1.

```openscad
cube(10 + 5 * sign(-42)); // sign(-42) = -1 -> a 5mm cube
```

#### sin(deg)

Sine of an angle in degrees.

```openscad
cube([10, 10, 20 * sin(30)]); // sin(30) = 0.5 (DEGREES) -> height 10
```

#### sphere(r | d, $fn, $fa, $fs)

Sphere by radius `r` or diameter `d`. Facet count controlled by `$fn`/`$fa`/`$fs`.

```openscad
sphere(r = 5, $fn = 64); // smooth sphere of radius 5
```

#### sqrt(x)

Square root of `x`.

```openscad
cube(sqrt(144)); // sqrt(144) = 12
```

#### square(size, center = false)

2D rectangle. `size` is a scalar or `[x, y]`.

```openscad
linear_extrude(4) square([20, 10], center = true); // 2D rectangle extruded to a 3D slab
```

#### str(a, b, ...)

Concatenate the arguments into a single string.

```openscad
s = str("v=", 42); echo(s); cube(len(s)); // str -> "v=42"; len 4 -> a 4mm cube
```

#### surface(file, center = false, invert = false)

Heightmap surface from a text/image `file`.

```openscad
cube([10, 10, 3]); // surface(file = "heightmap.dat", center = true) builds a heightmap from a data file (placeholder cube shown)
```

#### tan(deg)

Tangent of an angle in degrees.

```openscad
cube([10, 10, 10 * tan(45)]); // tan(45) = 1 (degrees) -> height 10
```

#### text(text, size = 10, font, halign, valign)

2D text outline. Combine with `linear_extrude` for 3D text.

```openscad
cube([14, 8, 2]); // text("Hi", size = 8) outlines 2D glyphs (font-dependent; wrap in linear_extrude for 3D letters)
```

#### translate(v)

Move children by the vector `v = [x, y, z]`.

```openscad
translate([10, 0, 5]) cube(4); // move children by [x, y, z]
```

#### union()

Combine all children into one solid (the default for a group).

```openscad
union() { cube(10); translate([8, 0, 0]) sphere(6); } // merge into one solid
```

#### version()

OpenSCAD version as a `[year, month, day]` vector.

```openscad
v = version(); cube(v[0] / 200); // version = [2021,1,0] -> ~10mm
```

#### version_num()

OpenSCAD version as a single number (e.g. 20210100).

```openscad
cube(version_num() / 2021010); // version_num = 20210100 -> ~10mm
```

#### Helical spur gear

An involute gear whose teeth twist along the thickness for quieter, smoother running than a straight-cut gear. Increase the twist for a steeper helix angle.

```openscad
// Helical spur gear
//
// Source: pd-gears — Unlicense
// Generated from the library above, used under its license.

use <pd-gears/pd-gears.scad>

mm_per_tooth  = 3;   // circular pitch in mm
teeth         = 19;  // number of teeth
thickness     = 10;  // gear thickness in mm (helix spans this)
hole_diameter = 5;   // shaft bore diameter in mm
twist_deg     = 30;  // total tooth twist top-to-bottom (helix amount)

gear(mm_per_tooth = mm_per_tooth,
     number_of_teeth = teeth,
     thickness = thickness,
     hole_diameter = hole_diameter,
     twist = twist_deg,
     pressure_angle = 20,
     center = true,
     $fn = 48);
```

#### Gear with reinforced hub

An involute spur gear with a raised cylindrical hub around the bore that adds grip length for a set screw or press-fit shaft. Tune the hub height and diameter for your shaft.

```openscad
// Gear with reinforced hub
//
// Source: pd-gears — Unlicense
// Generated from the library above, used under its license.

use <pd-gears/pd-gears.scad>

mm_per_tooth  = 3;   // circular pitch in mm
teeth         = 21;  // number of teeth
thickness     = 5;   // gear disc thickness in mm
hole_diameter = 5;   // shaft bore diameter in mm
hub_diameter  = 14;  // outer diameter of the reinforcing hub in mm
hub_height    = 7;   // hub length above the gear face in mm

difference() {
    union() {
        // Gear disc sitting on the build plate.
        gear(mm_per_tooth, teeth, thickness, hole_diameter,
             pressure_angle = 20, $fn = 48);
        // Reinforcing hub rising from the top face.
        translate([0, 0, thickness])
            cylinder(h = hub_height, d = hub_diameter, $fn = 48);
    }
    // Bore drilled all the way through gear + hub.
    translate([0, 0, -0.1])
        cylinder(h = thickness + hub_height + 0.2, d = hole_diameter, $fn = 48);
}
```

#### Meshing gear pair

Two involute spur gears spaced so their pitch circles touch and the teeth mesh, ready to print as a working reduction stage. Adjust the pinion and wheel tooth counts to set the gear ratio.

```openscad
// Meshing gear pair
//
// Source: pd-gears — Unlicense
// Generated from the library above, used under its license.

use <pd-gears/pd-gears.scad>

mm_per_tooth = 4;   // shared circular pitch (must match for meshing)
pinion_teeth = 11;  // small driving gear
wheel_teeth  = 23;  // large driven gear
thickness    = 6;   // gear thickness in mm
bore         = 5;   // shaft bore diameter in mm

// Center-to-center distance = sum of the two pitch radii so teeth mesh.
spacing = pitch_radius(mm_per_tooth, pinion_teeth)
        + pitch_radius(mm_per_tooth, wheel_teeth);

gear(mm_per_tooth, pinion_teeth, thickness, bore, pressure_angle = 20, $fn = 48);

translate([spacing, 0, 0])
    gear(mm_per_tooth, wheel_teeth, thickness, bore, pressure_angle = 20, $fn = 48);
```

#### Rack and pinion set

A toothed rack bar paired with a round pinion gear that rolls along it, converting rotation into straight-line travel. Print both to build a linear actuator or sliding mechanism.

```openscad
// Rack and pinion set
//
// Source: pd-gears — Unlicense
// Generated from the library above, used under its license.

use <pd-gears/pd-gears.scad>

mm_per_tooth = 5;   // shared circular pitch (must match for meshing)
rack_teeth   = 10;  // teeth along the rack bar
pinion_teeth = 12;  // teeth on the round pinion
thickness    = 6;   // part thickness in mm
rack_height  = 14;  // rack body height (tooth tip to far edge) in mm
bore         = 5;   // pinion shaft bore in mm

// Rack pitch line runs along X; place the pinion so its pitch circle sits on it.
rack(mm_per_tooth, rack_teeth, thickness, rack_height, pressure_angle = 20);

translate([rack_teeth * mm_per_tooth / 2,
           pitch_radius(mm_per_tooth, pinion_teeth), 0])
    gear(mm_per_tooth, pinion_teeth, thickness, bore, pressure_angle = 20, $fn = 48);
```

#### Involute spur gear

A single printable involute spur gear with a centered shaft bore. Tune the tooth count, tooth pitch, thickness, and hole diameter to fit your drivetrain.

```openscad
// Involute spur gear
//
// Source: pd-gears — Unlicense
// Generated from the library above, used under its license.

use <pd-gears/pd-gears.scad>

mm_per_tooth  = 3;   // circular pitch (arc length per tooth, mm)
teeth         = 17;  // number of teeth around the gear
thickness     = 6;   // gear thickness in mm
hole_diameter = 5;   // center shaft bore diameter in mm

gear(mm_per_tooth = mm_per_tooth,
     number_of_teeth = teeth,
     thickness = thickness,
     hole_diameter = hole_diameter,
     pressure_angle = 20,
     center = true,
     $fn = 48);
```

#### Arduino UNO Case

A custom-fit Arduino UNO R3 enclosure base with matched mounting standoffs and USB and barrel-jack cutouts on the wall.

```openscad
// Arduino UNO Case
//
// Source: project-boxy — MIT
// Generated from the library above, used under its license.

include <project-boxy/project_box.scad>

// Box base only, sized to host an Arduino UNO R3.
BOX = true;
LID = false;

EXTERIOR_WIDTH = 80;
EXTERIOR_HEIGHT = 70;
EXTERIOR_DEPTH = 30;
THICKNESS = 2;

// Use the built-in Arduino footprint: drops the four board standoffs and
// cuts the USB + barrel-jack openings in the wall it faces.
ARDUINO_UNO = true;
ARDUINO_UNO_ROTATION = 90;

// Footprint provides the standoffs, so disable the generic grid.
BOARD_STANDOFFS = false;
ROUND_HOLES = [];
```

#### Electronics Enclosure Box

A parametric 3D-printable electronics project box base with PCB standoffs and lid mounting posts.

```openscad
// Electronics Enclosure Box
//
// Source: project-boxy — MIT
// Generated from the library above, used under its license.

include <project-boxy/project_box.scad>

// Render only the box base (the lid is a separate part).
BOX = true;
LID = false;

// Compact enclosure footprint.
EXTERIOR_WIDTH = 70;
EXTERIOR_HEIGHT = 50;
EXTERIOR_DEPTH = 28;
THICKNESS = 2;

// PCB mounting standoffs inside the floor.
BOARD_STANDOFFS = true;
BOARD_STANDOFF_WIDTH = 30;
BOARD_STANDOFF_HEIGHT = 20;

// No side panel cutouts on this base.
ROUND_HOLES = [];
ARDUINO_UNO = false;
```

#### Snap-On Enclosure Lid

A parametric 3D-printable enclosure lid with an alignment lip and countersunk screw holes that mates to the matching box.

```openscad
// Snap-On Enclosure Lid
//
// Source: project-boxy — MIT
// Generated from the library above, used under its license.

include <project-boxy/project_box.scad>

// Render only the lid (matches the 70x50 enclosure box).
BOX = false;
LID = true;

EXTERIOR_WIDTH = 70;
EXTERIOR_HEIGHT = 50;
EXTERIOR_DEPTH = 28;
THICKNESS = 2;

// Lid features.
LID_THICKNESS = 3;
LID_LIP = true;
LID_LIP_WIDTH = 3;
LID_COUNTERSINK_DIA = 7;

ROUND_HOLES = [];
ARDUINO_UNO = false;
```

#### Panel-Mount Project Box

A tall electronics project box with a row of round panel-mount holes on the front face for connectors and switches.

```openscad
// Panel-Mount Project Box
//
// Source: project-boxy — MIT
// Generated from the library above, used under its license.

include <project-boxy/project_box.scad>

// Box base only.
BOX = true;
LID = false;

EXTERIOR_WIDTH = 90;
EXTERIOR_HEIGHT = 60;
EXTERIOR_DEPTH = 40;
THICKNESS = 2.5;

// PCB standoffs.
BOARD_STANDOFFS = true;
BOARD_STANDOFF_WIDTH = 40;
BOARD_STANDOFF_HEIGHT = 30;

// Three round panel-mount holes on the front (+y) face, 12mm diameter, spaced horizontally.
// Format: [Face, Diameter, Position_h, Position_v]  (Face 3 = front).
ROUND_HOLES = [[3, 12, -22, 5], [3, 12, 0, 5], [3, 12, 22, 5]];

ARDUINO_UNO = false;
```

#### Raspberry Pi B+ snap-on bumper

A low-profile protective bumper that clips around the edge of a Raspberry Pi 2/3/B+ board, with two external ear tabs drilled for wood screws so the whole board can be fastened flat to a panel or wall.

```openscad
// Raspberry Pi B+ snap-on bumper
//
// Source: OpenSCAD-RaspberryPi-Mounting-Library — MIT
// Generated from the library above, used under its license.

include <rpi-mounts/raspberry.scad>

$fn = 48;

bumper(
  boardType     = BPLUS,
  mountingHoles = true
);
```

#### Raspberry Pi B+ enclosure box

A walled case base for a Raspberry Pi 2/3/B+ with integrated tapped standoffs, side cut-outs for the USB, ethernet and power ports, and clip slots along the top edge that accept the matching snap-on lid.

```openscad
// Raspberry Pi B+ enclosure box
//
// Source: OpenSCAD-RaspberryPi-Mounting-Library — MIT
// Generated from the library above, used under its license.

include <rpi-mounts/raspberry.scad>

$fn = 32;

enclosure(
  boardType       = BPLUS,
  wall            = 2.5,
  offset          = 2,
  heightExtension = 6,
  cornerRadius    = 3,
  mountType       = TAPHOLE
);
```

#### Raspberry Pi B+ tapped standoffs

A set of four tapered standoffs positioned at the exact mounting-hole spacing of a Raspberry Pi 2/3/B+ board, each with a self-tapping screw hole so the board can be raised off a baseplate or screwed into a case floor.

```openscad
// Raspberry Pi B+ tapped standoffs
//
// Source: OpenSCAD-RaspberryPi-Mounting-Library — MIT
// Generated from the library above, used under its license.

include <rpi-mounts/raspberry.scad>

$fn = 48;

standoffs(
  boardType   = BPLUS,
  height      = 8,
  bottomRadius = 3.5,
  topRadius   = 2.6,
  mountType   = TAPHOLE
);
```

#### Raspberry Pi B+ enclosure lid

The snap-fit top cover for the Raspberry Pi 2/3/B+ case base, with a recessed inner lip and four flexible clips that lock it onto the enclosure clip slots.

```openscad
// Raspberry Pi B+ enclosure lid
//
// Source: OpenSCAD-RaspberryPi-Mounting-Library — MIT
// Generated from the library above, used under its license.

include <rpi-mounts/raspberry.scad>

$fn = 32;

enclosureLid(
  boardType    = BPLUS,
  wall         = 2.5,
  offset       = 2,
  cornerRadius = 3,
  ventHoles    = true
);
```

#### Raspberry Pi Zero tapped standoffs

Four short tapered standoffs spaced for the compact Raspberry Pi Zero mounting holes, each tapped for a self-threading screw so a Zero can be lifted off a baseplate or anchored inside a slim project box.

```openscad
// Raspberry Pi Zero tapped standoffs
//
// Source: OpenSCAD-RaspberryPi-Mounting-Library — MIT
// Generated from the library above, used under its license.

include <rpi-mounts/raspberry.scad>

$fn = 48;

standoffs(
  boardType    = ZERO,
  height       = 5,
  bottomRadius = 3.2,
  topRadius    = 2.4,
  mountType    = TAPHOLE
);
```

#### Snap-fit cable grommet

A round cable pass-through grommet with a cantilever snap-fit post on top, so it clips into a panel hole and stays put without screws. Tune the disc size, the cable bore, and the snap post to match your panel thickness and wire bundle.

```openscad
// Snap-fit cable grommet
//
// Source: snapfit-scad — MIT
// Generated from the library above, used under its license.

use <snapfit-scad/snapfit.scad>

disc_d   = 18;   // outer flange diameter
disc_h   = 3;    // flange thickness
bore_d   = 6;    // cable pass-through hole diameter
snap_dia = 5;    // snap-fit post diameter
snap_lip = 1;    // snap-fit lip (adds to diameter)
snap_col = 4;    // snap-fit column height
$fn = 96;

difference() {
  union() {
    translate([0, 0, disc_h / 2])
      cylinder(r1 = disc_d / 2, r2 = disc_d / 2.4, h = disc_h, center = true);
    translate([0, 0, disc_h])
      snapfit(dia = snap_dia, lip = snap_lip, col_h = snap_col,
              slot_ratio = 0.7, under_chamfer_angle = 45);
  }
  // central cable bore through everything
  translate([0, 0, -1])
    cylinder(r = bore_d / 2, h = disc_h + snap_col + snap_dia + snap_lip + 6);
}
```

#### Snap-together box clip coupler

A two-plate coupler that snaps shut on itself: the bottom plate carries a positive snap-fit post and the matching top plate has the negative socket bored into it, so the two halves click together and stay closed. Print both, set the same diameter, and press them home. Great for closing enclosures or joining panels without fasteners.

```openscad
// Snap-together box clip coupler
//
// Source: snapfit-scad — MIT
// Generated from the library above, used under its license.

use <snapfit-scad/snapfit.scad>

plate_d   = 14;   // plate diameter
plate_h   = 4;    // plate thickness
gap       = 8;    // spacing between the two printed halves
snap_dia  = 4;    // snap diameter
snap_lip  = 1;    // snap lip
snap_col  = 6;    // snap column height
neg_tol   = 0.5;  // clearance for the negative socket
$fn = 96;

// Bottom half: plate + positive snap post
union() {
  cylinder(r = plate_d / 2, h = plate_h, center = true);
  translate([0, 0, plate_h / 2])
    snapfit(dia = snap_dia, lip = snap_lip, col_h = snap_col,
            slot_ratio = 0.7, thickness = plate_h / 2);
}

// Top half: plate with negative socket, printed alongside
translate([plate_d + gap, 0, 0])
  difference() {
    cylinder(r = plate_d / 2, h = plate_h, center = true);
    translate([0, 0, -plate_h / 2 - 0.1])
      snapfit_neg(dia = snap_dia, lip = snap_lip, col_h = snap_col,
                  neg_tol = neg_tol, slot_ratio = 0.7, thickness = plate_h / 2);
  }
```

#### Snap-in PCB standoff foot

A tapered standoff foot with a chamfered snap-fit pin that pushes into a mounting hole on a circuit board or chassis and locks underneath. Print four to stand a board off its case. Tune the foot size and the pin diameter to match your board holes.

```openscad
// Snap-in PCB standoff foot
//
// Source: snapfit-scad — MIT
// Generated from the library above, used under its license.

use <snapfit-scad/snapfit.scad>

foot_d   = 10;   // base foot diameter
foot_h   = 4;    // standoff height
pin_dia  = 5;    // snap pin diameter (sized to the board hole)
pin_lip  = 1;    // snap lip
pin_col  = 2;    // pin column height
$fn = 96;

union() {
  // tapered standoff body
  translate([0, 0, foot_h / 2])
    cylinder(r1 = foot_d / 2, r2 = foot_d / 3, h = foot_h, center = true);
  // chamfered snap pin on top
  translate([0, 0, foot_h])
    snapfit(dia = pin_dia, lip = pin_lip, col_h = pin_col,
            col_tol = 0.1, slot_ratio = 0.8, under_chamfer_angle = 45);
}
```

#### Push-in snap rivet

A double-ended plastic push rivet: a snap-fit head on each end of a short waist, so it pushes through two aligned panel holes and locks both sheets together like a trim clip. Tune the head diameter, lip, and waist length for your combined panel thickness.

```openscad
// Push-in snap rivet
//
// Source: snapfit-scad — MIT
// Generated from the library above, used under its license.

use <snapfit-scad/snapfit.scad>

head_dia = 6;    // snap head diameter
head_lip = 1.2;  // snap lip
waist_h  = 5;    // central waist (panel grip) length
$fn = 96;

overlap = 0.6;   // weld overlap so the two halves fuse into one solid

union() {
  // bottom snap head pointing up (column slightly longer to overlap the join)
  snapfit(dia = head_dia, lip = head_lip, col_h = waist_h / 2 + overlap,
          col_tol = 0.1, slot_ratio = 0.7);

  // mirror a second snap head on top, sharing/overlapping the waist
  translate([0, 0, waist_h])
    mirror([0, 0, 1])
      snapfit(dia = head_dia, lip = head_lip, col_h = waist_h / 2 + overlap,
              col_tol = 0.1, slot_ratio = 0.7);
}
```

#### Auger drill bit

A printable auger-style bit with a deep helical flute that comes to a point, the shape used for hand drills, soil augers, and material feeders. Print it as a demo, a feeder screw, or a soft-material auger. Tune the diameters, height, and pitch to change how aggressive the flute is.

```openscad
// Auger drill bit
//
// Source: threads-scad — CC0
// Generated from the library above, used under its license.

use <threads-scad/threads.scad>

outer_d   = 20;  // outer flute diameter
inner_d   = 8;   // solid core diameter
auger_h   = 50;  // overall height of the fluted section
auger_p   = 14;  // pitch (vertical rise per turn) — larger = steeper flute
$fn = 64;

union() {
  AugerThread(outer_d, inner_d, auger_h, auger_p, tooth_angle = 30, tolerance = 0.4);
  // short solid shank below the flutes for chucking
  translate([0, 0, -10]) cylinder(d = inner_d, h = 10.01);
}
```

#### Threaded storage jar neck

A small open-top storage jar whose neck carries external threads, so a matching threaded lid screws on tight. Print it for screws, beads, spices, or small parts. Resize the body and the neck thread to make any jar you need.

```openscad
// Threaded storage jar neck
//
// Source: threads-scad — CC0
// Generated from the library above, used under its license.

use <threads-scad/threads.scad>

jar_diam     = 40;  // outer jar body diameter
jar_height   = 45;  // jar body height (below the neck)
wall         = 2.4; // wall thickness
neck_diam    = 30;  // external thread (neck) diameter
neck_height  = 12;  // threaded neck height
neck_pitch   = 3;   // coarse thread pitch for an easy screw lid
$fn = 96;

difference() {
  // Solid outer shape: jar body + threaded neck, fused into one piece.
  union() {
    cylinder(d = jar_diam, h = jar_height);
    // Neck overlaps 1 mm into the body so the two weld into a single shell.
    translate([0, 0, jar_height - 1])
      ScrewThread(neck_diam, neck_height + 1, pitch = neck_pitch, tolerance = 0.4);
  }
  // One continuous internal cavity opening through the neck.
  translate([0, 0, wall])
    cylinder(d = jar_diam - 2 * wall, h = jar_height + neck_height);
}
```

#### M10 hex nut

A printable M10 hex nut with metric-standard internal threads, sized to thread onto a matching M10 bolt. Great as a captive nut, a knob, or a spare fastener. Adjust the diameter for any metric size and the thickness for a thinner jam nut.

```openscad
// M10 hex nut
//
// Source: threads-scad — CC0
// Generated from the library above, used under its license.

use <threads-scad/threads.scad>

nut_diam  = 10;  // nominal metric thread diameter (M10)
nut_thick = 0;   // 0 = use the metric-standard thickness for this size
fit_tol   = 0.4; // print clearance tolerance
$fn = 48;

MetricNut(nut_diam, thickness = nut_thick, tolerance = fit_tol);
```

#### M8 hex-head bolt

A real, fully printable M8 hex-head machine bolt with metric-standard threads, a hex head, and a built-in hex-key recess on top. Print it for fixtures, jigs, or anywhere a plastic fastener works. Change the diameter and length numbers to make any metric size.

```openscad
// M8 hex-head bolt
//
// Source: threads-scad — CC0
// Generated from the library above, used under its license.

use <threads-scad/threads.scad>

bolt_diam   = 8;   // nominal metric thread diameter (M8)
bolt_length = 25;  // threaded shaft length in mm
fit_tol     = 0.4; // print clearance tolerance
$fn = 48;

MetricBolt(bolt_diam, bolt_length, tolerance = fit_tol);
```

#### Threaded grip knob

A round, knurled-free grip knob with a threaded blind hole in the base so it screws straight onto an M6 stud or bolt. Use it as a tightening handle for jigs, clamps, or machine adjustments. Tune the knob size or the thread diameter to fit your hardware.

```openscad
// Threaded grip knob
//
// Source: threads-scad — CC0
// Generated from the library above, used under its license.

use <threads-scad/threads.scad>

knob_diam   = 30;  // outer knob diameter in mm
knob_height = 16;  // knob height in mm
thread_diam = 6;   // metric thread the knob screws onto (M6)
thread_len  = 12;  // depth of the threaded hole
fit_tol     = 0.4; // print clearance tolerance
$fn = 64;

difference() {
  // domed grip body
  union() {
    cylinder(d = knob_diam, h = knob_height);
    translate([0, 0, knob_height])
      scale([1, 1, 0.45]) sphere(d = knob_diam);
  }
  // threaded mounting hole entering from the base
  ScrewHole(thread_diam, thread_len, position = [0, 0, -0.01], tolerance = fit_tol);
}
```

#### Basic project box enclosure

A clean two-part snap-together project box sized for a small circuit board, with a base tray and matching overlapping lid. Adjust the board length, width, and wall height to fit your own electronics.

```openscad
// Basic project box enclosure
//
// Source: YAPP_Box — MIT
// Generated from the library above, used under its license.

include <yappbox/YAPPgenerator_v3.scad>

// ---- Board footprint the box is built around ----
pcbLength      = 70;   // front-to-back (X)
pcbWidth       = 45;   // side-to-side (Y)
pcbThickness   = 1.6;

// ---- Padding between the board and the inside walls ----
paddingFront   = 3;
paddingBack    = 3;
paddingRight   = 3;
paddingLeft    = 3;

// ---- Shell thicknesses + heights ----
wallThickness      = 2.0;
basePlaneThickness = 1.5;
lidPlaneThickness  = 1.5;
baseWallHeight     = 14;
lidWallHeight      = 8;

// ---- Overlapping ridge that mates base + lid ----
ridgeHeight = 4.0;
roundRadius = 2.0;

printBaseShell = true;
printLidShell  = true;

YAPPgenerate();
```

#### PCB tray with standoffs

A base tray and lid with four printed standoffs that lift and fixate a circuit board off the floor, leaving room underneath for solder joints, plus a side cutout for a power jack. Tune the standoff height and diameter for your board's mounting holes.

```openscad
// PCB tray with standoffs
//
// Source: YAPP_Box — MIT
// Generated from the library above, used under its license.

include <yappbox/YAPPgenerator_v3.scad>

pcbLength      = 75;
pcbWidth       = 50;
pcbThickness   = 1.6;
standoffHeight = 6.0;
standoffDiameter = 7;

paddingFront   = 4;
paddingBack    = 4;
paddingRight   = 4;
paddingLeft    = 4;

wallThickness      = 2.0;
basePlaneThickness = 1.5;
lidPlaneThickness  = 1.5;
baseWallHeight     = 18;
lidWallHeight      = 9;

ridgeHeight = 4.0;
roundRadius = 2.0;

printBaseShell = true;
printLidShell  = true;

// ---- Standoffs that raise + pin the board above the base floor ----
// [posx, posy] relative to the PCB corners
pcbStands =
[
  [6,  6],
  [pcbLength - 6, 6],
  [6,  pcbWidth - 6],
  [pcbLength - 6, pcbWidth - 6],
];

// ---- Power-jack hole in the left wall ----
cutoutsLeft =
[
  [30, 6, 0, 0, 5.5, yappCircle],
];

YAPPgenerate();
```

#### Tool-free snap-fit box

A compact enclosure that clicks shut with integrated snap-fit clips on all four walls, so it needs no screws to close. Great for battery holders or quick-access gadgets. Adjust the clip width and box height to control how firmly the lid grips.

```openscad
// Tool-free snap-fit box
//
// Source: YAPP_Box — MIT
// Generated from the library above, used under its license.

include <yappbox/YAPPgenerator_v3.scad>

pcbLength      = 60;
pcbWidth       = 40;
pcbThickness   = 1.6;

paddingFront   = 3;
paddingBack    = 3;
paddingRight   = 3;
paddingLeft    = 3;

wallThickness      = 2.0;
basePlaneThickness = 1.5;
lidPlaneThickness  = 1.5;
baseWallHeight     = 12;
lidWallHeight      = 9;

ridgeHeight = 4.0;
roundRadius = 2.0;

printBaseShell = true;
printLidShell  = true;

// ---- Snap-fit clips so the lid clicks shut without screws ----
// [position-along-wall, clip-width, which-wall, ...]
snapJoins =
[
  [20, 8, yappLeft,  yappRight, yappCenter, yappSymmetric],
  [16, 8, yappFront, yappBack,  yappCenter, yappSymmetric],
];

YAPPgenerate();
```

#### Vented enclosure with USB port

A project box whose lid carries a row of ventilation slots for airflow and whose front wall has a rectangular cutout for a USB connector. Ideal for boards that run warm. Tune the slot count, slot size, and port opening to match your hardware.

```openscad
// Vented enclosure with USB port
//
// Source: YAPP_Box — MIT
// Generated from the library above, used under its license.

include <yappbox/YAPPgenerator_v3.scad>

pcbLength      = 80;
pcbWidth       = 55;
pcbThickness   = 1.6;

paddingFront   = 4;
paddingBack    = 4;
paddingRight   = 4;
paddingLeft    = 4;

wallThickness      = 2.0;
basePlaneThickness = 1.5;
lidPlaneThickness  = 1.5;
baseWallHeight     = 16;
lidWallHeight      = 10;

ridgeHeight = 4.0;
roundRadius = 2.5;

printBaseShell = true;
printLidShell  = true;

// ---- Ventilation slots across the lid ----
cutoutsLid =
[
  [20, 12, 4, 35, 0, yappRectangle],
  [30, 12, 4, 35, 0, yappRectangle],
  [40, 12, 4, 35, 0, yappRectangle],
  [50, 12, 4, 35, 0, yappRectangle],
];

// ---- USB connector opening in the front wall ----
cutoutsFront =
[
  [25, 4, 14, 8, 1.5, yappRoundedRect],
];

YAPPgenerate();
```

#### Wall-mountable screwed case

An enclosure with external mounting flanges on two sides so it can be screwed to a wall or panel, plus internal screw posts that bolt the lid down onto the base for a secure, serviceable closure. Tune the flange screw size and post diameter to your fasteners.

```openscad
// Wall-mountable screwed case
//
// Source: YAPP_Box — MIT
// Generated from the library above, used under its license.

include <yappbox/YAPPgenerator_v3.scad>

pcbLength      = 90;
pcbWidth       = 60;
pcbThickness   = 1.6;

paddingFront   = 5;
paddingBack    = 5;
paddingRight   = 5;
paddingLeft    = 5;

wallThickness      = 2.4;
basePlaneThickness = 2.0;
lidPlaneThickness  = 2.0;
baseWallHeight     = 18;
lidWallHeight      = 11;

ridgeHeight = 5.0;
roundRadius = 3.0;

printBaseShell = true;
printLidShell  = true;

// ---- External flanges for screwing the case to a wall ----
// [pos, screwDiameter, slotWidth, height, which-wall(s)]
boxMounts =
[
  [15, 3, 3, 3, yappLeft],
  [15, 3, 3, 3, yappRight],
];

// ---- Internal screw posts that bolt the lid to the base ----
// [posx, posy, standHeight, screwD, headD, insertD, outsideD, ...]
connectors =
[
  [8,  8,  5, 2.5, 5, 3.5, 7, yappAllCorners, yappCoordBoxInside],
];

YAPPgenerate();
```

## Deep references

- [Full API reference](/docs/scripting/api) — Every method with signature, parameters, and examples.
- [Full flat API index](/llms-full.md) — Every public scripting symbol in one scannable file — generated from the API manifest, never stale. Read once instead of re-fetching for discovery.
- [Recipe library (JSON)](/recipe-library.json) — Machine-readable corpus of curated recipes + worked examples, each tagged by intent (cad / mesh / previewer / math / io / scene). Filter by intent before authoring a multi-step build.
- [Previewer recipes](/docs/scripting/previewer) — Copy-pasteable round-trip examples.
- [Model Editor recipes](/docs/scripting/model-editor) — Copy-pasteable round-trip examples.

## External LLM agents

Agents auto-fetching site context land at [/llms.txt](/llms.txt) — a terse llmstxt.org-compliant teaser that links back here for the full doctrine. The two files share no duplicate ≥150-char paragraphs; /llms.txt is the machine-readable summary, /claudechrome.md is the comprehensive hub.
