const fs = require('fs');
const path = require('path');
const { miniCardGroup } = require('./trox_card');
/* ============================================================
TROX – Tuck Box Generator
------------------------------------------------------------
Box-Vorderseite zeigt ECHTE TROX-Karten (identisch zu den
Spielkarten, via gemeinsames Modul trox_card.js), gefächert.
Teile der hinteren Karten werden durch Überlappung verdeckt,
der untere Bereich durch das TROX-Logo – genau wie gewünscht.
node generate_box.js front | wrap | both
HINWEIS DRUCK (TGC): exakte Stanzkontur "Poker Tuck Box (90 cards)"
von der TGC-Produktseite laden und PANEL_W/PANEL_H/SPINE/FLAP unten
daran angleichen – das Artwork skaliert automatisch mit.
============================================================ */
const MODE = (process.argv[2] || 'both').toLowerCase();
const DPI = 300;
const PANEL_W = Math.round(2.55 * DPI); // 765
const PANEL_H = Math.round(3.55 * DPI); // 1065
const SPINE = Math.round(0.90 * DPI); // 270
const FLAP = Math.round(0.55 * DPI); // 165
const BLEED = 37;
const SAFE = 75;
/* ------------------------------------------------------------
FÄCHER-TUNING (hier anpassen):
CARD_SCALE – Größe einer Einzelkarte relativ zur Panelbreite.
Höher = größere Karten. Per Kommandozeile überschreibbar:
node generate_box.js front 0.32
FAN_SPREAD – wie weit der Fächer auseinandergeht (horizontaler Versatz).
FAN_ARC – wie stark der Fächer-Bogen nach unten ausschwingt.
FAN_TILT – maximaler Drehwinkel der äußeren Karten (Grad).
FAN_CY – vertikale Lage des Fächermittelpunkts (Anteil der Höhe).
Eine eingebaute Sicherung verkleinert den Fächer automatisch, falls er
sonst aus dem goldenen Rahmen (Safe Zone) herausragen würde.
------------------------------------------------------------ */
const CLI_SCALE = parseFloat(process.argv[3]);
const CARD_SCALE = !isNaN(CLI_SCALE) ? CLI_SCALE : 0.30; // vorher faktisch 0.42 -> jetzt kleiner
const FAN_SPREAD = 0.50;
const FAN_ARC = 0.11;
const FAN_TILT = 24;
const FAN_CY = 0.34;
const DEFS = `
`;
// Gefächerte ECHTE Karten.
// cx,cy = Fächermittelpunkt
// cardW = Breite einer Karte in px
// maxRight= rechte Grenze (Safe-Zone-Rand), darf nicht überschritten werden
// Gibt {svg, fittedCardW} zurück; verkleinert cardW automatisch, falls nötig.
function cardFan(cx, cy, cardW, panelW) {
const REF_RATIO = 1069 / 694; // Höhe/Breite einer Karte
const tiltRad = FAN_TILT * Math.PI / 180;
// Innenkante des goldenen Rahmens (Safe Zone) plus kleiner Puffer
const margin = SAFE + 6;
const leftLimit = margin;
const rightLimit = panelW - margin;
// Funktion: rechte Außenkante des Fächers für ein gegebenes cardW
function rightEdge(cw) {
const dx = cw * FAN_SPREAD;
const cardH = cw * REF_RATIO;
// halbe Diagonale der gedrehten Außenkarte (worst case Ausdehnung)
const halfSpanX = (Math.abs(Math.cos(tiltRad)) * cw + Math.abs(Math.sin(tiltRad)) * cardH) / 2;
const outerCenterX = cx + 1.55 * dx;
return outerCenterX + halfSpanX;
}
function leftEdge(cw) {
const dx = cw * FAN_SPREAD;
const cardH = cw * REF_RATIO;
const halfSpanX = (Math.abs(Math.cos(tiltRad)) * cw + Math.abs(Math.sin(tiltRad)) * cardH) / 2;
const outerCenterX = cx - 1.55 * dx;
return outerCenterX - halfSpanX;
}
// Auto-Fit: cardW so weit verkleinern, bis beide Außenkanten im Rahmen liegen
let cw = cardW;
let guard = 0;
while ((rightEdge(cw) > rightLimit || leftEdge(cw) < leftLimit) && guard < 200) {
cw *= 0.98;
guard++;
}
const dx = cw * FAN_SPREAD;
const dy = cw * FAN_ARC;
const t = FAN_TILT;
const svg = `
${miniCardGroup('Gruen', '7', cx - 1.55 * dx, cy + 1.7 * dy, cw, -t)}
${miniCardGroup('Schwarz', '12', cx - 0.52 * dx, cy + 0.3 * dy, cw, -t / 3)}
${miniCardGroup('Rot', '17', cx + 0.52 * dx, cy + 0.3 * dy, cw, t / 3)}
${miniCardGroup('Gelb', '4', cx + 1.55 * dx, cy + 1.7 * dy, cw, t)}
`;
return { svg, fittedCardW: cw };
}
function frontContent(w, h) {
const cx = w / 2;
// gewünschte Kartengröße aus CARD_SCALE; Auto-Fit hält sie im Rahmen
const wantCardW = w * CARD_SCALE;
const fan = cardFan(cx, h * FAN_CY, wantCardW, w);
return `
${fan.svg}
TROX
TACTICAL CARD GAME
2-6 Players | Ages 7+ | 80 Cards`;
}
function backContent(w, h) {
const cx = w / 2;
return `
HOW TO PLAY
Predict exactly how many tricks
you will win each round.
Hit your bid exactly -> +10 bonus.
Miss it -> no bonus at all.
Red (Crown) is the permanent
trump and beats every suit.
10 rounds. Cards rise then fall:
1-2-3-4-5-5-4-3-2-1
"Bid exactly. Win tricks.
Rule the table."`;
}
function spineContent(w, h) {
return `
TROX`;
}
function buildFront() {
const w = PANEL_W + 2 * BLEED;
const h = PANEL_H + 2 * BLEED;
return ``;
}
function buildWrap() {
const bandW = PANEL_W + SPINE + PANEL_W + SPINE;
const bandH = PANEL_H;
const totalW = bandW + 2 * BLEED;
const totalH = bandH + 2 * FLAP + 2 * BLEED;
const ox = BLEED, oy = BLEED + FLAP;
return ``;
}
const outputDir = path.join(__dirname, 'box_export');
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
console.log('TROX – Box wird generiert ...');
if (MODE === 'front' || MODE === 'both') {
fs.writeFileSync(path.join(outputDir, 'trox_box_front.svg'), buildFront());
console.log(` Front-Panel -> box_export/trox_box_front.svg (${PANEL_W + 2 * BLEED}x${PANEL_H + 2 * BLEED})`);
}
if (MODE === 'wrap' || MODE === 'both') {
fs.writeFileSync(path.join(outputDir, 'trox_box_wrap.svg'), buildWrap());
console.log(` Box-Netz -> box_export/trox_box_wrap.svg`);
}
console.log('Fertig.');