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; }