From bc8f70da9cdabd7398badedbd447e91f22a9783b Mon Sep 17 00:00:00 2001 From: CODEX Date: Wed, 24 Jun 2026 19:03:28 +0300 Subject: [PATCH] Fix BIM version model loading --- frontend/dcViewer.js | 47 +++++++++++++++++++++++++++++++++++++++----- frontend/index.html | 2 +- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/frontend/dcViewer.js b/frontend/dcViewer.js index c417787..282eb22 100644 --- a/frontend/dcViewer.js +++ b/frontend/dcViewer.js @@ -319,9 +319,11 @@ let modelVersionHistoryLoading = false; let modelVersionUploading = false; let versionHistoryModal = null; let loadModelAsyncRef = null; +let versionModelCounter = 0; let emptyWheelNavigationRestoreTimer = null; let lastSurfaceDollyFactor = null; let lastSurfaceDollyFactorAt = 0; +const MODEL_LOAD_TIMEOUT_MS = 120000; const GUEST_TOOLBAR_ACTIONS = new Set(["measure", "section", "projection"]); const memoryStore = new Map(); // url -> ArrayBuffer const blobUrls = []; @@ -4700,6 +4702,13 @@ const uploadBuffer = async (bytes, filename, options = {}) => { const getVersionKey = (version) => version?.versionId || `version-${version?.version || "unknown"}`; +const makeVersionModelId = (version) => { + versionModelCounter += 1; + const rawKey = version?.versionId || version?.version || version?.originalFilename || versionModelCounter; + const safeKey = String(rawKey).replace(/[^a-z0-9_-]+/gi, "-").replace(/^-+|-+$/g, "") || "version"; + return `model-version-${safeKey}-${versionModelCounter}`; +}; + const getModelVersionIdentity = (entry = getPrimaryModelEntry()) => { const meta = entry?.meta || {}; const direct = { @@ -4896,13 +4905,18 @@ const openModelVersion = async (version) => { if (settings?.viewerSettings) { meta.viewerSettings = settings.viewerSettings; } + const previousModelId = previousEntry?.model?.id || null; + const nextModelId = makeVersionModelId(version); + if (previousModelId && transformStore[previousModelId]) { + transformStore[nextModelId] = safeClone(transformStore[previousModelId]); + } await loadModelAsyncRef({ type: viewerType, url: viewerSrc, name: version.originalFilename || previousEntry?.name || "model", replace: true, preserveMeta: true, - id: previousEntry?.model?.id || "model", + id: nextModelId, meta, }); modelVersionHistory = { @@ -6093,6 +6107,7 @@ const initViewer = async () => { isMemory, name, replace, + requestedId: forcedId || null, url, }); const xktDataSource = inferredType === "xkt" @@ -6112,7 +6127,7 @@ const initViewer = async () => { dataSource: xktDataSource || gltfDataSource || lasDataSource || stlDataSource || undefined, }); - const id = forcedId || (replace ? "model" : `model-${++modelCounter}`); + const requestedId = forcedId || (replace ? "model" : `model-${++modelCounter}`); let srcToLoad = url; if (inferredType === "obj" && typeof url === "string" && url.startsWith("memory:") && memoryStore.has(url)) { const blobUrl = URL.createObjectURL(new Blob([memoryStore.get(url)], { type: "text/plain" })); @@ -6120,7 +6135,7 @@ const initViewer = async () => { srcToLoad = blobUrl; } const loadOpts = { - id, + id: requestedId, src: srcToLoad, edges: edges ?? (inferredType === "gltf" || inferredType === "las" ? false : toggleEdges.checked), }; @@ -6129,12 +6144,17 @@ const initViewer = async () => { if (inferredType === "stl") loadOpts.smoothNormals = true; const currentModel = currentLoader.load(loadOpts); + const id = currentModel?.id || requestedId; + if (id !== requestedId && transformStore[requestedId] && !transformStore[id]) { + transformStore[id] = safeClone(transformStore[requestedId]); + } sceneModels[id] = currentModel; modelTypes[id] = inferredType; currentModel.on("loaded", () => { reportViewerEvent("model-loaded", { id, + requestedId, inferredType, name, objectCount: Object.keys(viewer.scene.objects || {}).length, @@ -6180,6 +6200,7 @@ const initViewer = async () => { currentModel.on("error", (err) => { reportViewerEvent("model-error", { id, + requestedId, inferredType, name, error: err?.message || String(err), @@ -6225,8 +6246,24 @@ const initViewer = async () => { reject(new Error("Не удалось инициировать загрузку модели")); return; } - model.on("loaded", () => resolve(model)); - model.on("error", (err) => reject(new Error(err?.message || err))); + let settled = false; + let timer = null; + const settle = (handler, value) => { + if (settled) return; + settled = true; + if (timer) window.clearTimeout(timer); + handler(value); + }; + model.on("loaded", () => settle(resolve, model)); + model.on("error", (err) => settle(reject, new Error(err?.message || err))); + if (!settled) { + timer = window.setTimeout(() => { + setLoading(false); + updateNavCubeVisibility(); + updatePanelsVisibility(); + settle(reject, new Error("Загрузка модели заняла слишком много времени")); + }, MODEL_LOAD_TIMEOUT_MS); + } }); loadModelAsyncRef = loadModelAsync; diff --git a/frontend/index.html b/frontend/index.html index f802211..610794f 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -697,6 +697,6 @@ - +