Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/thatdeveloperguy-website / tools/svg/refine.ts |
| Date | Not specified in source |
| Repository | Janady13/thatdeveloperguy-website |
| Revision | 2bea2ebdff0e34494bff597d4fa9c8422a0c3a94 |
| Branch | main |
| Path or archive member | tools/svg/refine.ts |
| Original SHA-256 | 8681c11e083fcd99026ac03e0fa2221712d220b8c740f00a0e3812ea35ffe9a6 |
| Git blob SHA | 4d988c10dc2f9cce300f1b563a5426f0d2da205a |
| Reading-copy SHA-256 | 8681c11e083fcd99026ac03e0fa2221712d220b8c740f00a0e3812ea35ffe9a6 |
Recovered source transcript
Download complete source text ↓53 lines · 3.1 KB
import type { PathNode, SceneDoc, SvgNode } from './model.ts';
import { countRings } from './parse.ts';
import { fitRing, ringArea, unionRings, type Contour } from './geometry.ts';
export { serializeScene, extractPart, listGroupIds } from './serialize.ts';
export interface RefineOptions { minArea: number; tolerance: number; minTurnDeg: number; union: boolean }
/**
* minArea 1 px²: the traces' 0.6 px dither squares (0.36 px²) go, two-pixel lettering fragments stay.
* union stays off: the tracer draws lettering and cut-outs as opposite-wound holes, which a union fills in,
* and measured on the lobby it removed under 4% of vertices anyway.
*/
export const DEFAULT_REFINE: RefineOptions = { minArea: 1, tolerance: 0.75, minTurnDeg: 60, union: false };
export interface RefinedPath { kind: 'path'; id: string; fill: string; contours: Contour[] }
export interface RefinedGroup { kind: 'group'; id: string; label?: string; children: RefinedNode[] }
export type RefinedNode = RefinedPath | RefinedGroup;
export interface RefinedDoc { width: number; height: number; title: string; children: RefinedNode[] }
export interface RefineStats {
before: { paths: number; rings: number; vertices: number };
after: { paths: number; contours: number; vertices: number };
dropped: { rings: number; paths: number };
}
function refinePath(path: PathNode, options: RefineOptions, stats: RefineStats): RefinedPath | null {
const kept = path.rings.filter(ring => Math.abs(ringArea(ring)) >= options.minArea);
stats.dropped.rings += path.rings.length - kept.length;
if (kept.length === 0) { stats.dropped.paths++; return null; }
const rings = options.union ? unionRings(kept) : kept;
const contours = rings.map(ring => fitRing(ring, options.tolerance, options.minTurnDeg));
stats.after.paths++;
stats.after.contours += contours.length;
for (const contour of contours) stats.after.vertices += 1 + contour.segments.length;
return { kind: 'path', id: path.id, fill: path.fill, contours };
}
function refineNodes(nodes: SvgNode[], options: RefineOptions, stats: RefineStats): RefinedNode[] {
const out: RefinedNode[] = [];
for (const node of nodes) {
if (node.kind === 'path') { const refined = refinePath(node, options, stats); if (refined) out.push(refined); continue; }
const group: RefinedGroup = { kind: 'group', id: node.id, children: refineNodes(node.children, options, stats) };
if (node.label) group.label = node.label;
out.push(group); // empty groups stay: the manifest names them and Rive nodes may need them as pivots
}
return out;
}
/** Speckle drop → same-paint union → corner-aware cubic fit. Ids, labels, fills and tree order are untouched. */
export function refineScene(doc: SceneDoc, options: RefineOptions = DEFAULT_REFINE): { doc: RefinedDoc; stats: RefineStats } {
const stats: RefineStats = { before: countRings(doc), after: { paths: 0, contours: 0, vertices: 0 }, dropped: { rings: 0, paths: 0 } };
const children = refineNodes(doc.children, options, stats);
return { doc: { width: doc.width, height: doc.height, title: doc.title, children }, stats };
}