Skip to content

Commit

Permalink
Rename isosurface to adHocMesh (#7350)
Browse files Browse the repository at this point in the history
  • Loading branch information
frcroth committed Oct 16, 2023
1 parent d15170f commit 08852e8
Show file tree
Hide file tree
Showing 39 changed files with 446 additions and 514 deletions.
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

0 comments on commit 08852e8

Please sign in to comment.