Coaster

Honeycomb coaster: a round puck with a raised rim, a field of hexagonal pockets laid out on a staggered grid (only the cells that fit inside the rim), and a filleted top edge.
examples/coaster.tsx on GitHub
bunx tscad dev examples/coaster.tsx/**
* Honeycomb coaster: a round puck with a raised rim, a field of hexagonal pockets laid out on
* a staggered grid (only the cells that fit inside the rim), and a filleted top edge.
*/
import { defineParams, edges, type ParamValues } from "tscad"
export const params = defineParams({
diameter: { type: "number", default: 100, min: 60, max: 160, step: 1, unit: "mm", label: "Diameter" },
thickness: { type: "number", default: 6, min: 3, max: 15, step: 0.5, unit: "mm", label: "Thickness" },
cell: { type: "number", default: 10, min: 5, max: 25, step: 0.5, unit: "mm", label: "Cell size" },
web: { type: "number", default: 1.6, min: 0.8, max: 5, step: 0.1, unit: "mm", label: "Web" },
depth: { type: "number", default: 2.5, min: 0.5, max: 10, step: 0.1, unit: "mm", label: "Pocket depth" },
rim: { type: "number", default: 5, min: 2, max: 15, step: 0.5, unit: "mm", label: "Rim width" },
})
export default function Coaster(p: ParamValues<typeof params>) {
const R = p.diameter / 2
const inner = R - p.rim
const pitch = p.cell + p.web // flat-to-flat pitch between neighbouring cells
const hexR = p.cell / Math.sqrt(3) // circumradius of one cell
const n = Math.ceil(inner / pitch) + 1
// Honeycomb centres: rows `pitch` apart, every other column shifted by half a pitch.
// Keep only the cells that fit entirely inside the rim; it's just an array filter.
const centres: [number, number][] = []
for (let i = -2 * n; i <= 2 * n; i++) {
for (let j = -n; j <= n; j++) {
const x = (i * pitch * Math.sqrt(3)) / 2
const y = j * pitch + (i % 2 ? pitch / 2 : 0)
if (Math.hypot(x, y) + hexR <= inner) centres.push([x, y])
}
}
return (
<part name="coaster" color="#d9a066">
<difference>
<fillet radius={1} edges={edges.top()}>
<cylinder r={R} h={p.thickness} />
</fillet>
{/* every pocket in one sketch, so the whole honeycomb is a single cut */}
<extrude height={p.depth + 1} z={p.thickness - p.depth}>
{centres.map(([x, y]) => (
<regularPolygon sides={6} flats={p.cell} x={x} y={y} />
))}
</extrude>
</difference>
</part>
)
}