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 @@
-
+