Skip to content

Commit

Permalink
temp history prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisj committed Oct 22, 2024
1 parent 0a43852 commit cb5e201
Show file tree
Hide file tree
Showing 9 changed files with 223 additions and 49 deletions.
32 changes: 30 additions & 2 deletions src/datasource/graphene/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import { computeChunkBounds } from "#src/sliceview/volume/backend.js";
import { Uint64Set } from "#src/uint64_set.js";
import { fetchSpecialHttpByteRange } from "#src/util/byte_range_http_requests.js";
import type { CancellationToken } from "#src/util/cancellation.js";
import { invokeDisposers } from "#src/util/disposable.js";
import { vec3, vec3Key } from "#src/util/geom.js";
import { responseArrayBuffer, responseJson } from "#src/util/http_request.js";
import type {
Expand Down Expand Up @@ -365,7 +366,10 @@ export class ChunkedGraphLayer extends withSegmentationLayerBackendState(
leafRequestsActive: SharedWatchableValue<boolean>;
nBitsForLayerId: SharedWatchableValue<number>;

readonly sessionId = self.crypto.randomUUID();

constructor(rpc: RPC, options: any) {
console.log("ChunkedGraphLayer constructor");
super(rpc, options);
this.source = this.registerDisposer(
rpc.getRef<GrapheneChunkedGraphChunkSource>(options.source),
Expand All @@ -377,9 +381,26 @@ export class ChunkedGraphLayer extends withSegmentationLayerBackendState(
this.registerDisposer(
this.chunkManager.recomputeChunkPriorities.add(() => {
this.updateChunkPriorities();
this.debouncedupdateDisplayState();
this.debouncedUpdateDisplayState();
}),
);
this.registerDisposer(() => {
console.log("ChunkedGraphLayer disposed really", this.sessionId);
});
}

disposed() {
console.log("ChunkedGraphLayer disposed called");
if (this.refCount === 0) {
console.log("ChunkedGraphLayer invoking disposers", this.sessionId);
const { disposers } = this;
if (disposers !== undefined) {
invokeDisposers(disposers);
this.disposers = <any>undefined;
}
this.wasDisposed = true;
}
super.disposed();
}

attach(
Expand Down Expand Up @@ -500,7 +521,9 @@ export class ChunkedGraphLayer extends withSegmentationLayerBackendState(
}
}

private debouncedupdateDisplayState = debounce(() => {
private debouncedUpdateDisplayState = debounce(() => {
console.log("ChunkedGraphLayer debounced update invoked", this.sessionId);
if (this.wasDisposed) return;
this.updateDisplayState();
}, 100);

Expand Down Expand Up @@ -542,6 +565,11 @@ export class ChunkedGraphLayer extends withSegmentationLayerBackendState(
this.segmentEquivalences.delete([...this.segmentEquivalences.setElements(Uint64.parseString(root))].filter(x
=> !leaves.has(x) && !this.visibleSegments.has(x)));
}*/
// console.log(
// "debugDisposed",
// this.segmentEquivalences.debugDisposed,
// this.segmentEquivalences.sessionId,
// );
const filteredLeaves = [...leaves].filter(
(x) => !this.segmentEquivalences.has(x),
);
Expand Down
116 changes: 71 additions & 45 deletions src/datasource/graphene/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ async function getVolumeDataSource(
info,
credentialsProvider,
volume,
state,
state, // how to apply this to connection instead
);
const { modelSpace } = info;
const subsources: DataSubsourceEntry[] = [
Expand Down Expand Up @@ -708,56 +708,56 @@ export class GrapheneDataSource extends PrecomputedDataSource {
return "Graphene file-backed data source";
}

get(options: GetDataSourceOptions): Promise<DataSource> {
async get(options: GetDataSourceOptions): Promise<DataSource> {
const { url: providerUrl, parameters } = parseProviderUrl(
options.providerUrl,
);
return options.chunkManager.memoize.getUncounted(
{ type: "graphene:get", providerUrl, parameters },
async (): Promise<DataSource> => {
const { url, credentialsProvider } = parseSpecialUrl(
providerUrl,
options.credentialsManager,
);
let metadata: any;
try {
metadata = await getJsonMetadata(
options.chunkManager,
credentialsProvider,
url,
);
} catch (e) {
if (isNotFoundError(e)) {
if (parameters["type"] === "mesh") {
console.log("does this happen?");
}
}
throw e;
// return options.chunkManager.memoize.getUncounted(
// { type: "graphene:get", providerUrl, parameters },
// async (): Promise<DataSource> => {
const { url, credentialsProvider } = parseSpecialUrl(
providerUrl,
options.credentialsManager,
);
let metadata: any;
try {
metadata = await getJsonMetadata(
options.chunkManager,
credentialsProvider,
url,
);
} catch (e) {
if (isNotFoundError(e)) {
if (parameters["type"] === "mesh") {
console.log("does this happen?");
}
verifyObject(metadata);
const redirect = verifyOptionalObjectProperty(
}
throw e;
}
verifyObject(metadata);
const redirect = verifyOptionalObjectProperty(
metadata,
"redirect",
verifyString,
);
if (redirect !== undefined) {
throw new RedirectError(redirect);
}
const t = verifyOptionalObjectProperty(metadata, "@type", verifyString);
switch (t) {
case "neuroglancer_multiscale_volume":
case undefined:
return await getVolumeDataSource(
options,
credentialsProvider,
url,
metadata,
"redirect",
verifyString,
);
if (redirect !== undefined) {
throw new RedirectError(redirect);
}
const t = verifyOptionalObjectProperty(metadata, "@type", verifyString);
switch (t) {
case "neuroglancer_multiscale_volume":
case undefined:
return await getVolumeDataSource(
options,
credentialsProvider,
url,
metadata,
);
default:
throw new Error(`Invalid type: ${JSON.stringify(t)}`);
}
},
);
default:
throw new Error(`Invalid type: ${JSON.stringify(t)}`);
}
// },
// );
}
}

Expand Down Expand Up @@ -908,6 +908,7 @@ class GrapheneState extends RefCounted implements Trackable {
}

toJSON() {
console.log("GS toJSON");
return {
[MULTICUT_JSON_KEY]: this.multicutState.toJSON(),
[MERGE_JSON_KEY]: this.mergeState.toJSON(),
Expand Down Expand Up @@ -1314,6 +1315,7 @@ class GraphConnection extends SegmentationGraphSourceConnection {
private chunkSource: GrapheneMultiscaleVolumeChunkSource,
public state: GrapheneState,
) {
console.log("GraphConnection constructor");
super(graph, layer.displayState.segmentationGroupState.value);
const segmentsState = layer.displayState.segmentationGroupState.value;
this.previousVisibleSegmentCount = segmentsState.visibleSegments.size;
Expand Down Expand Up @@ -1404,6 +1406,26 @@ class GraphConnection extends SegmentationGraphSourceConnection {
mergeAnnotationState.source.add(mergeToLine(merge));
}

this.registerDisposer(
merges.changed.add(() => {
for (const annotation of mergeAnnotationState.source) {
if (!merges.value.map((x) => x.id).includes(annotation.id)) {
mergeAnnotationState.source.delete(
mergeAnnotationState.source.getReference(annotation.id),
);
}
}
const existingAnnotationIds = [...mergeAnnotationState.source].map(
(x) => x.id,
);
for (const merge of merges.value) {
if (!existingAnnotationIds.includes(merge.id)) {
mergeAnnotationState.source.add(mergeToLine(merge));
}
}
}),
);

// initialize source changes
this.registerDisposer(
mergeAnnotationState.source.childAdded.add((x) => {
Expand Down Expand Up @@ -2453,6 +2475,10 @@ class SliceViewPanelChunkedGraphLayer extends SliceViewPanelRenderLayer {
});
this.registerDisposer(sharedObject.visibility.add(this.visibility));

this.registerDisposer(() => {
console.log("SliceViewPanelChunkedGraphLayer disposed");
});

this.registerDisposer(
this.leafRequestsActive.changed.add(() => {
this.showOrHideMessage(this.leafRequestsActive.value);
Expand Down
3 changes: 3 additions & 0 deletions src/layer/segmentation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ export class SegmentationUserLayerGroupState
[this.graph],
),
),
"segmentEquivalences",
),
);
localSegmentEquivalences = false;
Expand All @@ -300,6 +301,7 @@ export class SegmentationUserLayerGroupState
SharedDisjointUint64Sets.makeWithCounterpart(
this.layer.manager.rpc,
this.segmentEquivalences.disjointSets.visibleSegmentEquivalencePolicy,
"temporarySegmentEquivalences",
),
);
useTemporaryVisibleSegments = this.layer.registerDisposer(
Expand Down Expand Up @@ -639,6 +641,7 @@ export class SegmentationUserLayer extends Base {
);

constructor(managedLayer: Borrowed<ManagedUserLayer>) {
console.log("SegmentationUserLayer constructor");
super(managedLayer);
this.registerDisposer(
registerNestedSync((context, group) => {
Expand Down
5 changes: 5 additions & 0 deletions src/segmentation_display_state/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,11 @@ export class SegmentationLayerSharedObject extends Base {
}
}

disposed(): void {
console.log("SegmentationLayerSharedObject disposed");
super.disposed();
}

initializeCounterpartWithChunkManager(options: any) {
const { displayState } = this;
options.chunkManager = this.chunkManager.rpcId;
Expand Down
18 changes: 18 additions & 0 deletions src/shared_disjoint_sets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,33 @@ export class SharedDisjointUint64Sets
disjointSets = new DisjointUint64Sets();
changed = new NullarySignal();

debugDisposed = false;

readonly sessionId = self.crypto.randomUUID();

/**
* For compatibility with `WatchableValueInterface`.
*/
get value() {
return this;
}

// constructor() {
// super();
// console.log("SharedDisjointUint64Sets constructor", this.sessionId);
// }

static makeWithCounterpart(
rpc: RPC,
highBitRepresentative: WatchableValueInterface<VisibleSegmentEquivalencePolicy>,
name = "foo",
) {
const obj = new SharedDisjointUint64Sets();
console.log(
"making SharedDisjointUint64Sets counterpart",
name,
obj.sessionId,
);
obj.disjointSets.visibleSegmentEquivalencePolicy = highBitRepresentative;
obj.registerDisposer(
highBitRepresentative.changed.add(() => {
Expand All @@ -64,10 +79,13 @@ export class SharedDisjointUint64Sets
if (highBitRepresentative.value) {
updateHighBitRepresentative(obj);
}
console.log("returning", obj);
return obj;
}

disposed() {
console.log("SharedDisjointUint64Sets disposed!", this.sessionId);
this.debugDisposed = true;
this.disjointSets = <any>undefined;
this.changed = <any>undefined;
super.disposed();
Expand Down
60 changes: 60 additions & 0 deletions src/ui/default_viewer_setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,5 +179,65 @@ export function setupDefaultViewer() {
bindDefaultCopyHandler(viewer);
bindDefaultPasteHandler(viewer);

const downloadObject = (obj: any, filename: string) => {
const a = document.createElement("a");
const file = new Blob([JSON.stringify(obj)], { type: "application/json" });
a.href = URL.createObjectURL(file);
a.download = filename;
a.click();
};

(window as any).saveHistory = (sessionId: string) => {
const res: any[] = [];
let historyIndex = 0;
let state: string | null = null;
while (
(state = localStorage.getItem(`${sessionId}_${historyIndex}`)) !== null
) {
res.push(JSON.parse(state));
historyIndex++;
}
downloadObject(res, "history.json");
};

let inReplay = false;

(window as any).ngReplay = (sessionId: string) => {
if (inReplay) return;
hashBinding.recording = false;
inReplay = true;
const startTime = Date.now();
let historyIndex = 0;
let nextState = localStorage.getItem(`${sessionId}_${historyIndex}`);
if (!nextState) {
console.log(`no history for session ${sessionId}`);
}
let nextTime = parseInt(
localStorage.getItem(`${sessionId}_${historyIndex}_time`)!,
);

const loop = () => {
const elapsedTime = Date.now() - startTime;
console.log("replay", elapsedTime, historyIndex, nextTime - elapsedTime);
if (nextTime > 0 && elapsedTime >= nextTime) {
// set new state
viewer.state.restoreState(JSON.parse(nextState!));
// get next states
historyIndex++;
nextState = localStorage.getItem(`${sessionId}_${historyIndex}`);
if (!nextState) {
inReplay = false;
console.log("done with replay");
return;
}
nextTime = parseInt(
localStorage.getItem(`${sessionId}_${historyIndex}_time`)!,
);
}
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
};

return viewer;
}
Loading

0 comments on commit cb5e201

Please sign in to comment.