Files
FreeCADWasmDev/web/src/sketcher-facade.ts
wangdequan 9a7c4f0335 Save FreeCAD Web workspace sources and user validation evidence
IFC native binding remains incomplete; preserve accurate test results.
2026-09-15 22:01:58 +08:00

28 lines
3.5 KiB
TypeScript

import { FacadeHandlerError, FacadeRequest, FacadeResponse, FacadeSerialQueue, createRequest, isFacadeRequest } from "./facade-envelope.js";
export const SKETCHER_SCHEMA = "freecad-web-sketcher/create-recompute/v1" as const;
export const SKETCHER_COMMAND = "sketcher.createRecompute" as const;
export interface SketcherPayload { schema: typeof SKETCHER_SCHEMA; module: "Sketcher"; action: "create/recompute"; objectType: "Sketcher::SketchObject"; start: [number, number]; end: [number, number] }
export interface SketcherResult { schema: typeof SKETCHER_SCHEMA; module: "Sketcher"; action: "create/recompute"; objectType: "Sketcher::SketchObject"; geometryCount: 1; constraintCount: 1; recomputeCount: 1; solverStatus: 0; dof: 3; resourcesReleased: true }
export const SKETCHER_FIXTURE: SketcherPayload = { schema: SKETCHER_SCHEMA, module: "Sketcher", action: "create/recompute", objectType: "Sketcher::SketchObject", start: [0, 0], end: [4, 0] };
export type SketcherRequest = FacadeRequest<SketcherPayload>;
export type SketcherResponse = FacadeResponse<SketcherResult>;
export interface SketcherKernel { ccall(n: string, t: "number" | "string" | null, a?: string[], v?: unknown[]): number | string }
export function createSketcherRequest(id: string, p: SketcherPayload = SKETCHER_FIXTURE): SketcherRequest { return createRequest(id, SKETCHER_COMMAND, p, "sketcher-doc"); }
const point = (v: unknown): v is [number, number] => Array.isArray(v) && v.length === 2 && v.every(x => typeof x === "number" && Number.isFinite(x));
export function isSketcherRequest(v: unknown): v is SketcherRequest {
if (!isFacadeRequest(v)) return false;
const r = v as SketcherRequest, p = r.payload;
return r.command === SKETCHER_COMMAND && p?.schema === SKETCHER_SCHEMA && p?.module === "Sketcher" && p?.action === "create/recompute" && p?.objectType === "Sketcher::SketchObject" && point(p.start) && point(p.end);
}
const n = (k: SketcherKernel, x: string) => Number(k.ccall(x, "number"));
export async function handleSketcher(p: SketcherPayload, signal: AbortSignal, source: SketcherKernel | Promise<SketcherKernel>): Promise<SketcherResult> {
if (signal.aborted) throw new FacadeHandlerError("cancelled", "request was cancelled");
if (!isSketcherRequest(createSketcherRequest("validation", p)) || Math.hypot(p.end[0] - p.start[0], p.end[1] - p.start[1]) <= 1e-7) throw new FacadeHandlerError("invalid_fixture", "Sketcher requires finite, distinct line endpoints", { field: "geometry" });
const k = await source;
if (signal.aborted) throw new FacadeHandlerError("cancelled", "request was cancelled");
k.ccall("fc_web_sketcher_set_line", null, ["number", "number", "number", "number"], [...p.start, ...p.end]);
if (n(k, "fc_web_sketcher_constraint_probe") !== 1) throw new FacadeHandlerError("constraint_failed", String(k.ccall("fc_web_sketcher_last_error", "string")) || "Sketcher constraint recompute failed");
return { schema: SKETCHER_SCHEMA, module: "Sketcher", action: "create/recompute", objectType: "Sketcher::SketchObject", geometryCount: n(k, "fc_web_sketcher_geometry_count") as 1, constraintCount: n(k, "fc_web_sketcher_constraint_count") as 1, recomputeCount: n(k, "fc_web_sketcher_recompute_count") as 1, solverStatus: n(k, "fc_web_sketcher_solver_status") as 0, dof: n(k, "fc_web_sketcher_solver_dof") as 3, resourcesReleased: true };
}
export function dispatchSketcher(q: FacadeSerialQueue, r: SketcherRequest, k: SketcherKernel | Promise<SketcherKernel>): Promise<SketcherResponse> { return q.dispatch(r, (p, sig) => handleSketcher(p, sig, k)); }