Skip to content

Commit c1225d9

Browse files
Reload mesh with opacity (#8622)
### URL of deployed dev instance (used for testing): - https://___.webknossos.xyz This PR improves the reloading of meshes by keeping their previous opacity. It also refactors the code by splitting the mesh_saga into three modules. ### Steps to test: - Open an annotation and select some segments. Load their meshes. - Now change the opacity of a mesh and reload it. The opacity should not change upon reloading. - Do this for precomputed and ad-hoc meshes - As the mesh_saga was refactored, it makes sense to test the usual mesh functionality: downloading mesh(es), reloading them, removing them, toggling the visibility, changing their color, etc ### Issues: - fixes #8504
1 parent cf115fd commit c1225d9

17 files changed

+1456
-1359
lines changed

CHANGELOG.unreleased.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
1111
[Commits](https://github.com/scalableminds/webknossos/compare/25.06.1...HEAD)
1212

1313
### Added
14+
- Meshes are now reloaded using their previous opacity value. [#8622](https://github.com/scalableminds/webknossos/pull/8622)
1415

1516
### Changed
1617

frontend/javascripts/components/color_picker.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { InputNumber, Popover } from "antd";
22
import useThrottledCallback from "beautiful-react-hooks/useThrottledCallback";
33
import * as Utils from "libs/utils";
4-
import { useRef, useState } from "react";
4+
import { useEffect, useRef, useState } from "react";
55
import type { CSSProperties } from "react";
66
import { HexColorInput, HexColorPicker, type RgbaColor, RgbaColorPicker } from "react-colorful";
77
import type { Vector3, Vector4 } from "viewer/constants";
@@ -36,14 +36,20 @@ const ThrottledColorPicker = ({
3636
}) => {
3737
const [value, localSetValue] = useState(color);
3838
const throttledSetValue = useThrottledCallback(onChange, [onChange], 20);
39+
40+
// Sync local state when external color prop changes
41+
useEffect(() => {
42+
localSetValue(color);
43+
}, [color]);
44+
3945
const setValue = (newValue: string) => {
4046
localSetValue(newValue);
4147
throttledSetValue(newValue);
4248
};
4349
return (
4450
<div style={{ marginRight: 10 }}>
4551
<HexColorPicker color={value} onChange={setValue} />
46-
<HexColorInput color={color} onChange={setValue} style={inputStyle} />
52+
<HexColorInput color={value} onChange={setValue} style={inputStyle} />
4753
</div>
4854
);
4955
};
@@ -90,6 +96,12 @@ const ThrottledRGBAColorPicker = ({
9096
}) => {
9197
const [value, localSetValue] = useState(color);
9298
const throttledSetValue = useThrottledCallback(onChangeColor, [onChangeColor, value], 20);
99+
100+
// Sync local state when external color prop changes
101+
useEffect(() => {
102+
localSetValue(color);
103+
}, [color]);
104+
93105
const setValue = (newValue: RgbaColor) => {
94106
localSetValue(newValue);
95107
throttledSetValue(newValue);

frontend/javascripts/viewer/api/api_latest.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2521,6 +2521,7 @@ class DataApi {
25212521
seedPosition,
25222522
seedAdditionalCoordinates,
25232523
meshFileName,
2524+
undefined,
25242525
effectiveLayerName,
25252526
),
25262527
);

frontend/javascripts/viewer/controller/mesh_helpers.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1+
import type { meshApi } from "admin/rest_api";
2+
import { V3 } from "libs/mjs";
13
import _ from "lodash";
24
import type * as THREE from "three";
5+
import type { Vector3 } from "viewer/constants";
36

47
export type BufferGeometryWithInfo = THREE.BufferGeometry & {
58
vertexSegmentMapping?: VertexSegmentMapping;
@@ -78,3 +81,12 @@ export class VertexSegmentMapping {
7881
return _.sortedIndexOf(this.unmappedSegmentIds, segmentId) !== -1;
7982
}
8083
}
84+
85+
export function sortByDistanceTo(
86+
availableChunks: Vector3[] | meshApi.MeshChunk[] | null | undefined,
87+
seedPosition: Vector3,
88+
) {
89+
return _.sortBy(availableChunks, (chunk: Vector3 | meshApi.MeshChunk) =>
90+
V3.length(V3.sub(seedPosition, "position" in chunk ? chunk.position : chunk)),
91+
) as Array<Vector3> | Array<meshApi.MeshChunk>;
92+
}

frontend/javascripts/viewer/controller/segment_mesh_controller.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@ import {
1313
getActiveSegmentationTracing,
1414
getSegmentColorAsHSLA,
1515
} from "viewer/model/accessors/volumetracing_accessor";
16-
import { NO_LOD_MESH_INDEX } from "viewer/model/sagas/mesh_saga";
1716
import Store from "viewer/store";
1817

1918
import { computeBvhAsync } from "libs/compute_bvh_async";
2019
import type { BufferAttribute } from "three";
20+
import Constants from "viewer/constants";
21+
import { NO_LOD_MESH_INDEX } from "viewer/model/sagas/meshes/common_mesh_saga";
2122
import type { BufferGeometryWithInfo } from "./mesh_helpers";
2223

2324
// Add the raycast function. Assumes the BVH is available on
@@ -110,6 +111,7 @@ export default class SegmentMeshController {
110111
vertices: Float32Array,
111112
segmentId: number,
112113
layerName: string,
114+
opacity: number | undefined,
113115
additionalCoordinates?: AdditionalCoordinate[] | undefined | null,
114116
): Promise<void> {
115117
// Currently, this function is only used by ad hoc meshing.
@@ -129,6 +131,7 @@ export default class SegmentMeshController {
129131
NO_LOD_MESH_INDEX,
130132
layerName,
131133
additionalCoordinates,
134+
opacity,
132135
false,
133136
);
134137
}
@@ -137,6 +140,7 @@ export default class SegmentMeshController {
137140
segmentId: number,
138141
layerName: string,
139142
geometry: BufferGeometryWithInfo,
143+
opacity: number | undefined,
140144
isMerged: boolean,
141145
): MeshSceneNode {
142146
const color = this.getColorObjectForSegment(segmentId, layerName);
@@ -169,7 +173,7 @@ export default class SegmentMeshController {
169173
tweenAnimation
170174
.to(
171175
{
172-
opacity: 1,
176+
opacity: opacity ?? Constants.DEFAULT_MESH_OPACITY,
173177
},
174178
100,
175179
)
@@ -189,6 +193,7 @@ export default class SegmentMeshController {
189193
lod: number,
190194
layerName: string,
191195
additionalCoordinates: AdditionalCoordinate[] | null | undefined,
196+
opacity: number | undefined,
192197
isMerged: boolean,
193198
): void {
194199
const additionalCoordinatesString = getAdditionalCoordinatesAsString(additionalCoordinates);
@@ -232,7 +237,7 @@ export default class SegmentMeshController {
232237
];
233238
targetGroup.scale.copy(new THREE.Vector3(...adaptedScale));
234239
}
235-
const meshChunk = this.constructMesh(segmentId, layerName, geometry, isMerged);
240+
const meshChunk = this.constructMesh(segmentId, layerName, geometry, opacity, isMerged);
236241

237242
const group = new THREE.Group() as SceneGroupForMeshes;
238243
group.add(meshChunk);

frontend/javascripts/viewer/model/actions/annotation_actions.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import type {
1212
} from "types/api_types";
1313
import type { AdditionalCoordinate } from "types/api_types";
1414
import type { Vector3 } from "viewer/constants";
15+
import Constants from "viewer/constants";
1516
import type {
1617
Annotation,
1718
MappingType,
@@ -333,6 +334,7 @@ export const addAdHocMeshAction = (
333334
seedAdditionalCoordinates: AdditionalCoordinate[] | undefined | null,
334335
mappingName: string | null | undefined,
335336
mappingType: MappingType | null | undefined,
337+
opacity: number | undefined,
336338
) =>
337339
({
338340
type: "ADD_AD_HOC_MESH",
@@ -342,6 +344,7 @@ export const addAdHocMeshAction = (
342344
seedAdditionalCoordinates,
343345
mappingName,
344346
mappingType,
347+
opacity: opacity ?? Constants.DEFAULT_MESH_OPACITY,
345348
}) as const;
346349

347350
export const addPrecomputedMeshAction = (
@@ -351,6 +354,7 @@ export const addPrecomputedMeshAction = (
351354
seedAdditionalCoordinates: AdditionalCoordinate[] | undefined | null,
352355
meshFileName: string,
353356
mappingName: string | null | undefined,
357+
opacity: number | undefined,
354358
) =>
355359
({
356360
type: "ADD_PRECOMPUTED_MESH",
@@ -360,6 +364,7 @@ export const addPrecomputedMeshAction = (
360364
seedAdditionalCoordinates,
361365
meshFileName,
362366
mappingName,
367+
opacity: opacity ?? Constants.DEFAULT_MESH_OPACITY,
363368
}) as const;
364369

365370
export const setOthersMayEditForAnnotationAction = (othersMayEdit: boolean) =>

frontend/javascripts/viewer/model/actions/segmentation_actions.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export type AdHocMeshInfo = {
77
mappingType: MappingType | null | undefined;
88
useDataStore?: boolean | null | undefined;
99
preferredQuality?: number | null | undefined;
10+
opacity?: number | undefined;
1011
};
1112
export type LoadAdHocMeshAction = ReturnType<typeof loadAdHocMeshAction>;
1213
export type LoadPrecomputedMeshAction = ReturnType<typeof loadPrecomputedMeshAction>;
@@ -34,6 +35,7 @@ export const loadPrecomputedMeshAction = (
3435
seedPosition: Vector3,
3536
seedAdditionalCoordinates: AdditionalCoordinate[] | undefined | null,
3637
meshFileName: string,
38+
opacity: number | undefined,
3739
layerName?: string | undefined,
3840
) =>
3941
({
@@ -42,5 +44,6 @@ export const loadPrecomputedMeshAction = (
4244
seedPosition,
4345
seedAdditionalCoordinates,
4446
meshFileName,
47+
opacity,
4548
layerName,
4649
}) as const;

frontend/javascripts/viewer/model/reducers/annotation_reducer.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { V3 } from "libs/mjs";
33
import * as Utils from "libs/utils";
44
import _ from "lodash";
55
import type { AdditionalCoordinate } from "types/api_types";
6-
import Constants from "viewer/constants";
76
import { maybeGetSomeTracing } from "viewer/model/accessors/tracing_accessor";
87
import { getDisplayedDataExtentInPlaneMode } from "viewer/model/accessors/view_mode_accessor";
98
import type { Action } from "viewer/model/actions/actions";
@@ -344,7 +343,7 @@ function AnnotationReducer(state: WebknossosState, action: Action): WebknossosSt
344343
});
345344
}
346345

347-
// Mesh information is stored in three places: the state in the store, segment_view_controller and within the mesh_saga.
346+
// Mesh information is stored in three places: the state in the store, segment_view_controller and within the mesh sagas.
348347
case "ADD_AD_HOC_MESH": {
349348
const {
350349
layerName,
@@ -353,6 +352,7 @@ function AnnotationReducer(state: WebknossosState, action: Action): WebknossosSt
353352
seedAdditionalCoordinates,
354353
mappingName,
355354
mappingType,
355+
opacity,
356356
} = action;
357357
const meshInfo: MeshInformation = {
358358
segmentId: segmentId,
@@ -361,7 +361,7 @@ function AnnotationReducer(state: WebknossosState, action: Action): WebknossosSt
361361
isLoading: false,
362362
isVisible: true,
363363
isPrecomputed: false,
364-
opacity: Constants.DEFAULT_MESH_OPACITY,
364+
opacity,
365365
mappingName,
366366
mappingType,
367367
};
@@ -398,6 +398,7 @@ function AnnotationReducer(state: WebknossosState, action: Action): WebknossosSt
398398
seedAdditionalCoordinates,
399399
meshFileName,
400400
mappingName,
401+
opacity,
401402
} = action;
402403
const meshInfo: MeshInformation = {
403404
segmentId: segmentId,
@@ -406,7 +407,7 @@ function AnnotationReducer(state: WebknossosState, action: Action): WebknossosSt
406407
isLoading: false,
407408
isVisible: true,
408409
isPrecomputed: true,
409-
opacity: Constants.DEFAULT_MESH_OPACITY,
410+
opacity,
410411
meshFileName,
411412
mappingName,
412413
};

0 commit comments

Comments
 (0)