UNPKG

@bitbybit-dev/manifold-worker

Version:

Bit By Bit Developers Manifold Based CAD Library to Program Geometry Via WebWorker

148 lines (147 loc) 7.37 kB
import { CacheHelper } from "./cache-helper"; import { ManifoldService } from "@bitbybit-dev/manifold"; let manifold; let cacheHelper; let dependencies; export const initializationComplete = (mnf, plugins, doNotPost) => { cacheHelper = new CacheHelper(); manifold = new ManifoldService(mnf); if (!doNotPost) { postMessage("manifold-initialised"); } }; export const onMessageInput = (d, postMessage) => { postMessage("busy"); let result; try { // Ok, so this is baked in memoization as all Manifold computations are potentially expensive // we can always return already computed entity hashes. On UI side we only deal with hashes as long // as we don't need to render things and when we do need, we call tessellation methods with these hashes // and receive real objects. This cache is useful in modeling operations throughout 'run' sessions. if (d.action.functionName !== "manifoldToMesh" && d.action.functionName !== "manifoldsToMeshes" && d.action.functionName !== "deleteManifoldOrCrossSection" && d.action.functionName !== "deleteManifoldsOrCrossSections" && d.action.functionName !== "manifoldToMeshPointer" && d.action.functionName !== "startedTheRun" && d.action.functionName !== "cleanAllCache" && d.action.functionName !== "addManifoldPluginDependency") { // if inputs have manifold or manifolds properties, these are hashes on which the operations need to be performed. // We thus replace these hashes to real objects from the cache before functions are called, // this probably looks like smth generic but isn't, so will need to check if it works Object.keys(d.action.inputs).forEach(key => { const val = d.action.inputs[key]; if (val && val.type && val.type === "manifold-shape" && val.hash) { d.action.inputs[key] = cacheHelper.checkCache(d.action.inputs[key].hash); } if (val && Array.isArray(val) && val.length > 0) { if ((val[0].type && val[0].type === "manifold-shape" && val[0].hash)) { d.action.inputs[key] = d.action.inputs[key].map(manifold => cacheHelper.checkCache(manifold.hash)); } else if ((Array.isArray(val[0]) && val[0][0].type && val[0][0].type === "manifold-shape" && val[0][0].hash)) { d.action.inputs[key] = d.action.inputs[key].map(manifolds => manifolds.map(manifold => cacheHelper.checkCache(manifold.hash))); } } }); const path = d.action.functionName.split("."); let res; if (path.length === 3) { res = cacheHelper.cacheOp(d.action, () => manifold[path[0]][path[1]][path[2]](d.action.inputs)); } else if (path.length === 2) { res = cacheHelper.cacheOp(d.action, () => manifold[path[0]][path[1]](d.action.inputs)); } else { res = cacheHelper.cacheOp(d.action, () => manifold[d.action.functionName](d.action.inputs)); } if (!cacheHelper.isManifoldObject(res)) { if (res && res.compound && res.data && res.manifolds && res.manifolds.length > 0) { const r = res; r.manifolds = r.manifolds.map(s => ({ id: s.id, manifold: { hash: s.manifold.hash, type: "manifold-shape" } })); r.compound = { hash: r.compound.hash, type: "manifold-shape" }; result = r; } else { result = res; } } else if (Array.isArray(res)) { result = res.map(r => ({ hash: r.hash, type: "manifold-shape" })); // if we return multiple shapes we should return array of cached hashes } else { result = { hash: res.hash, type: "manifold-shape" }; } } if (d.action.functionName === "addManifoldPluginDependency") { if (manifold && manifold.plugins) { Object.keys(d.action.inputs).forEach(c => { manifold.plugins.dependencies[c] = d.action.inputs[c]; }); } else { dependencies = d.action.inputs; } } if (d.action.functionName === "manifoldToMesh") { d.action.inputs.manifold = cacheHelper.checkCache(d.action.inputs.manifold.hash); result = manifold.decomposeManifoldOrCrossSection(d.action.inputs); } if (d.action.functionName === "manifoldToMeshPointer") { d.action.inputs.manifold = cacheHelper.checkCache(d.action.inputs.manifold.hash); const r = manifold.manifold.manifoldToMesh(d.action.inputs); const hash = cacheHelper.computeHash(d.action); cacheHelper.addToCache(hash, r); result = { hash, type: "manifold-shape" }; } if (d.action.functionName === "manifoldsToMeshes") { if (d.action.inputs.manifolds && d.action.inputs.manifolds.length > 0) { d.action.inputs.manifolds = d.action.inputs.manifolds.map(manifold => cacheHelper.checkCache(manifold.hash)); } else { throw new Error("No manifolds detected"); } result = manifold.decomposeManifoldsOrCrossSections(d.action.inputs); } if (d.action.functionName === "deleteManifoldOrCrossSection") { cacheHelper.cleanCacheForHash(d.action.inputs.manifoldOrCrossSection.hash); result = {}; } if (d.action.functionName === "deleteManifoldsOrCrossSections") { d.action.inputs.manifoldsOrCrossSections.forEach(manifold => cacheHelper.cleanCacheForHash(manifold.hash)); result = {}; } // Only the cache that was created in previous run has to be kept, the rest needs to go if (d.action.functionName === "startedTheRun") { // if certain threshold is reacherd we clean all the cache if (cacheHelper && Object.keys(cacheHelper.usedHashes).length > 10000) { cacheHelper.cleanAllCache(); } result = {}; } if (d.action.functionName === "cleanAllCache") { cacheHelper.cleanAllCache(); result = {}; } // Returns only the hash as main process can't receive pointers // But with hash reference we can always initiate further computations postMessage({ uid: d.uid, result }); } catch (e) { let props; if (d && d.action && d.action.inputs) { props = `Input values were: {${Object.keys(d.action.inputs).map(key => `${key}: ${JSON.stringify(d.action.inputs[key])}`).join(",")}}. `; } let fun; if (d && d.action && d.action.functionName) { fun = `- ${d.action.functionName}`; } postMessage({ uid: d.uid, result: undefined, error: `Manifold computation failed. ${e} While executing function ${fun}. ${props}` }); } };