import type { MeshEditOperation, MeshElementMode } from "./web-engine"; import type { SimplifyProfile } from "./simplify"; import type { WebEngineEditCommand } from "./web-engine"; type MeshCommand = Extract; type Vector3 = [number, number, number]; const vector3 = (value: readonly number[]): Vector3 => [value[0] ?? 0, value[1] ?? 0, value[2] ?? 0]; /** Typed constructors for mesh authoring commands sent to WebEngine Main. */ export const meshAuthoringCommands = Object.freeze({ edit(meshId: string, operation: MeshEditOperation, selectionMode: MeshElementMode, elementIndices: readonly number[], options: { offset?: readonly number[]; amount?: number; segments?: number } = {}): MeshCommand<"meshEdit"> { return { type: "meshEdit", meshId, operation, selectionMode, elementIndices: [...elementIndices], ...(options.offset ? { offset: vector3(options.offset) } : {}), ...(options.amount === undefined ? {} : { amount: options.amount }), ...(options.segments === undefined ? {} : { segments: options.segments }), }; }, meshSubdivide(meshId: string, selectionMode: MeshElementMode, elementIndices: readonly number[], segments = 1): MeshCommand<"meshSubdivide"> { return { type: "meshSubdivide", meshId, selectionMode, elementIndices: [...elementIndices], segments: Math.max(1, Math.min(64, Math.trunc(segments))) }; }, meshTriangulate(meshId: string, faceIndices: readonly number[]): MeshCommand<"meshTriangulate"> { return { type: "meshTriangulate", meshId, faceIndices: [...faceIndices] }; }, meshRecalculateNormals(meshId: string, faceIndices: readonly number[]): MeshCommand<"meshRecalculateNormals"> { return { type: "meshRecalculateNormals", meshId, faceIndices: [...faceIndices] }; }, meshBridgeLoops(meshId: string, edgeIndices: readonly number[]): MeshCommand<"meshBridgeLoops"> { return { type: "meshBridgeLoops", meshId, edgeIndices: [...edgeIndices] }; }, meshDeleteElements(meshId: string, selectionMode: MeshElementMode, elementIndices: readonly number[]): MeshCommand<"meshDeleteElements"> { return { type: "meshDeleteElements", meshId, selectionMode, elementIndices: [...elementIndices] }; }, meshUVProject(meshId: string, faceIndices: readonly number[], method: "PLANAR" | "CUBE" | "CYLINDER" | "SPHERE" = "PLANAR", seamEdgeIndices: readonly number[] = []): MeshCommand<"meshUVProject"> { return { type: "meshUVProject", meshId, faceIndices: [...faceIndices], method, ...(seamEdgeIndices.length ? { seamEdgeIndices: [...seamEdgeIndices] } : {}) }; }, decimate(meshId: string, profile: SimplifyProfile): MeshCommand<"decimateMesh"> { return { type: "decimateMesh", meshId, profile }; }, previewDecimate(meshId: string, profile: SimplifyProfile): MeshCommand<"previewDecimateMesh"> { return { type: "previewDecimateMesh", meshId, profile }; }, translateVertices(meshId: string, vertexIndices: readonly number[], offset: readonly number[]): MeshCommand<"translateMeshVertices"> { return { type: "translateMeshVertices", meshId, vertexIndices: [...vertexIndices], offset: vector3(offset) }; }, createUVMap(meshId: string, name: string): MeshCommand<"createUVMap"> { return { type: "createUVMap", meshId, name }; }, activeUVMap(meshId: string, name: string): MeshCommand<"setActiveUVMap"> { return { type: "setActiveUVMap", meshId, name }; }, } satisfies Record WebEngineEditCommand>);