printhive/public/printer-art.js
dlawler489 c2bae07e54 Show each printer's loaded filament on its card
Each sync keeps what the printer reported, slot by slot (material, colour,
empty, tagged — nothing that identifies a spool). The card's drawing colours
in the first AMS unit's spools, dashes empty slots, and lists every slot and
the external holder underneath with its colour name. "Test & sync" redraws
the card with what it just read.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 21:21:03 +10:00

131 lines
5.6 KiB
JavaScript

// Printer models, and simple line drawings of their shapes, drawn for
// Printhive. The drawings suggest a model's form — open frame, enclosed, large
// format — rather than depict any product exactly.
(function () {
const svg = (inner, width = 150) =>
`<svg viewBox="0 0 ${width} 120" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor"
stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" role="img">${inner}</svg>`;
// One spool in a drawing, filled with what's loaded there: a colour for a
// spool, dashed for an empty slot, plain when nothing is known.
const HEX = /^#[0-9a-f]{6}$/i;
const spool = (cx, cy, r, slot) => {
if (slot && slot.empty) return `<circle cx="${cx}" cy="${cy}" r="${r}" stroke-dasharray="2 3" opacity="0.6"/>`;
const fill = slot && HEX.test(slot.colour || '') ? slot.colour : null;
return fill
? `<circle cx="${cx}" cy="${cy}" r="${r}" fill="${fill}"/><circle cx="${cx}" cy="${cy}" r="${r / 3}"/>`
: `<circle cx="${cx}" cy="${cy}" r="${r}"/>`;
};
// An AMS lite: a stand with four spools around a hub.
const amsLite = (x, slots) => `
<line x1="${x}" y1="104" x2="${x}" y2="52"/>
<line x1="${x - 10}" y1="104" x2="${x + 10}" y2="104"/>
${spool(x - 9, 40, 7, slots[0])}${spool(x + 9, 40, 7, slots[1])}
${spool(x - 9, 58, 7, slots[2])}${spool(x + 9, 58, 7, slots[3])}`;
// An AMS on top of an enclosed printer: a box showing four spools.
const amsTop = (x, width, slots) => {
const step = width / 4;
let spools = '';
for (let i = 0; i < 4; i += 1) spools += spool(x + step / 2 + i * step, 23, 5, slots[i]);
return `<rect x="${x}" y="14" width="${width}" height="18" rx="3"/>${spools}`;
};
const toolhead = (x, y) => `<rect x="${x}" y="${y}" width="14" height="15" rx="2"/>
<line x1="${x + 7}" y1="${y + 15}" x2="${x + 7}" y2="${y + 20}"/>`;
const shapes = {
// A1: a bed on a base, an upright gantry, an AMS lite alongside.
open: (slots) => svg(`
<rect x="12" y="94" width="92" height="10" rx="3"/>
<rect x="24" y="84" width="68" height="6" rx="2"/>
<line x1="20" y1="94" x2="20" y2="26"/><line x1="96" y1="94" x2="96" y2="26"/>
<line x1="16" y1="26" x2="100" y2="26"/><line x1="20" y1="56" x2="96" y2="56"/>
${toolhead(50, 49)}
${amsLite(126, slots)}`),
// A1 mini: a single column holding the gantry out over the bed.
openMini: (slots) => svg(`
<rect x="22" y="94" width="72" height="10" rx="3"/>
<rect x="36" y="84" width="50" height="6" rx="2"/>
<rect x="24" y="30" width="10" height="64" rx="2"/>
<line x1="34" y1="54" x2="90" y2="54"/>
${toolhead(58, 47)}
${amsLite(122, slots)}`),
// A2L: the A1's form, stretched to a larger bed.
openLarge: (slots) => svg(`
<rect x="6" y="94" width="104" height="10" rx="3"/>
<rect x="14" y="84" width="88" height="6" rx="2"/>
<line x1="10" y1="94" x2="10" y2="22"/><line x1="106" y1="94" x2="106" y2="22"/>
<line x1="6" y1="22" x2="110" y2="22"/><line x1="10" y1="54" x2="106" y2="54"/>
${toolhead(52, 47)}
${amsLite(132, slots)}`),
// P1P: a bare cube frame.
frame: () => svg(`
<rect x="30" y="38" width="66" height="66" rx="3"/>
<path d="M30 38 L44 28 L110 28 L96 38 M110 28 L110 94 L96 104"/>
<line x1="30" y1="60" x2="96" y2="60"/>
${toolhead(56, 53)}
<rect x="40" y="90" width="46" height="5" rx="2"/>`, 140),
// P1S, P2S, X1C, X1E, X2D: an enclosed box with an AMS on top.
box: (slots) => svg(`
${amsTop(30, 66, slots)}
<rect x="28" y="36" width="70" height="70" rx="5"/>
<rect x="36" y="44" width="54" height="46" rx="3"/>
<line x1="36" y1="98" x2="90" y2="98"/>
${toolhead(56, 52)}`, 126),
// H2S, H2D, H2C: a wide enclosed box with an AMS on top.
large: (slots) => svg(`
${amsTop(28, 94, slots)}
<rect x="18" y="36" width="114" height="70" rx="5"/>
<rect x="26" y="44" width="98" height="46" rx="3"/>
<line x1="26" y1="98" x2="124" y2="98"/>
${toolhead(58, 52)}${toolhead(78, 52)}`),
// Anything else: a plain box with a nozzle.
generic: () => svg(`
<rect x="34" y="30" width="72" height="72" rx="8"/>
${toolhead(63, 50)}
<line x1="44" y1="92" x2="96" y2="92"/>`, 140),
};
// ams is only a suggestion — a P1S might have an AMS, an AMS 2 Pro or an AMS HT.
const models = [
{ id: 'A1 mini', shape: 'openMini', ams: 'ams_lite' },
{ id: 'A1', shape: 'open', ams: 'ams_lite' },
{ id: 'A2L', shape: 'openLarge', ams: 'ams_lite' },
{ id: 'P1P', shape: 'frame', ams: 'ams' },
{ id: 'P1S', shape: 'box', ams: 'ams' },
{ id: 'P2S', shape: 'box', ams: 'ams_2_pro' },
{ id: 'X1C', shape: 'box', ams: 'ams' },
{ id: 'X1E', shape: 'box', ams: 'ams' },
{ id: 'X2D', shape: 'box', ams: 'ams_2_pro' },
{ id: 'H2S', shape: 'large', ams: 'ams_2_pro' },
{ id: 'H2D', shape: 'large', ams: 'ams_2_pro' },
{ id: 'H2C', shape: 'large', ams: 'ams_2_pro' },
{ id: 'Other', shape: 'generic', ams: 'none' },
];
const amsTypes = [
{ id: 'none', label: 'No AMS (external spool)' },
{ id: 'ams_lite', label: 'AMS lite (full or empty only)' },
{ id: 'ams', label: 'AMS' },
{ id: 'ams_2_pro', label: 'AMS 2 Pro' },
{ id: 'ams_ht', label: 'AMS HT' },
];
const find = (id) => models.find((m) => m.id === id) || null;
window.PRINTER_MODELS = {
models,
amsTypes,
find,
// `slots`: the first AMS unit's four slots, as { empty, colour }, to colour in.
art: (id, slots = []) => (shapes[(find(id) || {}).shape] || shapes.generic)(slots),
};
}());