Skip to content

Reload mesh with opacity #8622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 4, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions frontend/javascripts/components/color_picker.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { InputNumber, Popover } from "antd";
import useThrottledCallback from "beautiful-react-hooks/useThrottledCallback";
import * as Utils from "libs/utils";
import { useRef, useState } from "react";
import { useEffect, useRef, useState } from "react";
import type { CSSProperties } from "react";
import { HexColorInput, HexColorPicker, type RgbaColor, RgbaColorPicker } from "react-colorful";
import type { Vector3, Vector4 } from "viewer/constants";
Expand Down Expand Up @@ -36,14 +36,20 @@ const ThrottledColorPicker = ({
}) => {
const [value, localSetValue] = useState(color);
const throttledSetValue = useThrottledCallback(onChange, [onChange], 20);

// Sync local state when external color prop changes
useEffect(() => {
localSetValue(color);
}, [color]);

const setValue = (newValue: string) => {
localSetValue(newValue);
throttledSetValue(newValue);
};
return (
<div style={{ marginRight: 10 }}>
<HexColorPicker color={value} onChange={setValue} />
<HexColorInput color={color} onChange={setValue} style={inputStyle} />
<HexColorInput color={value} onChange={setValue} style={inputStyle} />
</div>
);
};
Expand Down Expand Up @@ -90,6 +96,12 @@ const ThrottledRGBAColorPicker = ({
}) => {
const [value, localSetValue] = useState(color);
const throttledSetValue = useThrottledCallback(onChangeColor, [onChangeColor, value], 20);

// Sync local state when external color prop changes
useEffect(() => {
localSetValue(color);
}, [color]);

const setValue = (newValue: RgbaColor) => {
localSetValue(newValue);
throttledSetValue(newValue);
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/viewer/api/api_latest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2425,6 +2425,7 @@ class DataApi {
seedPosition,
seedAdditionalCoordinates,
meshFileName,
Constants.DEFAULT_MESH_OPACITY,
effectiveLayerName,
),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ export default class SegmentMeshController {
vertices: Float32Array,
segmentId: number,
layerName: string,
opacity: number,
additionalCoordinates?: AdditionalCoordinate[] | undefined | null,
): Promise<void> {
// Currently, this function is only used by ad hoc meshing.
Expand All @@ -129,6 +130,7 @@ export default class SegmentMeshController {
NO_LOD_MESH_INDEX,
layerName,
additionalCoordinates,
opacity,
false,
);
}
Expand All @@ -137,6 +139,7 @@ export default class SegmentMeshController {
segmentId: number,
layerName: string,
geometry: BufferGeometryWithInfo,
opacity: number,
isMerged: boolean,
): MeshSceneNode {
const color = this.getColorObjectForSegment(segmentId, layerName);
Expand Down Expand Up @@ -169,7 +172,7 @@ export default class SegmentMeshController {
tweenAnimation
.to(
{
opacity: 1,
opacity,
},
100,
)
Expand All @@ -189,6 +192,7 @@ export default class SegmentMeshController {
lod: number,
layerName: string,
additionalCoordinates: AdditionalCoordinate[] | null | undefined,
opacity: number,
isMerged: boolean,
): void {
const additionalCoordinatesString = getAdditionalCoordinatesAsString(additionalCoordinates);
Expand Down Expand Up @@ -232,7 +236,7 @@ export default class SegmentMeshController {
];
targetGroup.scale.copy(new THREE.Vector3(...adaptedScale));
}
const meshChunk = this.constructMesh(segmentId, layerName, geometry, isMerged);
const meshChunk = this.constructMesh(segmentId, layerName, geometry, opacity, isMerged);

const group = new THREE.Group() as SceneGroupForMeshes;
group.add(meshChunk);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
} from "types/api_types";
import type { AdditionalCoordinate } from "types/api_types";
import type { Vector3 } from "viewer/constants";
import Constants from "viewer/constants";
import type {
Annotation,
MappingType,
Expand Down Expand Up @@ -333,6 +334,7 @@ export const addAdHocMeshAction = (
seedAdditionalCoordinates: AdditionalCoordinate[] | undefined | null,
mappingName: string | null | undefined,
mappingType: MappingType | null | undefined,
opacity: number = Constants.DEFAULT_MESH_OPACITY,
) =>
({
type: "ADD_AD_HOC_MESH",
Expand All @@ -342,6 +344,7 @@ export const addAdHocMeshAction = (
seedAdditionalCoordinates,
mappingName,
mappingType,
opacity,
}) as const;

export const addPrecomputedMeshAction = (
Expand All @@ -351,6 +354,7 @@ export const addPrecomputedMeshAction = (
seedAdditionalCoordinates: AdditionalCoordinate[] | undefined | null,
meshFileName: string,
mappingName: string | null | undefined,
opacity: number,
) =>
({
type: "ADD_PRECOMPUTED_MESH",
Expand All @@ -360,6 +364,7 @@ export const addPrecomputedMeshAction = (
seedAdditionalCoordinates,
meshFileName,
mappingName,
opacity,
}) as const;

export const setOthersMayEditForAnnotationAction = (othersMayEdit: boolean) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export type AdHocMeshInfo = {
mappingType: MappingType | null | undefined;
useDataStore?: boolean | null | undefined;
preferredQuality?: number | null | undefined;
opacity?: number | null | undefined;
};
export type LoadAdHocMeshAction = ReturnType<typeof loadAdHocMeshAction>;
export type LoadPrecomputedMeshAction = ReturnType<typeof loadPrecomputedMeshAction>;
Expand Down Expand Up @@ -34,6 +35,7 @@ export const loadPrecomputedMeshAction = (
seedPosition: Vector3,
seedAdditionalCoordinates: AdditionalCoordinate[] | undefined | null,
meshFileName: string,
opacity: number,
layerName?: string | undefined,
) =>
({
Expand All @@ -42,5 +44,6 @@ export const loadPrecomputedMeshAction = (
seedPosition,
seedAdditionalCoordinates,
meshFileName,
opacity,
layerName,
}) as const;
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ function AnnotationReducer(state: WebknossosState, action: Action): WebknossosSt
seedAdditionalCoordinates,
mappingName,
mappingType,
opacity,
} = action;
const meshInfo: MeshInformation = {
segmentId: segmentId,
Expand All @@ -361,7 +362,7 @@ function AnnotationReducer(state: WebknossosState, action: Action): WebknossosSt
isLoading: false,
isVisible: true,
isPrecomputed: false,
opacity: Constants.DEFAULT_MESH_OPACITY,
opacity,
mappingName,
mappingType,
};
Expand Down Expand Up @@ -398,6 +399,7 @@ function AnnotationReducer(state: WebknossosState, action: Action): WebknossosSt
seedAdditionalCoordinates,
meshFileName,
mappingName,
opacity,
} = action;
const meshInfo: MeshInformation = {
segmentId: segmentId,
Expand All @@ -406,7 +408,7 @@ function AnnotationReducer(state: WebknossosState, action: Action): WebknossosSt
isLoading: false,
isVisible: true,
isPrecomputed: true,
opacity: Constants.DEFAULT_MESH_OPACITY,
opacity: opacity || Constants.DEFAULT_MESH_OPACITY,
meshFileName,
mappingName,
};
Expand Down
30 changes: 26 additions & 4 deletions frontend/javascripts/viewer/model/sagas/mesh_saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import type { APIDataset, APIMeshFileInfo, APISegmentationLayer } from "types/ap
import type { AdditionalCoordinate } from "types/api_types";
import { WkDevFlags } from "viewer/api/wk_dev";
import type { Vector3, Vector4 } from "viewer/constants";
import { MappingStatusEnum } from "viewer/constants";
import Constants, { MappingStatusEnum } from "viewer/constants";
import CustomLOD from "viewer/controller/custom_lod";
import {
type BufferGeometryWithInfo,
Expand Down Expand Up @@ -343,7 +343,7 @@ function* loadFullAdHocMesh(
removeExistingMesh: boolean,
): Saga<void> {
let isInitialRequest = true;
const { mappingName, mappingType } = meshExtraInfo;
const { mappingName, mappingType, opacity } = meshExtraInfo;
const clippedPosition = clipPositionToCubeBoundary(position, zoomStep, magInfo);
yield* put(
addAdHocMeshAction(
Expand All @@ -353,6 +353,7 @@ function* loadFullAdHocMesh(
additionalCoordinates,
mappingName,
mappingType,
opacity || Constants.DEFAULT_MESH_OPACITY,
),
);
yield* put(startedLoadingMeshAction(layer.name, segmentId));
Expand Down Expand Up @@ -527,6 +528,8 @@ function* maybeLoadMeshChunk(
segmentMeshController.removeMeshById(segmentId, layer.name);
}

const opacity = meshExtraInfo.opacity || Constants.DEFAULT_MESH_OPACITY;

// We await addMeshFromVerticesAsync here, because the mesh saga will remove
// an ad-hoc loaded mesh immediately if it was "empty". Since the check is
// done by looking at the scene, we await the population of the scene.
Expand All @@ -538,6 +541,7 @@ function* maybeLoadMeshChunk(
vertices,
segmentId,
layer.name,
opacity,
additionalCoordinates,
);
return neighbors.map((neighbor) =>
Expand Down Expand Up @@ -621,6 +625,7 @@ function* refreshMesh(action: RefreshMeshAction): Saga<void> {
meshInfo.seedPosition,
meshInfo.seedAdditionalCoordinates,
meshInfo.meshFileName,
meshInfo.opacity,
layerName,
),
);
Expand All @@ -630,7 +635,14 @@ function* refreshMesh(action: RefreshMeshAction): Saga<void> {
if (threeDMap == null) {
return;
}
yield* call(_refreshMeshWithMap, segmentId, threeDMap, layerName, additionalCoordinates);
yield* call(
_refreshMeshWithMap,
segmentId,
threeDMap,
layerName,
additionalCoordinates,
meshInfo.opacity,
);
}
}

Expand All @@ -639,6 +651,7 @@ function* _refreshMeshWithMap(
threeDMap: ThreeDMap<boolean>,
layerName: string,
additionalCoordinates: AdditionalCoordinate[] | null,
opacity: number = Constants.DEFAULT_MESH_OPACITY,
): Saga<void> {
const meshInfo = yield* select((state) =>
getMeshInfoForSegment(state, additionalCoordinates, layerName, segmentId),
Expand Down Expand Up @@ -679,6 +692,7 @@ function* _refreshMeshWithMap(
{
mappingName,
mappingType,
opacity,
},
);
shouldBeRemoved = false;
Expand Down Expand Up @@ -745,7 +759,8 @@ function* maybeFetchMeshFiles(action: MaybeFetchMeshFilesAction): Saga<void> {
}

function* loadPrecomputedMesh(action: LoadPrecomputedMeshAction) {
const { segmentId, seedPosition, seedAdditionalCoordinates, meshFileName, layerName } = action;
const { segmentId, seedPosition, seedAdditionalCoordinates, meshFileName, layerName, opacity } =
action;
const layer = yield* select((state) =>
layerName != null
? getSegmentationLayerByName(state.dataset, layerName)
Expand All @@ -768,6 +783,7 @@ function* loadPrecomputedMesh(action: LoadPrecomputedMeshAction) {
seedAdditionalCoordinates,
meshFileName,
layer,
opacity || Constants.DEFAULT_MESH_OPACITY,
),
cancel: take(
((otherAction: Action) =>
Expand All @@ -786,6 +802,7 @@ function* loadPrecomputedMeshForSegmentId(
seedAdditionalCoordinates: AdditionalCoordinate[] | undefined | null,
meshFileName: string,
segmentationLayer: APISegmentationLayer,
opacity: number,
): Saga<void> {
const layerName = segmentationLayer.name;
const mappingName = yield* call(getMappingName, segmentationLayer);
Expand All @@ -797,6 +814,7 @@ function* loadPrecomputedMeshForSegmentId(
seedAdditionalCoordinates,
meshFileName,
mappingName,
opacity,
),
);
yield* put(startedLoadingMeshAction(layerName, segmentId));
Expand Down Expand Up @@ -864,6 +882,7 @@ function* loadPrecomputedMeshForSegmentId(
(lod: number) => extractScaleFromMatrix(lods[lod].transform),
chunkScale,
additionalCoordinates,
opacity,
);
}

Expand Down Expand Up @@ -972,6 +991,7 @@ function* loadPrecomputedMeshesInChunksForLod(
getGlobalScale: (lod: number) => Vector3 | null,
chunkScale: Vector3 | null,
additionalCoordinates: AdditionalCoordinate[] | null,
opacity: number,
) {
const { segmentMeshController } = getSceneController();
const loader = getDracoLoader();
Expand Down Expand Up @@ -1047,6 +1067,7 @@ function* loadPrecomputedMeshesInChunksForLod(
lod,
layerName,
additionalCoordinates,
opacity,
false,
);

Expand Down Expand Up @@ -1114,6 +1135,7 @@ function* loadPrecomputedMeshesInChunksForLod(
lod,
layerName,
additionalCoordinates,
opacity,
true,
);
}
Expand Down
3 changes: 2 additions & 1 deletion frontend/javascripts/viewer/model/sagas/proofread_saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { SoftError, isBigInt, isNumberMap } from "libs/utils";
import _ from "lodash";
import { all, call, put, spawn, takeEvery } from "typed-redux-saga";
import type { AdditionalCoordinate, ServerEditableMapping } from "types/api_types";
import { MappingStatusEnum, TreeTypeEnum, type Vector3 } from "viewer/constants";
import Constants, { MappingStatusEnum, TreeTypeEnum, type Vector3 } from "viewer/constants";
import { getSegmentIdForPositionAsync } from "viewer/controller/combinations/volume_handlers";
import {
getLayerByName,
Expand Down Expand Up @@ -180,6 +180,7 @@ function* loadCoarseMesh(
position,
additionalCoordinates,
currentMeshFile.name,
Constants.DEFAULT_MESH_OPACITY,
undefined,
),
);
Expand Down
3 changes: 2 additions & 1 deletion frontend/javascripts/viewer/view/context_menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import type {
VoxelSize,
} from "types/api_types";
import type { AdditionalCoordinate } from "types/api_types";
import {
import Constants, {
AltOrOptionKey,
CtrlOrCmdKey,
LongUnitToShortUnitMap,
Expand Down Expand Up @@ -963,6 +963,7 @@ function getNoNodeContextMenuOptions(props: NoNodeContextMenuProps): ItemType[]
globalPosition,
additionalCoordinates,
currentMeshFile.name,
Constants.DEFAULT_MESH_OPACITY,
undefined,
),
);
Expand Down
Loading
Loading