import type { STLImportResult } from "./stl-import"; export const STL_EXPORT_SCHEMA_VERSION = 1 as const; export interface STLLossReport { schemaVersion: typeof STL_EXPORT_SCHEMA_VERSION; operation: "STL_EXPORT_LOSS_REPORT"; canRoundTrip: boolean; warningCount: number; warnings: Array<{ code: "STL_MATERIAL_UNSUPPORTED"; severity: "warning"; message: string }>; } function writeFloat(view: DataView, offset: number, value: number): void { if (!Number.isFinite(value)) throw new Error("STL_EXPORT_NUMBER_INVALID"); view.setFloat32(offset, value, true); } export function exportBinarySTL(document: STLImportResult): ArrayBuffer { if (document.schemaVersion !== 1 || document.triangleCount !== document.vertices.length || document.triangleCount !== document.normals.length) throw new Error("STL_EXPORT_DOCUMENT_INVALID"); const output = new ArrayBuffer(84 + document.triangleCount * 50); const bytes = new Uint8Array(output); bytes.set(new TextEncoder().encode("Web Blender STL schema 1").subarray(0, 80)); const view = new DataView(output); view.setUint32(80, document.triangleCount, true); for (let triangle = 0; triangle < document.triangleCount; triangle++) { const offset = 84 + triangle * 50; for (let axis = 0; axis < 3; axis++) writeFloat(view, offset + axis * 4, document.normals[triangle][axis]); for (let vertex = 0; vertex < 3; vertex++) for (let axis = 0; axis < 3; axis++) writeFloat(view, offset + 12 + vertex * 12 + axis * 4, document.vertices[triangle][vertex][axis]); view.setUint16(offset + 48, 0, true); } return output; } export function createSTLLossReport(sourceMaterialCount: number): STLLossReport { if (!Number.isSafeInteger(sourceMaterialCount) || sourceMaterialCount < 0) throw new Error("STL_EXPORT_MATERIAL_COUNT_INVALID"); const warnings = sourceMaterialCount > 0 ? [{ code: "STL_MATERIAL_UNSUPPORTED" as const, severity: "warning" as const, message: `STL has no material slots; ${sourceMaterialCount} source material assignments are omitted`, }] : []; return { schemaVersion: STL_EXPORT_SCHEMA_VERSION, operation: "STL_EXPORT_LOSS_REPORT", canRoundTrip: true, warningCount: warnings.length, warnings }; }