Fix BIM share transform persistence

This commit is contained in:
CODEX 2026-07-02 19:31:59 +03:00
parent abf899fefd
commit ef7179dc6c
2 changed files with 75 additions and 13 deletions

View File

@ -2301,6 +2301,7 @@ const getShareableModelPayload = () => {
src,
type: normalizeType(null, src) || entry.meta?.artifactType || entry.type,
versionId: entry.meta?.versionId || entry.meta?.conversion?.versionId || null,
viewerSettings: buildModelViewerSettings(),
versions: Array.isArray(entry.meta?.versions) ? entry.meta.versions : [],
};
};
@ -4077,6 +4078,37 @@ const applyStoredTransformsToModel = (modelId) => {
return applied;
};
const getViewerSettingsTransformMap = (settings) => {
const transforms = settings?.viewerState?.transforms;
return transforms && typeof transforms === "object" && !Array.isArray(transforms)
? transforms
: null;
};
const ensureLoadedModelTransformAlias = (modelId, settings = null) => {
if (!modelId) return false;
const currentTransforms = transformStore[modelId];
if (currentTransforms && Object.keys(currentTransforms).length) {
return false;
}
const transforms = getViewerSettingsTransformMap(settings) || transformStore;
const preferredIds = Array.isArray(settings?.viewerState?.models)
? settings.viewerState.models.map((item) => item?.id).filter(Boolean)
: [];
const candidateIds = [...preferredIds, ...Object.keys(transforms || {})]
.filter((id, index, items) => id && id !== modelId && items.indexOf(id) === index);
const sourceId = candidateIds.find((id) => {
const value = transformStore[id] || transforms?.[id];
return value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length;
});
if (!sourceId) return false;
transformStore[modelId] = safeClone(transformStore[sourceId] || transforms[sourceId]);
return true;
};
const getModelIdFromNode = (node) => {
const title = (node?.title || node?.text || node?.name || "").trim();
const firstObject = (node && collectObjectIds(node)[0]) || null;
@ -5532,9 +5564,7 @@ const snapshotVec = (v) => {
return null;
};
const buildProjectSnapshot = async (name) => {
const models = [];
const buildActiveTransformsSnapshot = () => {
const transformsCopy = safeClone(transformStore);
activeModels.forEach(({ model }) => {
if (!model || !model.id) return;
@ -5547,6 +5577,18 @@ const buildProjectSnapshot = async (name) => {
};
}
});
return transformsCopy;
};
const buildActiveModelStateSnapshot = () => activeModels.map((entry) => ({
id: entry.model?.id,
visible: entry.model?.visible !== false
}));
const buildProjectSnapshot = async (name) => {
const models = [];
const transformsCopy = buildActiveTransformsSnapshot();
for (const entry of activeModels) {
const model = entry.model;
@ -5637,10 +5679,7 @@ const buildProjectSnapshot = async (name) => {
ids: lastSelection.ids || [],
modelId: lastSelection.modelId || null,
},
models: activeModels.map((entry) => ({
id: entry.model?.id,
visible: entry.model?.visible !== false
})),
models: buildActiveModelStateSnapshot(),
transforms: transformsCopy
} : {};
@ -5704,6 +5743,8 @@ const buildModelViewerSettings = () => ({
lightingMode: activeLightingMode,
navigationAxis: activeNavigationAxis,
zoomSpeed: activeZoomSpeed,
models: buildActiveModelStateSnapshot(),
transforms: buildActiveTransformsSnapshot(),
lod: (() => {
const entry = getPrimaryModelEntry();
const selected = getSelectedLodRecord(entry);
@ -5885,7 +5926,10 @@ const applyViewerState = (state) => {
if (state.transforms && typeof state.transforms === "object") {
Object.keys(transformStore).forEach((k) => delete transformStore[k]);
Object.assign(transformStore, safeClone(state.transforms));
Object.keys(sceneModels).forEach((mid) => applyStoredTransformsToModel(mid));
Object.keys(sceneModels).forEach((mid) => {
ensureLoadedModelTransformAlias(mid, { viewerState: state });
applyStoredTransformsToModel(mid);
});
}
if (Array.isArray(state.models)) {
state.models.forEach((m) => {
@ -6445,6 +6489,7 @@ const initViewer = async () => {
}
sceneModels[id] = currentModel;
modelTypes[id] = inferredType;
ensureLoadedModelTransformAlias(id, modelSettings);
currentModel.on("loaded", () => {
reportViewerEvent("model-loaded", {
@ -6455,8 +6500,14 @@ const initViewer = async () => {
objectCount: Object.keys(viewer.scene.objects || {}).length,
src: srcToLoad,
});
setTimeout(() => applyStoredTransformsToModel(id), 0);
setTimeout(() => applyStoredTransformsToModel(id), 60);
setTimeout(() => {
ensureLoadedModelTransformAlias(id, modelSettings);
applyStoredTransformsToModel(id);
}, 0);
setTimeout(() => {
ensureLoadedModelTransformAlias(id, modelSettings);
applyStoredTransformsToModel(id);
}, 60);
const hasModelCamera = !!modelSettings?.viewerState?.camera;
if (!isLoadingSavedProject && !hasModelCamera) {
viewer.cameraFlight.flyTo({ aabb: viewer.scene.getAABB({}) });
@ -6520,6 +6571,7 @@ const initViewer = async () => {
refreshModelLods({ silent: true }).catch(() => {});
// если есть сохранённые трансформы для модели — применим
ensureLoadedModelTransformAlias(id, modelSettings);
applyStoredTransformsToModel(id);
return currentModel;
} catch (e) {
@ -6600,6 +6652,9 @@ const initViewer = async () => {
clearShareStartupPending();
let settingsSrc = sharedViewer?.settingsSrc || params.get("settingsSrc") || params.get("sourceSrc") || params.get("originalSrc") || src;
const sharedViewerSettings = sharedViewer?.viewerSettings && typeof sharedViewer.viewerSettings === "object"
? sharedViewer.viewerSettings
: null;
const applyQueryViewerOverrides = () => {
if (params.has("displayMode")) {
activeDisplayMode = params.get("displayMode") || "source";
@ -6637,11 +6692,11 @@ const initViewer = async () => {
selectedLod: sharedViewer?.selectedLod || null,
versions: Array.isArray(sharedViewer?.versions) ? sharedViewer.versions : [],
shareToken: startupSharePayload?.share?.token || getShareTokenFromPath(),
viewerSettings: null,
viewerSettings: sharedViewerSettings,
};
const applyFetchedStartupSettings = (settingsResponse) => {
const modelSettings = settingsResponse?.viewerSettings || null;
const modelSettings = sharedViewerSettings || settingsResponse?.viewerSettings || null;
settingsSrc = settingsResponse?.sourceSrc || settingsSrc;
reportViewerEvent("startup-settings-loaded", {
settingsSrc,
@ -6683,7 +6738,9 @@ const initViewer = async () => {
if (params.has("edges")) {
startupMeta.viewerSettings = {
...(startupMeta.viewerSettings || {}),
viewerState: {
...(startupMeta.viewerSettings?.viewerState || {}),
edges: params.get("edges") !== "false"
}
};
@ -6694,7 +6751,7 @@ const initViewer = async () => {
name,
type,
settingsSrc,
hasViewerSettings: false,
hasViewerSettings: !!sharedViewerSettings,
});
loadModel({
type,

View File

@ -1973,6 +1973,9 @@ const handleCreateShare = async (req, res) => {
const name = typeof payload?.name === "string" && payload.name.trim()
? payload.name.trim().slice(0, 240)
: path.basename(src);
const viewerSettings = payload?.viewerSettings && typeof payload.viewerSettings === "object" && !Array.isArray(payload.viewerSettings)
? payload.viewerSettings
: null;
const share = {
token,
access: "public-readonly",
@ -1984,6 +1987,7 @@ const handleCreateShare = async (req, res) => {
projectId: identity.projectId,
versionId: typeof payload?.versionId === "string" ? payload.versionId : null,
selectedLod: stringOrNull(payload?.selectedLod),
viewerSettings,
createdAt: now,
createdBy: session?.user ? {
id: session.user.id || null,
@ -2073,6 +2077,7 @@ const handleGetShare = async (req, res, token) => {
projectId: share.projectId || null,
versionId: share.versionId || versionHistory?.currentVersionId || null,
selectedLod: share.selectedLod || null,
viewerSettings: share.viewerSettings || null,
versions: versionHistory?.versions || []
},
share: {