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 11 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
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- Added the possibility for super users to retry manually cancelled jobs from the jobs list. [#8629](https://github.com/scalableminds/webknossos/pull/8629)
- Added checkboxes to the segments tab that allow to show/hide individual segments. The visibility of segments that are not listed in the segments list can be controlled with a new "Hide unlisted segments" toggle in the layer settings. Additionally, you can right-click a segment in a data viewport and select "Only show this segment" (and similar functionality). [#8546](https://github.com/scalableminds/webknossos/pull/8546)
- Instead of pasting a dataset position from the clipboard to the position input box, you can simply paste it without focussing the position input first. Furthermore, you can also paste a "hash string", such as `#1406,1794,1560,0,0.234,186`, as it can be found in WK URLs. Pasting such a string will also set the encoded zoom, rotation, viewport etc. Note that the `#` has to be included in the pasted text. You can also copy and paste an entire link, but note that the dataset or annotation id in the link will be ignored. [#8652](https://github.com/scalableminds/webknossos/pull/8652)
- Meshes are now reloaded using their previous opacity value. [#8622](https://github.com/scalableminds/webknossos/pull/8622)

### Changed
- Remove `data.maybe` dependency and replaced with regular Typescript types. [#8563](https://github.com/scalableminds/webknossos/pull/8563)
Expand Down
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 @@ -2521,6 +2521,7 @@ class DataApi {
seedPosition,
seedAdditionalCoordinates,
meshFileName,
Constants.DEFAULT_MESH_OPACITY,
effectiveLayerName,
),
);
Expand Down
12 changes: 12 additions & 0 deletions frontend/javascripts/viewer/controller/mesh_helpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { meshApi } from "admin/rest_api";
import { V3 } from "libs/mjs";
import _ from "lodash";
import type * as THREE from "three";
import type { Vector3 } from "viewer/constants";

export type BufferGeometryWithInfo = THREE.BufferGeometry & {
vertexSegmentMapping?: VertexSegmentMapping;
Expand Down Expand Up @@ -78,3 +81,12 @@ export class VertexSegmentMapping {
return _.sortedIndexOf(this.unmappedSegmentIds, segmentId) !== -1;
}
}

export function sortByDistanceTo(
availableChunks: Vector3[] | meshApi.MeshChunk[] | null | undefined,
seedPosition: Vector3,
) {
return _.sortBy(availableChunks, (chunk: Vector3 | meshApi.MeshChunk) =>
V3.length(V3.sub(seedPosition, "position" in chunk ? chunk.position : chunk)),
) as Array<Vector3> | Array<meshApi.MeshChunk>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import {
getActiveSegmentationTracing,
getSegmentColorAsHSLA,
} from "viewer/model/accessors/volumetracing_accessor";
import { NO_LOD_MESH_INDEX } from "viewer/model/sagas/mesh_saga";
import Store from "viewer/store";

import { computeBvhAsync } from "libs/compute_bvh_async";
import type { BufferAttribute } from "three";
import { NO_LOD_MESH_INDEX } from "viewer/model/sagas/meshes/common_mesh_saga";
import type { BufferGeometryWithInfo } from "./mesh_helpers";

// Add the raycast function. Assumes the BVH is available on
Expand Down 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 @@ -344,7 +344,7 @@ function AnnotationReducer(state: WebknossosState, action: Action): WebknossosSt
});
}

// Mesh information is stored in three places: the state in the store, segment_view_controller and within the mesh_saga.
// Mesh information is stored in three places: the state in the store, segment_view_controller and within the mesh sagas.
case "ADD_AD_HOC_MESH": {
const {
layerName,
Expand All @@ -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
Loading