diff --git a/frontend/dcViewer.js b/frontend/dcViewer.js index d211054..d83f32c 100644 --- a/frontend/dcViewer.js +++ b/frontend/dcViewer.js @@ -215,6 +215,7 @@ let gizmo = null; const transformStore = {}; // modelId -> objectId -> {position, rotation, scale} const modelTypes = {}; // modelId -> normalized type const sceneModels = {}; // modelId -> SceneModel +const pointCloudColorSnapshots = new Map(); // modelId -> [{ layer, colors, workingColors }] let activeDisplayMode = "source"; let activeLightingMode = "ambient"; let activeNavigationAxis = "auto"; @@ -443,6 +444,92 @@ const getBaseEdgesForModel = (modelId) => { const isPointCloudModel = (modelId) => modelTypes[modelId] === "las"; +const getPointCloudLayers = (model) => ( + (model?.layerList || []).filter((layer) => ( + layer?.primitive === "points" && + layer?._state?.colorsBuf?.getData && + layer?._state?.colorsBuf?.setData + )) +); + +const snapshotPointCloudColors = (modelId) => { + const model = sceneModels[modelId] || viewer?.scene?.models?.[modelId]; + if (!model || !isPointCloudModel(modelId) || pointCloudColorSnapshots.has(modelId)) return; + const snapshots = getPointCloudLayers(model) + .map((layer) => { + try { + const colors = layer._state.colorsBuf.getData(); + return colors?.length ? { layer, colors, workingColors: null } : null; + } catch (err) { + console.warn("point cloud color snapshot failed", err); + return null; + } + }) + .filter(Boolean); + if (snapshots.length) { + pointCloudColorSnapshots.set(modelId, snapshots); + } +}; + +const clearPointCloudEntityColorState = (targets) => { + targets.forEach((entity) => { + if (!entity) return; + try { + entity.xrayed = false; + entity.opacity = 1; + entity.edges = false; + (entity.meshes || []).forEach((mesh) => { + mesh._colorizing = false; + }); + if (entity._colorizeUpdated && viewer?.scene?._objectColorizeUpdated) { + viewer.scene._objectColorizeUpdated(entity, false); + entity._colorizeUpdated = false; + } + } catch (err) { + console.warn("point cloud entity reset failed", err); + } + }); +}; + +const writePointCloudColors = (modelId, colorize = null) => { + snapshotPointCloudColors(modelId); + const snapshots = pointCloudColorSnapshots.get(modelId) || []; + const color = colorize + ? colorize.map((v) => Math.max(0, Math.min(255, Math.round(v * 255)))) + : null; + + snapshots.forEach((snapshot) => { + const colorsBuf = snapshot.layer?._state?.colorsBuf; + if (!colorsBuf?.setData) return; + try { + if (!color) { + colorsBuf.setData(snapshot.colors); + return; + } + const original = snapshot.colors; + const working = snapshot.workingColors?.length === original.length + ? snapshot.workingColors + : new Uint8Array(original.length); + for (let i = 0; i < original.length; i += 4) { + working[i] = color[0]; + working[i + 1] = color[1]; + working[i + 2] = color[2]; + working[i + 3] = original[i + 3]; + } + snapshot.workingColors = working; + colorsBuf.setData(working); + } catch (err) { + console.warn("point cloud color restore failed", err); + } + }); +}; + +const applyPointCloudDisplayMode = (modelId, preset, targets) => { + clearPointCloudEntityColorState(targets); + writePointCloudColors(modelId, preset.colorize); + viewer?.scene?.glRedraw?.(); +}; + const applyDisplayModeToModel = (modelId) => { const model = sceneModels[modelId] || viewer?.scene?.models?.[modelId]; if (!model) return; @@ -450,6 +537,11 @@ const applyDisplayModeToModel = (modelId) => { const objects = Object.values(model.objects || {}); const targets = objects.length ? objects : [model]; + if (isPointCloudModel(modelId)) { + applyPointCloudDisplayMode(modelId, preset, targets); + return; + } + targets.forEach((entity) => { if (!entity) return; try { @@ -1599,6 +1691,7 @@ const destroyAllModels = ({ preserveMeta = false } = {}) => { activeModels.length = 0; Object.keys(sceneModels).forEach((key) => delete sceneModels[key]); Object.keys(modelTypes).forEach((key) => delete modelTypes[key]); + pointCloudColorSnapshots.clear(); if (!preserveMeta) { Object.keys(transformStore).forEach((key) => delete transformStore[key]); } @@ -1717,6 +1810,7 @@ const deleteModelById = (modelId) => { activeModels.splice(idx, 1); delete sceneModels[modelId]; delete modelTypes[modelId]; + pointCloudColorSnapshots.delete(modelId); delete transformStore[modelId]; treeModelIds.delete(modelId); if (customTrees.has(modelId)) { @@ -2929,6 +3023,9 @@ const initViewer = async () => { setStatus(`Загружено: ${name || inferredType}`); dropZone.classList.add("hidden"); setLoading(false); + if (inferredType === "las") { + snapshotPointCloudColors(id); + } applyDisplayModeToModel(id); updateNavCubeVisibility(); updatePanelsVisibility(); diff --git a/frontend/index.html b/frontend/index.html index 8c77143..38ca7d8 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -239,6 +239,6 @@ - +