56 lines
1.8 KiB
TypeScript
56 lines
1.8 KiB
TypeScript
import type { PathCommand } from "../types/typeface.js";
|
|
|
|
type XYCommand = PathCommand & { x: number; y: number };
|
|
|
|
/**
|
|
* Reverses the winding order of path commands for TrueType/CFF compatibility.
|
|
* Three.js FontLoader requires counter-clockwise winding; some fonts ship clockwise.
|
|
*/
|
|
export function reverseWinding(commands: PathCommand[]): PathCommand[] {
|
|
const subpaths: PathCommand[][] = [];
|
|
let current: PathCommand[] = [];
|
|
|
|
for (const cmd of commands) {
|
|
if (cmd.type === "m") {
|
|
current = [cmd];
|
|
subpaths.push(current);
|
|
} else if (cmd.type !== "z") {
|
|
current.push(cmd);
|
|
}
|
|
}
|
|
|
|
const reversed: PathCommand[] = [];
|
|
|
|
for (const path of subpaths) {
|
|
// All non-z path commands carry x/y; cast is safe here
|
|
const last = path[path.length - 1] as XYCommand;
|
|
reversed.push({ type: "m", x: last.x, y: last.y });
|
|
|
|
for (let i = path.length - 1; i > 0; i--) {
|
|
const cmd = path[i] as PathCommand;
|
|
const dest = path[i - 1] as XYCommand;
|
|
const out: PathCommand = { type: cmd.type, x: dest.x, y: dest.y };
|
|
|
|
if (cmd.x2 !== undefined && cmd.y2 !== undefined) {
|
|
// Swap control point pairs for cubic bezier reversal.
|
|
// x1/y1 are always defined when x2/y2 are present in a valid cubic bezier.
|
|
const cubic = cmd as PathCommand & {
|
|
x1: number;
|
|
y1: number;
|
|
};
|
|
out.x1 = cmd.x2;
|
|
out.y1 = cmd.y2;
|
|
out.x2 = cubic.x1;
|
|
out.y2 = cubic.y1;
|
|
} else if (cmd.x1 !== undefined && cmd.y1 !== undefined) {
|
|
out.x1 = cmd.x1;
|
|
out.y1 = cmd.y1;
|
|
}
|
|
|
|
reversed.push(out);
|
|
}
|
|
}
|
|
|
|
return reversed;
|
|
}
|