ttrpg-tools/src/components/md-deck/hooks/usePlotterExport.ts

124 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { DeckStore } from './deckStore';
import type { PageData } from './usePDFExport';
import type { CardShape } from '../types';
import { pts2plotter } from '../../../plotcutter';
export interface UsePlotterExportReturn {
exportToPlt: (pages: PageData[]) => void;
}
/**
* 根据形状生成卡片轮廓点单位mm相对于卡片左下角
*/
function getCardShapePoints(
shape: CardShape,
width: number,
height: number
): [number, number][] {
const points: [number, number][] = [];
switch (shape) {
case 'circle': {
// 圆形:生成 36 个点近似圆
const radius = Math.min(width, height) / 2;
const centerX = width / 2;
const centerY = height / 2;
for (let i = 0; i < 36; i++) {
const angle = (i / 36) * Math.PI * 2;
points.push([
centerX + radius * Math.cos(angle),
centerY + radius * Math.sin(angle)
]);
}
break;
}
case 'triangle': {
// 正三角形:顶点向上
points.push([width / 2, 0]);
points.push([0, height]);
points.push([width, height]);
break;
}
case 'hexagon': {
// 六边形:尖顶向上
const halfW = width / 2;
const quarterH = height / 4;
points.push([halfW, 0]);
points.push([width, quarterH]);
points.push([width, height - quarterH]);
points.push([halfW, height]);
points.push([0, height - quarterH]);
points.push([0, quarterH]);
break;
}
case 'rectangle':
default: {
// 矩形:顺时针方向
points.push([0, 0]);
points.push([width, 0]);
points.push([width, height]);
points.push([0, height]);
break;
}
}
return points;
}
/**
* PLT 导出 hook - 生成 HPGL 格式文件并下载
*/
export function usePlotterExport(store: DeckStore): UsePlotterExportReturn {
const exportToPlt = (pages: PageData[]) => {
const cardWidth = store.state.dimensions?.cardWidth || 56;
const cardHeight = store.state.dimensions?.cardHeight || 88;
const shape = store.state.shape;
// 计算所有页面的总尺寸
const a4Width = 297; // 横向 A4
const a4Height = 210;
// 收集所有卡片的轮廓点
const allPaths: [number, number][][] = [];
for (const page of pages) {
for (const card of page.cards) {
// 只导出正面
if (card.side !== 'front') continue;
// 获取卡片形状点(相对于卡片原点)
const shapePoints = getCardShapePoints(shape, cardWidth, cardHeight);
// 转换点到页面坐标Y 轴翻转SVG Y 向下plotter Y 向上)
const pagePoints = shapePoints.map(([x, y]) => [
card.x + x,
a4Height - (card.y + y)
] as [number, number]);
allPaths.push(pagePoints);
}
}
if (allPaths.length === 0) {
alert('没有可导出的卡片');
return;
}
// 生成 HPGL 指令
const plotterCode = pts2plotter(allPaths, a4Width, a4Height, 1);
// 创建 Blob 并下载
const blob = new Blob([plotterCode], { type: 'application/vnd.hp-HPGL' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `deck-export-${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.plt`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};
return { exportToPlt };
}