Skip to content
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

Rename isosurface to adHocMesh #7350

Merged
merged 12 commits into from
Oct 16, 2023
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
### Changed
- Updated backend code to Scala 2.13, with upgraded Dependencies for optimized performance. [#7327](https://github.com/scalableminds/webknossos/pull/7327)
- Remote datasets with a datasource-properties.json can now also be imported without the need for OME metadata. [#7372](https://github.com/scalableminds/webknossos/pull/7372)
- Occurrences of isosurface were renamed to ad-hoc mesh. This also applies to configuration files. [#7350](https://github.com/scalableminds/webknossos/pull/7350)

### Fixed
- Fixed that some segment (group) actions were not properly disabled for non-editable segmentation layers. [#7207](https://github.com/scalableminds/webknossos/issues/7207)
Expand Down
2 changes: 2 additions & 0 deletions MIGRATIONS.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md).
## Unreleased
[Commits](https://github.com/scalableminds/webknossos/compare/23.10.2...HEAD)

The `datastore/isosurface` configuration key was renamed to `datastore/adHocMesh.

### Postgres Evolutions:
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ WEBKNOSSOS is an open-source tool for annotating and exploring large 3D image da
* [Standalone datastore component](https://github.com/scalableminds/webknossos/tree/master/webknossos-datastore) for flexible deployments
* Supported dataset formats: [WKW](https://github.com/scalableminds/webknossos-wrap), [Neuroglancer Precomputed, and BossDB](https://github.com/scalableminds/webknossos-connect), [Zarr](https://zarr.dev), [N5](https://github.com/saalfeldlab/n5)
* Supported image formats: Grayscale, Segmentation Maps, RGB, Multi-Channel
* [Support for 3D mesh rendering and on-the-fly isosurface generation](https://docs.webknossos.org/webknossos/mesh_visualization.html)
* [Support for 3D mesh rendering and ad-hoc mesh generation](https://docs.webknossos.org/webknossos/mesh_visualization.html)
* Export and streaming of any dataset and annotation as [Zarr](https://zarr.dev) to third-party tools
* [Documented frontend API for user scripts](https://webknossos.org/assets/docs/frontend-api/index.html), REST API for backend access
* Open-source development with [automated test suite](https://circleci.com/gh/scalableminds/webknossos)
Expand Down
2 changes: 1 addition & 1 deletion conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ datastore {
cumsumMaxReaderRange = 1310720
}
}
isosurface {
adHocMesh {
timeout = 30 seconds
actorPoolSize = 1
}
Expand Down
12 changes: 6 additions & 6 deletions frontend/javascripts/admin/admin_rest_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2130,9 +2130,9 @@ window.setMaintenance = setMaintenance;

// Meshes

// These parameters are bundled into an object to avoid that the computeIsosurface function
// These parameters are bundled into an object to avoid that the computeAdHocMesh function
// receives too many parameters, since this doesn't play well with the saga typings.
type IsosurfaceRequest = {
type MeshRequest = {
// The position is in voxels in mag 1
position: Vector3;
mag: Vector3;
Expand All @@ -2146,9 +2146,9 @@ type IsosurfaceRequest = {
findNeighbors: boolean;
};

export function computeIsosurface(
export function computeAdHocMesh(
requestUrl: string,
isosurfaceRequest: IsosurfaceRequest,
meshRequest: MeshRequest,
): Promise<{
buffer: ArrayBuffer;
neighbors: Array<number>;
Expand All @@ -2160,14 +2160,14 @@ export function computeIsosurface(
subsamplingStrides,

...rest
} = isosurfaceRequest;
} = meshRequest;

return doWithToken(async (token) => {
const params = new URLSearchParams();
params.append("token", token);

const { buffer, headers } = await Request.sendJSONReceiveArraybufferWithHeaders(
`${requestUrl}/isosurface?${params}`,
`${requestUrl}/adHocMesh?${params}`,
{
data: {
// The back-end needs a small padding at the border of the
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ instead. Only enable this option if you understand its effect. All layers will n
"A corruption in the current skeleton annotation was detected. Please contact your supervisor and/or the maintainers of WEBKNOSSOS to get help for restoring a working version. Please include as much details as possible about your past user interactions. This will be very helpful to investigate the source of this bug.",
"tracing.merger_mode_node_outside_segment":
"You cannot place nodes outside of a segment in merger mode.",
"tracing.not_isosurface_available_to_download":
"tracing.not_mesh_available_to_download":
"There is no mesh for the active segment id available to download.",
"tracing.mesh_listing_failed":
"A precomputed mesh could not be loaded for this segment. More information was printed to the browser's console.",
Expand Down
22 changes: 11 additions & 11 deletions frontend/javascripts/oxalis/api/api_latest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ import { setPositionAction, setRotationAction } from "oxalis/model/actions/flyca
import { setToolAction } from "oxalis/model/actions/ui_actions";
import {
updateCurrentMeshFileAction,
refreshIsosurfacesAction,
updateIsosurfaceVisibilityAction,
removeIsosurfaceAction,
refreshMeshesAction,
updateMeshVisibilityAction,
removeMeshAction,
dispatchMaybeFetchMeshFilesAsync,
} from "oxalis/model/actions/annotation_actions";
import {
Expand Down Expand Up @@ -1590,8 +1590,8 @@ class DataApi {
);
}

refreshIsosurfaces() {
Store.dispatch(refreshIsosurfacesAction());
refreshMeshes() {
Store.dispatch(refreshMeshesAction());
}

/**
Expand Down Expand Up @@ -2257,8 +2257,8 @@ class DataApi {
layerName,
).name;

if (Store.getState().localSegmentationData[effectiveLayerName].isosurfaces[segmentId] != null) {
Store.dispatch(updateIsosurfaceVisibilityAction(effectiveLayerName, segmentId, isVisible));
if (Store.getState().localSegmentationData[effectiveLayerName].meshes[segmentId] != null) {
Store.dispatch(updateMeshVisibilityAction(effectiveLayerName, segmentId, isVisible));
}
}

Expand All @@ -2275,8 +2275,8 @@ class DataApi {
layerName,
).name;

if (Store.getState().localSegmentationData[effectiveLayerName].isosurfaces[segmentId] != null) {
Store.dispatch(removeIsosurfaceAction(effectiveLayerName, segmentId));
if (Store.getState().localSegmentationData[effectiveLayerName].meshes[segmentId] != null) {
Store.dispatch(removeMeshAction(effectiveLayerName, segmentId));
}
}

Expand All @@ -2293,11 +2293,11 @@ class DataApi {
layerName,
).name;
const segmentIds = Object.keys(
Store.getState().localSegmentationData[effectiveLayerName].isosurfaces,
Store.getState().localSegmentationData[effectiveLayerName].meshes,
);

for (const segmentId of segmentIds) {
Store.dispatch(removeIsosurfaceAction(effectiveLayerName, Number(segmentId)));
Store.dispatch(removeMeshAction(effectiveLayerName, Number(segmentId)));
}
}

Expand Down
8 changes: 4 additions & 4 deletions frontend/javascripts/oxalis/controller/scene_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class SceneController {
this.rootGroup.scale.copy(new THREE.Vector3(...Store.getState().dataset.dataSource.scale));
// Add scene to the group, all Geometries are then added to group
this.scene.add(this.rootGroup);
this.scene.add(this.segmentMeshController.isosurfacesLODRootGroup);
this.scene.add(this.segmentMeshController.meshesLODRootGroup);
this.scene.add(this.meshesRootGroup);
this.rootGroup.add(new THREE.DirectionalLight());
this.setupDebuggingMethods();
Expand Down Expand Up @@ -340,7 +340,7 @@ class SceneController {

this.taskBoundingBox?.updateForCam(id);

this.segmentMeshController.isosurfacesLODRootGroup.visible = id === OrthoViews.TDView;
this.segmentMeshController.meshesLODRootGroup.visible = id === OrthoViews.TDView;
this.annotationToolsGeometryGroup.visible = id !== OrthoViews.TDView;
this.lineMeasurementGeometry.updateForCam(id);

Expand Down Expand Up @@ -537,8 +537,8 @@ class SceneController {

this.taskBoundingBox?.setVisibility(false);

if (this.segmentMeshController.isosurfacesLODRootGroup != null) {
this.segmentMeshController.isosurfacesLODRootGroup.visible = false;
if (this.segmentMeshController.meshesLODRootGroup != null) {
this.segmentMeshController.meshesLODRootGroup.visible = false;
}
}

Expand Down
96 changes: 44 additions & 52 deletions frontend/javascripts/oxalis/controller/segment_mesh_controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,36 @@ import _ from "lodash";
import type { Vector3 } from "oxalis/constants";
import CustomLOD from "oxalis/controller/custom_lod";
import { getSegmentColorAsHSLA } from "oxalis/model/accessors/volumetracing_accessor";
import { NO_LOD_MESH_INDEX } from "oxalis/model/sagas/isosurface_saga";
import { NO_LOD_MESH_INDEX } from "oxalis/model/sagas/mesh_saga";
import Store from "oxalis/store";

export default class SegmentMeshController {
// isosurfacesLODRootGroup holds lights and one group per segmentation id.
// meshesLODRootGroup holds lights and one group per segmentation id.
// Each group can hold multiple meshes.
isosurfacesLODRootGroup: CustomLOD;
isosurfacesGroupsPerSegmentationId: Record<string, Record<number, Record<number, THREE.Group>>> =
{};
meshesLODRootGroup: CustomLOD;
meshesGroupsPerSegmentationId: Record<string, Record<number, Record<number, THREE.Group>>> = {};

constructor() {
this.isosurfacesLODRootGroup = new CustomLOD();
this.meshesLODRootGroup = new CustomLOD();
this.addLights();
}

hasIsosurface(id: number, layerName: string): boolean {
const segments = this.isosurfacesGroupsPerSegmentationId[layerName];
hasMesh(id: number, layerName: string): boolean {
const segments = this.meshesGroupsPerSegmentationId[layerName];
if (!segments) {
return false;
}
return segments[id] != null;
}

addIsosurfaceFromVertices(
vertices: Float32Array,
segmentationId: number,
layerName: string,
): void {
addMeshFromVertices(vertices: Float32Array, segmentationId: number, layerName: string): void {
let bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));

bufferGeometry = mergeVertices(bufferGeometry);
bufferGeometry.computeVertexNormals();

this.addIsosurfaceFromGeometry(
this.addMeshFromGeometry(
bufferGeometry,
segmentationId,
null,
Expand All @@ -51,7 +46,7 @@ export default class SegmentMeshController {
);
}

constructIsosurfaceMesh(segmentId: number, geometry: THREE.BufferGeometry) {
constructMesh(segmentId: number, geometry: THREE.BufferGeometry) {
const color = this.getColorObjectForSegment(segmentId);
const meshMaterial = new THREE.MeshLambertMaterial({
color,
Expand Down Expand Up @@ -82,83 +77,80 @@ export default class SegmentMeshController {
return mesh;
}

addIsosurfaceFromGeometry(
addMeshFromGeometry(
geometry: THREE.BufferGeometry,
segmentationId: number,
offset: Vector3 | null = null,
scale: Vector3 | null = null,
lod: number,
layerName: string,
): void {
if (this.isosurfacesGroupsPerSegmentationId[layerName] == null) {
this.isosurfacesGroupsPerSegmentationId[layerName] = {};
if (this.meshesGroupsPerSegmentationId[layerName] == null) {
this.meshesGroupsPerSegmentationId[layerName] = {};
}
if (this.isosurfacesGroupsPerSegmentationId[layerName][segmentationId] == null) {
this.isosurfacesGroupsPerSegmentationId[layerName][segmentationId] = {};
if (this.meshesGroupsPerSegmentationId[layerName][segmentationId] == null) {
this.meshesGroupsPerSegmentationId[layerName][segmentationId] = {};
}
if (this.isosurfacesGroupsPerSegmentationId[layerName][segmentationId][lod] == null) {
if (this.meshesGroupsPerSegmentationId[layerName][segmentationId][lod] == null) {
const newGroup = new THREE.Group();
this.isosurfacesGroupsPerSegmentationId[layerName][segmentationId][lod] = newGroup;
this.meshesGroupsPerSegmentationId[layerName][segmentationId][lod] = newGroup;
if (lod === NO_LOD_MESH_INDEX) {
this.isosurfacesLODRootGroup.addNoLODSupportedMesh(newGroup);
this.meshesLODRootGroup.addNoLODSupportedMesh(newGroup);
} else {
this.isosurfacesLODRootGroup.addLODMesh(newGroup, lod);
this.meshesLODRootGroup.addLODMesh(newGroup, lod);
}
// @ts-ignore
newGroup.cellId = segmentationId;
if (scale != null) {
newGroup.scale.copy(new THREE.Vector3(...scale));
}
}
const mesh = this.constructIsosurfaceMesh(segmentationId, geometry);
const mesh = this.constructMesh(segmentationId, geometry);
if (offset) {
mesh.translateX(offset[0]);
mesh.translateY(offset[1]);
mesh.translateZ(offset[2]);
}

this.isosurfacesGroupsPerSegmentationId[layerName][segmentationId][lod].add(mesh);
this.meshesGroupsPerSegmentationId[layerName][segmentationId][lod].add(mesh);
}

removeIsosurfaceById(segmentationId: number, layerName: string): void {
if (this.isosurfacesGroupsPerSegmentationId[layerName] == null) {
removeMeshById(segmentationId: number, layerName: string): void {
if (this.meshesGroupsPerSegmentationId[layerName] == null) {
return;
}
if (this.isosurfacesGroupsPerSegmentationId[layerName][segmentationId] == null) {
if (this.meshesGroupsPerSegmentationId[layerName][segmentationId] == null) {
return;
}
_.forEach(
this.isosurfacesGroupsPerSegmentationId[layerName][segmentationId],
(meshGroup, lod) => {
const lodNumber = parseInt(lod);
if (lodNumber !== NO_LOD_MESH_INDEX) {
this.isosurfacesLODRootGroup.removeLODMesh(meshGroup, lodNumber);
} else {
this.isosurfacesLODRootGroup.removeNoLODSupportedMesh(meshGroup);
}
},
);
delete this.isosurfacesGroupsPerSegmentationId[layerName][segmentationId];
_.forEach(this.meshesGroupsPerSegmentationId[layerName][segmentationId], (meshGroup, lod) => {
const lodNumber = parseInt(lod);
if (lodNumber !== NO_LOD_MESH_INDEX) {
this.meshesLODRootGroup.removeLODMesh(meshGroup, lodNumber);
} else {
this.meshesLODRootGroup.removeNoLODSupportedMesh(meshGroup);
}
});
delete this.meshesGroupsPerSegmentationId[layerName][segmentationId];
}

getIsosurfaceGeometryInBestLOD(segmentId: number, layerName: string): THREE.Group {
getMeshGeometryInBestLOD(segmentId: number, layerName: string): THREE.Group {
const bestLod = Math.min(
...Object.keys(this.isosurfacesGroupsPerSegmentationId[layerName][segmentId]).map((lodVal) =>
...Object.keys(this.meshesGroupsPerSegmentationId[layerName][segmentId]).map((lodVal) =>
parseInt(lodVal),
),
);
return this.isosurfacesGroupsPerSegmentationId[layerName][segmentId][bestLod];
return this.meshesGroupsPerSegmentationId[layerName][segmentId][bestLod];
}

setIsosurfaceVisibility(id: number, visibility: boolean, layerName: string): void {
_.forEach(this.isosurfacesGroupsPerSegmentationId[layerName][id], (meshGroup) => {
setMeshVisibility(id: number, visibility: boolean, layerName: string): void {
_.forEach(this.meshesGroupsPerSegmentationId[layerName][id], (meshGroup) => {
meshGroup.visible = visibility;
});
}

setIsosurfaceColor(id: number, layerName: string): void {
setMeshColor(id: number, layerName: string): void {
const color = this.getColorObjectForSegment(id);
_.forEach(this.isosurfacesGroupsPerSegmentationId[layerName][id], (meshGroup) => {
_.forEach(this.meshesGroupsPerSegmentationId[layerName][id], (meshGroup) => {
if (meshGroup) {
for (const child of meshGroup.children) {
// @ts-ignore
Expand Down Expand Up @@ -200,9 +192,9 @@ export default class SegmentMeshController {
pointLight.position.y = -25;
pointLight.position.z = 10;

this.isosurfacesLODRootGroup.add(ambientLight);
this.isosurfacesLODRootGroup.add(directionalLight);
this.isosurfacesLODRootGroup.add(directionalLight2);
this.isosurfacesLODRootGroup.add(pointLight);
this.meshesLODRootGroup.add(ambientLight);
this.meshesLODRootGroup.add(directionalLight);
this.meshesLODRootGroup.add(directionalLight2);
this.meshesLODRootGroup.add(pointLight);
}
}
Loading