feat: Implement 'find path' tool for spatially indexed skeleton - #221
Draft
afonsobspinto wants to merge 3 commits into
Draft
feat: Implement 'find path' tool for spatially indexed skeleton#221afonsobspinto wants to merge 3 commits into
afonsobspinto wants to merge 3 commits into
Conversation
afonsobspinto
force-pushed
the
feature/find-path
branch
from
August 12, 2026 22:18
94920ec to
eecaff1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat: Implement Find Path tool for spatially indexed skeletons
Summary
Adds a datasource-owned Find Path tool for spatially indexed skeletons.
The tool lets users select two exact nodes in a visible skeleton and renders the
shortest route between them as a white annotation polyline. It is intended
primarily as a debugging aid for locating erroneous connections in merged
neurons.
The interaction follows Graphene's Find Path tool where applicable:
Unlike Graphene, path calculation is performed locally using the complete
skeleton topology already cached in the client. Submitting Find Path does not
initiate a skeleton download.
Closes https://metacell.atlassian.net/browse/NGLANCERSU-12
User-facing behavior
SpatialSkeletonActions.inspect.tool asks the user to wait and submit again.
segment IDs from different sources to alias.
Technical design
Datasource-owned state
Find Path state is stored with the skeleton datasource rather than directly on
SegmentationUserLayer.A new generic
SkeletonDataSourceStatecontains aSkeletonFindPathStateandserializes as:
{ "findPath": { "source": { "segmentId": "42", "nodeId": "101", "position": [10, 20, 30] }, "target": { "segmentId": "42", "nodeId": "205", "position": [40, 50, 60] }, "result": [ { "nodeId": "101", "position": [10, 20, 30] }, { "nodeId": "150", "position": [25, 35, 45] }, { "nodeId": "205", "position": [40, 50, 60] } ] } }Because this state is returned as
DataSource.state, the existing datasourcemachinery persists it under the corresponding
source[].stateentry:{ "source": [ { "url": "catmaid://...", "state": { "findPath": { "...": "..." } } } ] }The datasource itself provides the source identity, so no datasource index or
subsource locator is serialized. Reordering datasource entries does not require
rebasing a source locator because the state moves with its owning
LayerDataSource.Endpoint and result IDs are represented as uint64 values and serialized as
decimal strings. Restore validation requires:
Runtime annotation references and request generations are never serialized.
Generic Find Path state
SkeletonFindPathStateis independent of spatial rendering and CATMAID-specificAPIs. It owns:
Changing either endpoint automatically invalidates the previous result. A
topology change clears only the resolved route and preserves the endpoints.
This separation is intended to allow a future regular-skeleton adapter to reuse
the same state and persistence lifecycle. Such an adapter could map a regular
skeleton object ID to
segmentIdand a stable vertex index tonodeId.Local path calculation
getPathBetweenNodesis added to the existing spatial skeleton navigation graphhelpers.
The algorithm:
cycles.
tie-breaking.
undefinedfor missing or disconnected nodes.CATMAID skeletons are expected to be trees, but the implementation does not rely
on that restriction.
Only topology expressible through the current
parentNodeIdrepresentation isconsidered. Arbitrary additional graph edges are outside the scope of this
change.
Annotation adapter
SpatialSkeletonFindPathAnnotationControllerprojects the generic state into asource-local
LocalAnnotationSource.Each active spatial skeleton subsource receives a controller using that
subsource's coordinate transform. The controller creates and synchronizes:
find path sourcepoint.find path targetpoint.find path resultpolyline.All annotations are related to the selected skeleton segment through the
associated segmentsrelationship.The persisted Find Path state is canonical. Annotation IDs and
AnnotationReferenceinstances remain controller-owned runtime objects.Synchronization is bidirectional for deletion:
If an annotation editing tool directly modifies one of these derived
annotations, the controller restores it from the canonical Find Path state.
Programmatic update/deletion guards prevent controller operations from being
interpreted as user edits and creating event feedback loops.
Disposing the controller removes its runtime annotations and invalidates pending
completion, but does not clear the datasource-owned persisted state.
Multiple spatial skeleton sources
A segmentation layer may contain multiple active spatial skeleton datasources.
Each active source receives a
SpatialSkeletonFindPathContextcontaining:SpatiallyIndexedSkeletonLayer.LoadedDataSubsource.SkeletonDataSourceState.The contexts are indexed by the concrete spatial skeleton base layer, allowing
mouse picks to identify the owning datasource through the picked render layer.
The UI intentionally supports one active route per segmentation layer:
lowest datasource-index state is retained and the others are reset.
Disabling a spatial subsource disposes its annotations and cache scope but
retains its datasource state for reactivation.
Source-scoped complete-skeleton cache
Before this change, complete-skeleton inspection data was stored in layer-wide
maps keyed only by numeric IDs:
That was only safe while a segmentation layer effectively contained one spatial
skeleton source. If two datasources both contained segment
42or node101, acache lookup or pending request from one datasource could be reused by the
other.
The cache is now partitioned into
FullSkeletonCacheScopeobjects keyed bySpatiallyIndexedSkeletonLayer:The effective cache identities are now:
This provides:
replaced or disposed caches.
Legacy unscoped mutation methods remain available where an ID resolves to
exactly one cache scope. Ambiguous unscoped operations do nothing rather than
modifying an arbitrary datasource.
Find Path does not call the full-skeleton fetch API directly. Visible-skeleton
rendering continues to populate these caches through the existing inspection
path, while Find Path submission only reads
getCachedSegmentNodes(segmentId, skeletonLayer).Comparison with Graphene Find Path
The implementation intentionally follows Graphene's interaction and
datasource-owned persistence model, but the route providers and runtime
lifecycles differ.
grapheneFindPathspatialSkeletonFindPathGrapheneState.findPathStateSkeletonDataSourceState.findPathState/graph/find_pathserviceGraphConnectionGraphConnectionShared behavior
Both tools provide:
Lifecycle differences
Graphene uses the segmentation graph connection infrastructure:
A segmentation layer supports only one active segmentation graph, so
layer.graphConnectionimplicitly identifies the Find Path owner.Spatial skeletons are activated through mesh/skeleton subsources and may have
several active sources:
The explicit context map and active-route reconciliation are therefore required
for spatial skeletons but not for Graphene.
Invalidation and disposal behavior
skeleton datasource states while preserving endpoints.
completion from that source.
Implementation map
src/skeleton/find_path.tssrc/skeleton/find_path.spec.tssrc/skeleton/find_path_annotations.tssrc/skeleton/find_path_annotations.spec.tssrc/skeleton/navigation_graph.tssrc/skeleton/navigation_graph.spec.tslong-chain tests.
src/skeleton/spatial_skeleton_manager.tssrc/skeleton/spatial_skeleton_manager.spec.tsdisposal tests.
src/datasource/catmaid/frontend.tsSkeletonDataSourceStateconstruction andrestoration.
src/layer/segmentation/index.tsinvalidation.
src/layer/segmentation/index.spec.tstests.
src/skeleton/frontend.tssrc/ui/skeleton_edit_tools.tsUI.
src/ui/skeleton_edit_tools.spec.tsmulti-source tests.
src/ui/skeleton_edit_tools.csssrc/ui/skeleton_tab.tsdocs/user-guide/skeleton_editing.rstValidation
Focused test coverage includes:
Local validation performed:
catmaid_src/static/libs/neuroglancer/tfjs-library.bundle.js.Compatibility and non-goals
as JSON.
PR.