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

New Mesh File Support #6491

Merged
merged 73 commits into from
Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
dba08ec
refactor Hdf5Access
leowe Sep 22, 2022
7426496
Merge branch 'master' into new-mesh-file-support
leowe Sep 22, 2022
c9aecb7
add first new mesh file functionality
leowe Sep 22, 2022
1f60390
finished basic functionality
leowe Sep 23, 2022
4190440
write new routes
leowe Sep 26, 2022
ca0690c
add small todo
leowe Sep 26, 2022
0865d99
add dummy draco route
leowe Sep 27, 2022
56cd3f7
remove draco file from binaryData
leowe Sep 27, 2022
786129c
add dummy loading of draco encoded file
philippotto Sep 27, 2022
a71efdf
add dummy draco file
philippotto Sep 27, 2022
220a97a
add draco loader
philippotto Sep 27, 2022
b931cda
change response from dummyDracoFile
leowe Sep 27, 2022
e214ee0
remove println
leowe Sep 27, 2022
01a6d0c
Merge branch 'master' into new-mesh-file-support
leowe Sep 28, 2022
610a331
reformat
leowe Sep 28, 2022
006e7a4
Merge branch 'master' into new-mesh-file-support
leowe Sep 29, 2022
ef11d68
add changelog entry
leowe Sep 29, 2022
877a85e
add formatVersion als parameter in URL
leowe Sep 29, 2022
8e60a9d
rename chunkdatarequest and add custom writes to vec3float
leowe Sep 29, 2022
72e325d
use new mesh v3 api
philippotto Sep 29, 2022
de2c466
temporary workaround for mapping name with empty string
philippotto Sep 29, 2022
5aa3ee8
Merge branch 'new-mesh-file-support' of github.com:scalableminds/webk…
philippotto Sep 29, 2022
8b55ab4
adapt frontend to backend changes
philippotto Sep 29, 2022
a8bfbac
add new api modules
philippotto Sep 29, 2022
65081e9
remove the dummy draco file code again
philippotto Sep 29, 2022
8839007
fix addition
leowe Sep 29, 2022
d269280
use DRACOLoader from threejs package
philippotto Sep 29, 2022
023d7ed
Merge branch 'new-mesh-file-support' of github.com:scalableminds/webk…
philippotto Sep 29, 2022
d506a52
use wasm for draco decoding
philippotto Sep 29, 2022
dbdd895
reformat and rename fragment to chunk
leowe Sep 29, 2022
56ba4e2
clean up typing for draco loader
philippotto Sep 29, 2022
4495107
remove unused draco3d image
philippotto Sep 29, 2022
39596a1
pin URI to WASM in GH repo
philippotto Sep 29, 2022
de4f75e
Merge branch 'new-mesh-file-support' of github.com:scalableminds/webk…
philippotto Sep 29, 2022
97facd4
rename fragment to chunk like the back-end did it
philippotto Sep 29, 2022
7b7764a
NOW ALL NEW: THE GREAT NEW MESHCONTROLLER
leowe Sep 29, 2022
e9f26c6
fix colors by computing vertex normals
philippotto Sep 29, 2022
470b6fb
Merge branch 'new-mesh-file-support' of github.com:scalableminds/webk…
philippotto Sep 29, 2022
233f03e
remove todo
leowe Sep 29, 2022
562da59
what we don't need we don't keep
leowe Sep 29, 2022
5c71d4b
Update util/src/main/scala/com/scalableminds/util/tools/Fox.scala
leowe Sep 30, 2022
947a4c8
Update webknossos-datastore/app/com/scalableminds/webknossos/datastor…
leowe Sep 30, 2022
c76c090
Update webknossos-datastore/app/com/scalableminds/webknossos/datastor…
leowe Sep 30, 2022
599033d
Merge branch 'master' into new-mesh-file-support
leowe Sep 30, 2022
5ab2f83
incorporate pr feedback (move methods, renaming)
leowe Sep 30, 2022
57ad538
make all meshes smooth
philippotto Sep 30, 2022
2710f81
generalize switch setting to allow for different width of label
philippotto Sep 30, 2022
720d31f
remove setting for smooth meshes again (always use smooth meshes)
philippotto Sep 30, 2022
c6ddc48
improve lighting and expose window.testLights method for tuning param…
philippotto Sep 30, 2022
b84f347
Merge branch 'new-mesh-file-support' of github.com:scalableminds/webk…
philippotto Sep 30, 2022
5c2d866
remove unused imports
philippotto Sep 30, 2022
9f5a4eb
remove unused imports
leowe Sep 30, 2022
d5fc95a
use formatVersion of mesh file to decide which routes to use
philippotto Sep 30, 2022
488c92c
Merge branch 'new-mesh-file-support' of github.com:scalableminds/webk…
philippotto Sep 30, 2022
4dab4a4
fix failing tests by simply copying DracoLoader and BufferGeometryUti…
philippotto Sep 30, 2022
616bf50
fix linting
philippotto Oct 4, 2022
0c95492
fix import
philippotto Oct 4, 2022
4518225
fix linting
philippotto Oct 4, 2022
126f2cc
fix error when sharing link with meshes
philippotto Oct 4, 2022
9e4bd54
refactor maybeFetchMeshFiles to be a saga
philippotto Oct 4, 2022
e658ada
avoid redundant fetches of mesh files for the same layer
philippotto Oct 4, 2022
286325b
add draco decoder wasm to repo and use that instead of depending on g…
philippotto Oct 4, 2022
4358f83
integrate some PR feedback
philippotto Oct 5, 2022
96c0934
simplify maybeFetchMeshFiles saga
philippotto Oct 5, 2022
fb33b56
clean up version handling
philippotto Oct 5, 2022
e8902f5
read artifact_schema_version from mesh files instead of version prope…
philippotto Oct 5, 2022
122b1bd
re-add support for v1 and v2 mesh files
philippotto Oct 5, 2022
2c2e23a
clean up lighting
philippotto Oct 5, 2022
378b29a
fix enabling of corresponding mesh file when loading meshes specified…
philippotto Oct 5, 2022
6ef6faf
fix linting
philippotto Oct 5, 2022
978b493
set opacity to 100% for non-passive meshes
philippotto Oct 5, 2022
466a12f
fix typo
philippotto Oct 5, 2022
c08a8a9
always add an isosurface object when loading ad-hoc (will simply over…
philippotto Oct 5, 2022
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 .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"eslint-comments/no-unused-disable": "error",
"flowtype/no-types-missing-file-annotation": "off",
"import/extensions": ["warn", { "js": "never", "jsx": "always" }],
"import/prefer-default-export": ["warn"],
"import/no-cycle": "off",
"import/no-extraneous-dependencies": [
"error",
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
[Commits](https://github.com/scalableminds/webknossos/compare/22.10.0...HEAD)

### Added
- Support for a new mesh file format which allows up to billions of meshes. [#6491](https://github.com/scalableminds/webknossos/pull/6491)

### Changed
- Creating tasks in bulk now also supports referencing task types by their summary instead of id. [#6486](https://github.com/scalableminds/webknossos/pull/6486)
Expand Down
110 changes: 13 additions & 97 deletions frontend/javascripts/admin/admin_rest_api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,14 @@ import messages from "messages";
import window, { location } from "libs/window";
import { SaveQueueType } from "oxalis/model/actions/save_actions";
import { DatasourceConfiguration } from "types/schemas/datasource.types";
import { doWithToken } from "./api/token";

const MAX_SERVER_ITEMS_PER_RESPONSE = 1000;

export * from "./api/token";
export * as meshV3 from "./api/mesh_v3";
export * as meshV0 from "./api/mesh_v0";

type NewTeam = {
readonly name: string;
};
Expand All @@ -107,58 +112,6 @@ function assertResponseLimit(collection: unknown[]) {
}

// ### Do with userToken
let tokenRequestPromise: Promise<string> | null;

function requestUserToken(): Promise<string> {
if (tokenRequestPromise) {
return tokenRequestPromise;
}

tokenRequestPromise = Request.receiveJSON("/api/userToken/generate", {
method: "POST",
}).then((tokenObj) => {
tokenRequestPromise = null;
return tokenObj.token as string;
});

return tokenRequestPromise;
}

export function getSharingTokenFromUrlParameters(): string | null | undefined {
if (location != null) {
const params = Utils.getUrlParamsObject();

if (params != null && params.token != null) {
return params.token;
}
}

return null;
}

let tokenPromise: Promise<string>;
export function doWithToken<T>(fn: (token: string) => Promise<T>, tries: number = 1): Promise<any> {
const sharingToken = getSharingTokenFromUrlParameters();

if (sharingToken != null) {
return fn(sharingToken);
}

if (!tokenPromise) tokenPromise = requestUserToken();
return tokenPromise.then(fn).catch((error) => {
if (error.status === 403) {
console.warn("Token expired. Requesting new token...");
tokenPromise = requestUserToken();

// If three new tokens did not fix the 403, abort, otherwise we'll get into an endless loop here
if (tries < 3) {
return doWithToken(fn, tries + 1);
}
}

throw error;
});
}

export function sendAnalyticsEvent(eventType: string, eventProperties: {} = {}): void {
// Note that the Promise from sendJSONReceiveJSON is not awaited or returned here,
Expand Down Expand Up @@ -2118,61 +2071,24 @@ export function getEditableAgglomerateSkeleton(
);
}

export function getMeshfilesForDatasetLayer(
export async function getMeshfilesForDatasetLayer(
dataStoreUrl: string,
datasetId: APIDatasetId,
layerName: string,
): Promise<Array<APIMeshFile>> {
return doWithToken((token) =>
const meshFiles: Array<APIMeshFile> = await doWithToken((token) =>
Request.receiveJSON(
`${dataStoreUrl}/data/datasets/${datasetId.owningOrganization}/${datasetId.name}/layers/${layerName}/meshes?token=${token}`,
),
);
}

export function getMeshfileChunksForSegment(
dataStoreUrl: string,
datasetId: APIDatasetId,
layerName: string,
meshFile: string,
segmentId: number,
): Promise<Array<Vector3>> {
return doWithToken((token) =>
Request.sendJSONReceiveJSON(
`${dataStoreUrl}/data/datasets/${datasetId.owningOrganization}/${datasetId.name}/layers/${layerName}/meshes/chunks?token=${token}`,
{
data: {
meshFile,
segmentId,
},
showErrorToast: false,
},
),
);
}
for (const file of meshFiles) {
if (file.mappingName === "") {
file.mappingName = undefined;
}
}

export function getMeshfileChunkData(
dataStoreUrl: string,
datasetId: APIDatasetId,
layerName: string,
meshFile: string,
segmentId: number,
position: Vector3,
): Promise<ArrayBuffer> {
return doWithToken(async (token) => {
const data = await Request.sendJSONReceiveArraybufferWithHeaders(
`${dataStoreUrl}/data/datasets/${datasetId.owningOrganization}/${datasetId.name}/layers/${layerName}/meshes/chunks/data?token=${token}`,
{
data: {
meshFile,
segmentId,
position,
},
useWebworkerForArrayBuffer: false,
},
);
return data;
});
return meshFiles;
}

// ### Connectomes
Expand Down
49 changes: 49 additions & 0 deletions frontend/javascripts/admin/api/mesh_v0.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Request from "libs/request";
import { Vector3 } from "oxalis/constants";
import { APIDatasetId } from "types/api_flow_types";
import { doWithToken } from "./token";

export function getMeshfileChunksForSegment(
dataStoreUrl: string,
datasetId: APIDatasetId,
layerName: string,
meshFile: string,
segmentId: number,
): Promise<Array<Vector3>> {
return doWithToken((token) =>
Request.sendJSONReceiveJSON(
`${dataStoreUrl}/data/datasets/${datasetId.owningOrganization}/${datasetId.name}/layers/${layerName}/meshes/chunks?token=${token}`,
{
data: {
meshFile,
segmentId,
},
showErrorToast: false,
},
),
);
}

export function getMeshfileChunkData(
dataStoreUrl: string,
datasetId: APIDatasetId,
layerName: string,
meshFile: string,
segmentId: number,
position: Vector3,
): Promise<ArrayBuffer> {
return doWithToken(async (token) => {
const data = await Request.sendJSONReceiveArraybufferWithHeaders(
`${dataStoreUrl}/data/datasets/${datasetId.owningOrganization}/${datasetId.name}/layers/${layerName}/meshes/chunks/data?token=${token}`,
{
data: {
meshFile,
segmentId,
position,
},
useWebworkerForArrayBuffer: false,
},
);
return data;
});
}
70 changes: 70 additions & 0 deletions frontend/javascripts/admin/api/mesh_v3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import Request from "libs/request";
import { Vector3 } from "oxalis/constants";
import { APIDatasetId } from "types/api_flow_types";
import { doWithToken } from "./token";

export type MeshChunk = { position: Vector3; byteOffset: number; byteSize: number };

type MeshLodInfo = {
scale: number;
vertexOffset: Vector3;
chunkShape: Vector3;
chunks: Array<MeshChunk>;
};

type MeshSegmentInfo = {
chunkShape: Vector3;
gridOrigin: Vector3;
lods: Array<MeshLodInfo>;
};

type SegmentInfo = {
transform: number[][]; // 3x3 matrix
meshFormat: "draco";
chunks: MeshSegmentInfo;
};

export function getMeshfileChunksForSegment(
dataStoreUrl: string,
datasetId: APIDatasetId,
layerName: string,
meshFile: string,
segmentId: number,
): Promise<SegmentInfo> {
return doWithToken((token) =>
Request.sendJSONReceiveJSON(
`${dataStoreUrl}/data/datasets/${datasetId.owningOrganization}/${datasetId.name}/layers/${layerName}/meshes/formatVersion/3/chunks?token=${token}`,
{
data: {
meshFile,
segmentId,
},
showErrorToast: false,
},
),
);
}

export function getMeshfileChunkData(
dataStoreUrl: string,
datasetId: APIDatasetId,
layerName: string,
meshFile: string,
byteOffset: number,
byteSize: number,
): Promise<ArrayBuffer> {
return doWithToken(async (token) => {
const data = await Request.sendJSONReceiveArraybufferWithHeaders(
`${dataStoreUrl}/data/datasets/${datasetId.owningOrganization}/${datasetId.name}/layers/${layerName}/meshes/formatVersion/3/chunks/data?token=${token}`,
{
data: {
meshFile,
byteOffset,
byteSize,
},
useWebworkerForArrayBuffer: false,
},
);
return data;
});
}
57 changes: 57 additions & 0 deletions frontend/javascripts/admin/api/token.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { location } from "libs/window";
import Request from "libs/request";
import * as Utils from "libs/utils";

let tokenPromise: Promise<string>;

let tokenRequestPromise: Promise<string> | null;

function requestUserToken(): Promise<string> {
if (tokenRequestPromise) {
return tokenRequestPromise;
}

tokenRequestPromise = Request.receiveJSON("/api/userToken/generate", {
method: "POST",
}).then((tokenObj) => {
tokenRequestPromise = null;
return tokenObj.token as string;
});

return tokenRequestPromise;
}

export function getSharingTokenFromUrlParameters(): string | null | undefined {
if (location != null) {
const params = Utils.getUrlParamsObject();

if (params != null && params.token != null) {
return params.token;
}
}

return null;
}

export function doWithToken<T>(fn: (token: string) => Promise<T>, tries: number = 1): Promise<any> {
const sharingToken = getSharingTokenFromUrlParameters();

if (sharingToken != null) {
return fn(sharingToken);
}

if (!tokenPromise) tokenPromise = requestUserToken();
return tokenPromise.then(fn).catch((error) => {
if (error.status === 403) {
console.warn("Token expired. Requesting new token...");
tokenPromise = requestUserToken();

// If three new tokens did not fix the 403, abort, otherwise we'll get into an endless loop here
if (tries < 3) {
return doWithToken(fn, tries + 1);
}
}

throw error;
});
}
Loading