ttrpg-tools/src/plotcutter/plotter.ts

73 lines
1.8 KiB
TypeScript

import {normalize} from "./normalize";
export function pts2plotter(pts: [number, number][][], width: number, height: number, px2mm = 0.1){
let str = init(width * px2mm, height * px2mm);
// sort paths by x(long) then by y(short)
const sorted = pts.slice();
sorted.sort(function (a, b) {
const [ax,ay] = topleft(a);
const [bx,by] = topleft(b);
if (ax !== bx) return ax - bx;
return ay - by;
});
let lead = true;
for(const path of sorted){
for (const cmd of poly(normalize(path), height, px2mm, lead)) {
str += cmd;
}
lead = false;
}
str += end();
return str;
}
function topleft(pts: [number, number][]){
let minx = NaN;
let miny = NaN;
for(const pt of pts){
if (isNaN(minx) || minx > pt[0]) minx = pt[0];
if (isNaN(miny) || miny > pt[1]) miny = pt[1];
}
return [minx, miny] as [number, number];
}
function init(w: number, h: number) {
return ` IN TB26,${plu(w)},${plu(h)} CT1 U0,0 D0,0 D40,0`;
}
function end() {
return ' U0,0 @ @';
}
function* poly(pts: [number, number][], height: number, px2mm: number, lead = false){
function cutpt(down: boolean, pt: [number, number]) {
return ` ${down ? 'D' : 'U'}${plu(pt[0] * px2mm)},${plu((height - pt[1]) * px2mm)}`;
}
if (lead) {
yield cutpt(false, [0, 0]);
yield cutpt(true, [0, 1]);
}
yield cutpt(false, pts[0]);
for(const pt of pts){
yield cutpt(true, pt);
}
}
function sqrlen(x: number, y: number) {
return x * x + y * y;
}
function lerp(s: [number, number], e: [number, number], i: number) {
return [s[0] + (e[0] - s[0]) * i, s[1] + (e[1] - s[1]) * i] as typeof s;
}
function plu(n: number) {
return Math.round(n / 0.025);
}