From 3b034ee99245e273021c739af325e84e8028e19a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 14 Mar 2026 19:10:56 +0000 Subject: [PATCH] Optimize mesh lookups from O(N) to O(1) using reverse-lookup Maps Replace linear scans in getBlockKeyFromBlock, getBlockKeyFromBlockID, getMeshFromBlockKey, and getMeshesFromBlockKey with direct Map lookups. - Add blockToKeyMap, blockIdToKeyMap, and blockKeyMeshesMap to generators.js - Maintain all 5 Maps together at every assignment site (17 locations) - Clear all Maps at both generator init/init2 reset sites - Register meshes via registerMeshForBlockKey in api/shapes.js, api/mesh.js, api/csg.js, and api/camera.js at each metadata.blockKey assignment - Rewrite the 4 lookup functions in blockmesh.js to use Map.get() / Set iteration - Clean up reverse Maps in deleteMeshFromBlock on disposal https://claude.ai/code/session_011y6sJ1hJUwznDNwHzcphiQ --- api/camera.js | 3 +++ api/csg.js | 6 +++++ api/mesh.js | 3 +++ api/shapes.js | 6 +++++ generators/generators.js | 51 ++++++++++++++++++++++++++++++++++++++++ ui/blockmesh.js | 30 +++++++++++++---------- 6 files changed, 86 insertions(+), 13 deletions(-) diff --git a/api/camera.js b/api/camera.js index ec024e20..9307c68a 100644 --- a/api/camera.js +++ b/api/camera.js @@ -1,3 +1,5 @@ +import { registerMeshForBlockKey } from "../generators/generators.js"; + let flock; export function setFlockReference(ref) { @@ -141,6 +143,7 @@ export const flockCamera = { ); constraintBox.metadata = constraintBox.metadata || {}; constraintBox.metadata.blockKey = constraintBox.name; + registerMeshForBlockKey(constraintBox, constraintBox.name); constraintBox.name = constraintBox.name + "_" + constraintBox.uniqueId; constraintBox.isVisible = false; constraintBox.material = constraintBox.material || new flock.BABYLON.StandardMaterial("staticMaterial", scene); diff --git a/api/csg.js b/api/csg.js index 4588a459..89fe743a 100644 --- a/api/csg.js +++ b/api/csg.js @@ -1,3 +1,5 @@ +import { registerMeshForBlockKey } from "../generators/generators.js"; + let flock; export function setFlockReference(ref) { @@ -357,6 +359,7 @@ export const flockCSG = { {}; mergedMesh.metadata.blockKey = blockId; + registerMeshForBlockKey(mergedMesh, blockId); mergedMesh.metadata.sharedMaterial = false; return mergedMesh; @@ -455,6 +458,7 @@ export const flockCSG = { mergedMesh.metadata = mergedMesh.metadata || {}; mergedMesh.metadata.blockKey = blockId; + registerMeshForBlockKey(mergedMesh, blockId); mergedMesh.metadata.sharedMaterial = false; const isDefaultMaterial = ( @@ -1714,6 +1718,7 @@ export const flockCSG = { {}; mesh.metadata.blockKey = blockId; + registerMeshForBlockKey(mesh, blockId); resolve(mesh); } else { console.warn( @@ -1736,6 +1741,7 @@ export const flockCSG = { resultMesh.name = modelId; resultMesh.metadata = resultMesh.metadata || {}; resultMesh.metadata.blockKey = blockId; + registerMeshForBlockKey(resultMesh, blockId); // Apply physics flock.applyPhysics( diff --git a/api/mesh.js b/api/mesh.js index 8a962f9a..ad9f1d8f 100644 --- a/api/mesh.js +++ b/api/mesh.js @@ -1,4 +1,5 @@ import { attachBlockMapping, attachMixamoMapping } from "../config.js"; +import { registerMeshForBlockKey } from "../generators/generators.js"; let flock; @@ -270,6 +271,7 @@ export const flockMesh = { mesh.metadata = { ...(mesh.metadata || {}), shapeType }; mesh.metadata.blockKey = mesh.name; + registerMeshForBlockKey(mesh, mesh.name); if (applyColor) { const colorInput = Array.isArray(color) ? color.flat() : color; @@ -691,6 +693,7 @@ export const flockMesh = { bb.name = modelId; bb.metadata = bb.metadata || {}; bb.metadata.blockKey = blockId; + registerMeshForBlockKey(bb, blockId); //console.log("Model setup", bb.name, bb.metadata.blockKey); bb.isPickable = false; diff --git a/api/shapes.js b/api/shapes.js index f567e1e5..3d241084 100644 --- a/api/shapes.js +++ b/api/shapes.js @@ -1,6 +1,7 @@ import earcut from "earcut"; import Module from "manifold-3d"; import opentype from "opentype.js"; +import { registerMeshForBlockKey } from "../generators/generators.js"; let flock; let manifoldModule = null; @@ -278,6 +279,7 @@ export const flockShapes = { newBox.metadata = newBox.metadata || {}; newBox.metadata.blockKey = blockKey; + registerMeshForBlockKey(newBox, blockKey); // Define and apply the physics shape const boxShape = new flock.BABYLON.PhysicsShapeBox( @@ -351,6 +353,7 @@ export const flockShapes = { newSphere.metadata = newSphere.metadata || {}; newSphere.metadata.blockKey = blockKey; + registerMeshForBlockKey(newSphere, blockKey); // Define and apply the physics shape const sphereShape = new flock.BABYLON.PhysicsShapeSphere( @@ -435,6 +438,7 @@ export const flockShapes = { newCylinder.metadata = newCylinder.metadata || {}; newCylinder.metadata.blockKey = blockKey; + registerMeshForBlockKey(newCylinder, blockKey); // Create and apply physics shape const startPoint = new flock.BABYLON.Vector3(0, -height / 2, 0); @@ -506,6 +510,7 @@ export const flockShapes = { newCapsule.metadata = newCapsule.metadata || {}; newCapsule.metadata.blockKey = blockKey; + registerMeshForBlockKey(newCapsule, blockKey); // Define central point for the capsule const center = flock.BABYLON.Vector3.Zero(); @@ -608,6 +613,7 @@ export const flockShapes = { }); newPlane.metadata.blockKey = blockKey; + registerMeshForBlockKey(newPlane, blockKey); flock.announceMeshReady(newPlane.name, groupName); diff --git a/generators/generators.js b/generators/generators.js index f1763196..5845ea21 100644 --- a/generators/generators.js +++ b/generators/generators.js @@ -4,6 +4,17 @@ import "@blockly/block-plus-minus"; import { FlowGraphLog10Block } from "babylonjs"; export let meshMap = {}; export let meshBlockIdMap = {}; +export let blockToKeyMap = new Map(); +export let blockIdToKeyMap = new Map(); +export let blockKeyMeshesMap = new Map(); + +export function registerMeshForBlockKey(mesh, blockKey) { + if (!blockKey || !mesh) return; + if (!blockKeyMeshesMap.has(blockKey)) { + blockKeyMeshesMap.set(blockKey, new Set()); + } + blockKeyMeshesMap.get(blockKey).add(mesh); +} let uniqueIdCounter = 0; @@ -760,6 +771,8 @@ export function defineGenerators() { const meshId = "ground"; meshMap[meshId] = block; meshBlockIdMap[meshId] = block.id; + blockToKeyMap.set(block, meshId); + blockIdToKeyMap.set(block.id, meshId); let color = javascriptGenerator.valueToCode( block, @@ -804,6 +817,8 @@ export function defineGenerators() { const meshId = "sky"; meshMap[meshId] = block; meshBlockIdMap[meshId] = block.id; + blockToKeyMap.set(block, meshId); + blockIdToKeyMap.set(block.id, meshId); let color = javascriptGenerator.valueToCode( block, @@ -1239,6 +1254,8 @@ export function defineGenerators() { const meshId = `${userVariableName}__${block.id}`; meshMap[block.id] = block; meshBlockIdMap[block.id] = block.id; + blockToKeyMap.set(block, block.id); + blockIdToKeyMap.set(block.id, block.id); let doCode = ""; if (block.getInput("DO")) { @@ -1288,6 +1305,8 @@ export function defineGenerators() { const meshId = `${userVariableName}__${block.id}`; meshMap[block.id] = block; meshBlockIdMap[block.id] = block.id; + blockToKeyMap.set(block, block.id); + blockIdToKeyMap.set(block.id, block.id); // Generate the code for the "do" part (if present) let doCode = ""; @@ -1331,6 +1350,8 @@ export function defineGenerators() { const meshId = `${userVariableName}__${block.id}`; meshMap[block.id] = block; meshBlockIdMap[block.id] = block.id; + blockToKeyMap.set(block, block.id); + blockIdToKeyMap.set(block.id, block.id); //```text // Generate the code for the "do" part (if present) let doCode = ""; @@ -1369,6 +1390,8 @@ export function defineGenerators() { const meshId = `${userVariableName}__${block.id}`; meshMap[block.id] = block; meshBlockIdMap[block.id] = block.id; + blockToKeyMap.set(block, block.id); + blockIdToKeyMap.set(block.id, block.id); // Generate the code for the "do" part (if present) let doCode = ""; @@ -1405,6 +1428,8 @@ export function defineGenerators() { const meshId = `${userVariableName}__${block.id}`; meshMap[block.id] = block; meshBlockIdMap[block.id] = block.id; + blockToKeyMap.set(block, block.id); + blockIdToKeyMap.set(block.id, block.id); // Generate the code for the "do" part (if present) let doCode = ""; @@ -1444,6 +1469,8 @@ export function defineGenerators() { const cloneId = sourceMeshName + "_" + generateUniqueId(); meshMap[cloneId] = block; meshBlockIdMap[cloneId] = block.id; + blockToKeyMap.set(block, cloneId); + blockIdToKeyMap.set(block.id, cloneId); // Generate the code for the "do" part (if present) let doCode = ""; @@ -1496,6 +1523,8 @@ export function defineGenerators() { const meshId = "text_" + generateUniqueId(); meshMap[meshId] = block; meshBlockIdMap[meshId] = block.id; + blockToKeyMap.set(block, meshId); + blockIdToKeyMap.set(block.id, meshId); let doCode = ""; if (block.getInput("DO")) { @@ -1692,6 +1721,8 @@ export function defineGenerators() { meshMap[block.id] = block; meshBlockIdMap[block.id] = block.id; + blockToKeyMap.set(block, block.id); + blockIdToKeyMap.set(block.id, block.id); const doCode = block.getInput("DO") ? javascriptGenerator.statementToCode(block, "DO") || "" @@ -1831,6 +1862,8 @@ export function defineGenerators() { meshMap[meshId] = block; meshBlockIdMap[meshId] = block.id; + blockToKeyMap.set(block, meshId); + blockIdToKeyMap.set(block.id, meshId); // Background block should always request clear-color behaviour return `setSky(${color}, { clear: true });\n`; }; @@ -1852,6 +1885,8 @@ export function defineGenerators() { const wallId = `wall_${generateUniqueId()}`; meshMap[wallId] = block; meshBlockIdMap[wallId] = block.id; + blockToKeyMap.set(block, wallId); + blockIdToKeyMap.set(block.id, wallId); // Directly passing all parameters to the helper function return `${variableName} = newWall(${color}, ${startX}, ${startZ}, ${endX}, ${endZ}, ${yPosition}, "${wallType}", "${wallId}");\n`; }; @@ -2600,6 +2635,8 @@ export function defineGenerators() { const meshId = "ground"; meshMap[meshId] = block; meshBlockIdMap[meshId] = block.id; + blockToKeyMap.set(block, meshId); + blockIdToKeyMap.set(block.id, meshId); return `createMap("${mapName}", ${material});\n`; }; @@ -2788,6 +2825,8 @@ export function defineGenerators() { const meshId = "merged" + "_" + generateUniqueId(); meshMap[meshId] = block; meshBlockIdMap[meshId] = block.id; + blockToKeyMap.set(block, meshId); + blockIdToKeyMap.set(block.id, meshId); // Use helper function to merge the meshes return `${resultVar} = await mergeMeshes("${meshId}", ${meshList});\n`; @@ -2813,6 +2852,8 @@ export function defineGenerators() { const meshId = "subtracted" + "_" + generateUniqueId(); meshMap[meshId] = block; meshBlockIdMap[meshId] = block.id; + blockToKeyMap.set(block, meshId); + blockIdToKeyMap.set(block.id, meshId); // Use helper function to subtract meshes from the base mesh return `${resultVar} = await subtractMeshes("${meshId}", ${baseMesh}, ${meshList});\n`; @@ -2834,6 +2875,8 @@ export function defineGenerators() { const meshId = "intersected" + "_" + generateUniqueId(); meshMap[meshId] = block; meshBlockIdMap[meshId] = block.id; + blockToKeyMap.set(block, meshId); + blockIdToKeyMap.set(block.id, meshId); // Use helper function to intersect the meshes return `${resultVar} = await intersectMeshes("${meshId}", ${meshList});\n`; @@ -2854,6 +2897,8 @@ export function defineGenerators() { const meshId = "hull" + "_" + generateUniqueId(); meshMap[meshId] = block; meshBlockIdMap[meshId] = block.id; + blockToKeyMap.set(block, meshId); + blockIdToKeyMap.set(block.id, meshId); // Use helper function to create the hull return `${resultVar} = await createHull("${meshId}", ${meshList});\n`; @@ -3338,6 +3383,9 @@ export function defineGenerators() { javascriptGenerator.init = function (workspace) { meshMap = {}; meshBlockIdMap = {}; + blockToKeyMap = new Map(); + blockIdToKeyMap = new Map(); + blockKeyMeshesMap = new Map(); console.log("Initializing JavaScript generator..."); if (!javascriptGenerator.nameDB_) { javascriptGenerator.nameDB_ = new Blockly.Names( @@ -3399,6 +3447,9 @@ export function defineGenerators() { javascriptGenerator.init2 = function (workspace) { meshMap = {}; meshBlockIdMap = {}; + blockToKeyMap = new Map(); + blockIdToKeyMap = new Map(); + blockKeyMeshesMap = new Map(); console.log("Initializing JavaScript generator..."); if (!javascriptGenerator.nameDB_) { diff --git a/ui/blockmesh.js b/ui/blockmesh.js index 378941af..6ff9f7b6 100644 --- a/ui/blockmesh.js +++ b/ui/blockmesh.js @@ -1,5 +1,5 @@ import * as Blockly from "blockly"; -import { meshMap, meshBlockIdMap } from "../generators/generators.js"; +import { meshMap, meshBlockIdMap, blockToKeyMap, blockIdToKeyMap, blockKeyMeshesMap } from "../generators/generators.js"; import { flock } from "../flock.js"; import { objectColours } from "../config.js"; import { createMeshOnCanvas } from "./addmeshes.js"; @@ -95,32 +95,36 @@ export function deleteMeshFromBlock(blockId) { }); // Remove mappings + const block = meshMap[blockKey]; + const mappedBlockId = meshBlockIdMap[blockKey]; + if (block) blockToKeyMap.delete(block); + if (mappedBlockId) blockIdToKeyMap.delete(mappedBlockId); + blockKeyMeshesMap.delete(blockKey); delete meshMap[blockKey]; delete meshBlockIdMap[blockKey]; } export function getBlockKeyFromBlock(block) { - return Object.keys(meshMap).find((key) => meshMap[key] === block); + return blockToKeyMap.get(block); } export function getBlockKeyFromBlockID(blockId) { - return Object.keys(meshBlockIdMap).find( - (key) => meshBlockIdMap[key] === blockId, - ); + return blockIdToKeyMap.get(blockId); } export function getMeshFromBlockKey(blockKey) { - return flock.scene?.meshes?.find( - (mesh) => mesh.metadata?.blockKey === blockKey, - ); + const meshes = blockKeyMeshesMap.get(blockKey); + if (!meshes) return undefined; + for (const mesh of meshes) { + if (!mesh.isDisposed()) return mesh; + } + return undefined; } export function getMeshesFromBlockKey(blockKey) { - return ( - flock.scene?.meshes?.filter( - (mesh) => mesh.metadata?.blockKey === blockKey, - ) || [] - ); + const meshes = blockKeyMeshesMap.get(blockKey); + if (!meshes) return []; + return [...meshes].filter((mesh) => !mesh.isDisposed()); } export function getMeshFromBlock(block) {