diff --git a/src/datasource/graphene/frontend.ts b/src/datasource/graphene/frontend.ts index 34075c637..9be8f03e6 100644 --- a/src/datasource/graphene/frontend.ts +++ b/src/datasource/graphene/frontend.ts @@ -32,6 +32,7 @@ import type { ChunkManager, WithParameters } from "#src/chunk_manager/frontend.j import { makeIdentityTransform } from "#src/coordinate_transform.js"; import { WithCredentialsProvider } from "#src/credentials_provider/chunk_source_frontend.js"; import type { CredentialsManager } from "#src/credentials_provider/index.js"; + import type { ChunkedGraphChunkSource as ChunkedGraphChunkSourceInterface, ChunkedGraphChunkSpecification, @@ -78,6 +79,7 @@ import type { import type { LoadedDataSubsource } from "#src/layer/layer_data_source.js"; import { LoadedLayerDataSource } from "#src/layer/layer_data_source.js"; import { SegmentationUserLayer } from "#src/layer/segmentation/index.js"; +import * as json_keys from "#src/layer/segmentation/json_keys.js"; import { MeshSource } from "#src/mesh/frontend.js"; import type { DisplayDimensionRenderInfo } from "#src/navigation_state.js"; import type { @@ -205,6 +207,10 @@ import { CancellationTokenSource, } from "#src/util/cancellation"; import { debounce } from "lodash"; +import type { LayerControlDefinition } from "#src/widget/layer_control.js"; +import { addLayerControlToOptionsTab } from "#src/widget/layer_control.js"; +import { rangeLayerControl } from "#src/widget/layer_control_range.js"; + function vec4FromVec3(vec: vec3, alpha = 0) { const res = vec4.clone([...vec]); @@ -1626,7 +1632,7 @@ class GraphConnection extends SegmentationGraphSourceConnection { ); const segmentConst = segmentId.clone(); if (added && isBaseSegment) { - this.graph.getRoot(segmentConst).then((rootId) => { + this.graph.getRoot(segmentConst, this.layer.displayState.stopLayer.value).then((rootId) => { if (segmentsState.visibleSegments.has(segmentConst)) { segmentsState.visibleSegments.add(rootId); } @@ -1952,11 +1958,14 @@ class GrapheneGraphServerInterface { private credentialsProvider: SpecialProtocolCredentialsProvider, ) { } - async getRoot(segment: Uint64, timestamp = "") { + async getRoot(segment: Uint64, timestamp = "", stop_layer: number | undefined = undefined) { const timestampEpoch = new Date(timestamp).valueOf() / 1000; - const url = `${this.url}/node/${String(segment)}/root?int64_as_str=1${Number.isNaN(timestampEpoch) ? "" : `×tamp=${timestampEpoch}` + let url = `${this.url}/node/${String(segment)}/root?int64_as_str=1${Number.isNaN(timestampEpoch) ? "" : `×tamp=${timestampEpoch}` }`; + if (stop_layer !== undefined) { + url += "&stop_layer=" + stop_layer; + } const promise = cancellableFetchSpecialOk( this.credentialsProvider, @@ -2125,6 +2134,17 @@ class GrapheneGraphServerInterface { } } +export const LAYER_CONTROLS: LayerControlDefinition[] = [ + { + label: "Stop Layer", // Adjust the label as needed + toolJson: json_keys.STOP_LAYER, + // ... other properties (toolJson, isValid, title) based on your requirements + ...rangeLayerControl((layer) => ({ + value: layer.displayState.stopLayer, + options: { min: 0.0, max: 10, step: 1.0 } + })), // Integrate your getter function + }] + class GrapheneGraphSource extends SegmentationGraphSource { private connections = new Set(); public graphServer: GrapheneGraphServerInterface; @@ -2188,8 +2208,14 @@ class GrapheneGraphSource extends SegmentationGraphSource { } } - getRoot(segment: Uint64) { - return this.graphServer.getRoot(segment); + + getRoot(segment: Uint64, stop_layer: number) { + if (stop_layer > 0) { + return this.graphServer.getRoot(segment, "", stop_layer); + } + else { + return this.graphServer.getRoot(segment); + } } async findPath( @@ -2276,7 +2302,15 @@ class GrapheneGraphSource extends SegmentationGraphSource { title: "Find Path", }), ); + for (const control of LAYER_CONTROLS) { + toolbox.appendChild( + addLayerControlToOptionsTab(tab, layer, tab.visibility, control), + ); + } + + parent.appendChild(toolbox); + parent.appendChild( context.registerDisposer( new MulticutAnnotationLayerView(layer, layer.annotationDisplayState), @@ -2310,6 +2344,7 @@ class GrapheneGraphSource extends SegmentationGraphSource { } } + class ChunkedGraphChunkSource extends SliceViewChunkSource implements ChunkedGraphChunkSourceInterface { diff --git a/src/layer/segmentation/index.ts b/src/layer/segmentation/index.ts index 4427ad264..1aecf0e8b 100644 --- a/src/layer/segmentation/index.ts +++ b/src/layer/segmentation/index.ts @@ -122,6 +122,7 @@ import { verifyFiniteNonNegativeFloat, verifyObjectAsMap, verifyOptionalObjectProperty, + verifyPositiveInt, verifyString, } from "#src/util/json.js"; import { Signal } from "#src/util/signal.js"; @@ -485,6 +486,7 @@ class SegmentationUserLayerDisplayState implements SegmentationDisplayState { ); objectAlpha = trackableAlphaValue(1.0); ignoreNullVisibleSet = new TrackableBoolean(true, true); + stopLayer = new TrackableValue(0, verifyPositiveInt); skeletonRenderingOptions = new SkeletonRenderingOptions(); shaderError = makeWatchableShaderError(); renderScaleHistogram = new RenderScaleHistogram(); @@ -595,7 +597,11 @@ export class SegmentationUserLayer extends Base { }; displayState = new SegmentationUserLayerDisplayState(this); - + stopLayer = new TrackableValue( + 0, + verifyFiniteNonNegativeFloat, + 0, + ); anchorSegment = new TrackableValue(undefined, (x) => x === undefined ? undefined : Uint64.parseString(x), ); diff --git a/src/layer/segmentation/json_keys.ts b/src/layer/segmentation/json_keys.ts index 96417a61c..6d8442f25 100644 --- a/src/layer/segmentation/json_keys.ts +++ b/src/layer/segmentation/json_keys.ts @@ -9,6 +9,7 @@ export const IGNORE_NULL_VISIBLE_SET_JSON_KEY = "ignoreNullVisibleSet"; export const MESH_JSON_KEY = "mesh"; export const SKELETONS_JSON_KEY = "skeletons"; export const SEGMENTS_JSON_KEY = "segments"; +export const STOP_LAYER = 'stop_layer'; export const EQUIVALENCES_JSON_KEY = "equivalences"; export const COLOR_SEED_JSON_KEY = "colorSeed"; export const SEGMENT_STATED_COLORS_JSON_KEY = "segmentColors";