Skip to content
5 changes: 3 additions & 2 deletions src/datasource/catmaid/spatial_skeleton_commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,9 @@ async function commitAndApplyDeleteNode(
resolvedNode.node.segmentId,
) ?? [];
if (remainingNodes.length > 0) {
resolvedNode.skeletonLayer.retainOverlaySegment(resolvedNode.node.segmentId);
resolvedNode.skeletonLayer.retainOverlaySegment(
resolvedNode.node.segmentId,
);
} else {
resolvedNode.skeletonLayer.markSegmentEdited(resolvedNode.node.segmentId);
}
Expand Down Expand Up @@ -2132,7 +2134,6 @@ class MergeCommand implements SpatialSkeletonCommand {
if (deletedSkeletonId !== resultSkeletonId) {
firstNode.skeletonLayer.markSegmentEdited(deletedSkeletonId);
}
this.layer.clearSpatialSkeletonMergeAnchor();
await refreshTopologySegments(
this.layer,
[resultSkeletonId, deletedSkeletonId],
Expand Down
33 changes: 19 additions & 14 deletions src/layer/segmentation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,6 @@ import {
getSegmentIdFromLayerSelectionValue,
SpatialSkeletonHoverState,
} from "#src/layer/segmentation/selection.js";
import {
executeSpatialSkeletonDeleteNode,
executeSpatialSkeletonNodeConfidenceUpdate,
executeSpatialSkeletonNodeDescriptionUpdate,
executeSpatialSkeletonNodeRadiusUpdate,
executeSpatialSkeletonReroot,
executeSpatialSkeletonNodeTrueEndUpdate,
showSpatialSkeletonActionError,
} from "#src/layer/segmentation/spatial_skeleton_commands.js";
import {
MeshLayer,
MeshSource,
Expand Down Expand Up @@ -138,6 +129,15 @@ import {
SpatialSkeletonDisplayNodeType,
SpatialSkeletonNodeFilterType,
} from "#src/skeleton/node_types.js";
import {
executeSpatialSkeletonDeleteNode,
executeSpatialSkeletonNodeConfidenceUpdate,
executeSpatialSkeletonNodeDescriptionUpdate,
executeSpatialSkeletonNodeRadiusUpdate,
executeSpatialSkeletonReroot,
executeSpatialSkeletonNodeTrueEndUpdate,
showSpatialSkeletonActionError,
} from "#src/skeleton/spatial_skeleton_commands.js";
import {
editableSpatiallyIndexedSkeletonSourceSupportsAction,
getEditableSpatiallyIndexedSkeletonSource,
Expand Down Expand Up @@ -2449,7 +2449,8 @@ export class SegmentationUserLayer extends Base {
let committedTrueEnd = nodeHasTrueEnd;
let leafTypeSavePending = false;
const leafTypeEditor = document.createElement("div");
leafTypeEditor.className = "neuroglancer-selection-details-skeleton-leaf-type";
leafTypeEditor.className =
"neuroglancer-selection-details-skeleton-leaf-type";
const leafTypeRadioName = `neuroglancer-selection-details-skeleton-leaf-type-${segmentId}-${fullNodeInfo.nodeId}`;
const leafTypeOptionElements: HTMLLabelElement[] = [];
const makeLeafTypeOption = (options: {
Expand All @@ -2458,15 +2459,17 @@ export class SegmentationUserLayer extends Base {
trueEnd: boolean;
}) => {
const option = document.createElement("label");
option.className = "neuroglancer-selection-details-skeleton-leaf-type-option";
option.className =
"neuroglancer-selection-details-skeleton-leaf-type-option";
const input = document.createElement("input");
input.type = "radio";
input.name = leafTypeRadioName;
input.value = options.trueEnd ? "trueEnd" : "virtualEnd";
input.className =
"neuroglancer-selection-details-skeleton-leaf-type-option-input";
const icon = document.createElement("span");
icon.className = "neuroglancer-selection-details-skeleton-leaf-type-option-icon";
icon.className =
"neuroglancer-selection-details-skeleton-leaf-type-option-icon";
icon.appendChild(
makeIcon({
svg: options.svg,
Expand All @@ -2475,7 +2478,8 @@ export class SegmentationUserLayer extends Base {
}),
);
const text = document.createElement("span");
text.className = "neuroglancer-selection-details-skeleton-leaf-type-option-text";
text.className =
"neuroglancer-selection-details-skeleton-leaf-type-option-text";
text.textContent = options.label;
option.appendChild(input);
option.appendChild(icon);
Expand Down Expand Up @@ -2639,7 +2643,8 @@ export class SegmentationUserLayer extends Base {
} else {
let committedRadius = fullNodeInfo.radius ?? 0;
const radiusInput = document.createElement("input");
radiusInput.className = "neuroglancer-selection-details-skeleton-properties-input";
radiusInput.className =
"neuroglancer-selection-details-skeleton-properties-input";
radiusInput.type = "number";
radiusInput.step = "any";
radiusInput.value = formatSpatialSkeletonEditableNumber(
Expand Down
3 changes: 2 additions & 1 deletion src/layer/segmentation/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,8 @@
justify-content: center;
}

.neuroglancer-selection-details-skeleton-leaf-type-option-icon .neuroglancer-icon {
.neuroglancer-selection-details-skeleton-leaf-type-option-icon
.neuroglancer-icon {
min-width: 14px;
min-height: 14px;
}
Expand Down
4 changes: 3 additions & 1 deletion src/skeleton/frontend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1824,7 +1824,9 @@ interface SelectedSkeletonNodeInfo {

interface SpatiallyIndexedSkeletonLayerOptions {
sources2d?: SpatiallyIndexedSkeletonSourceEntry[];
selectedNodeInfo?: WatchableValueInterface<SelectedSkeletonNodeInfo | undefined>;
selectedNodeInfo?: WatchableValueInterface<
SelectedSkeletonNodeInfo | undefined
>;
pendingNodePositionVersion?: WatchableValueInterface<number>;
getPendingNodePosition?: (nodeId: number) => ArrayLike<number> | undefined;
getCachedNode?: (nodeId: number) => SpatiallyIndexedSkeletonNode | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ import { afterEach, describe, expect, it, vi } from "vitest";
import { makeCatmaidNodeSourceState } from "#src/datasource/catmaid/api.js";
import { buildCatmaidNeighborhoodEditContext } from "#src/datasource/catmaid/edit_state.js";
import { CatmaidSpatialSkeletonEditCommands } from "#src/datasource/catmaid/spatial_skeleton_commands.js";
import { SpatialSkeletonActions } from "#src/skeleton/actions.js";
import type { SpatiallyIndexedSkeletonNode } from "#src/skeleton/api.js";
import { SpatialSkeletonCommandHistory } from "#src/skeleton/command_history.js";
import {
findSpatiallyIndexedSkeletonNode,
getSpatiallyIndexedSkeletonDirectChildren,
getSpatiallyIndexedSkeletonNodeParent,
} from "#src/skeleton/node_traversal.js";
import {
executeSpatialSkeletonAddNode,
executeSpatialSkeletonDeleteNode,
Expand All @@ -33,15 +41,7 @@ import {
executeSpatialSkeletonSplit,
redoSpatialSkeletonCommand,
undoSpatialSkeletonCommand,
} from "#src/layer/segmentation/spatial_skeleton_commands.js";
import { SpatialSkeletonActions } from "#src/skeleton/actions.js";
import type { SpatiallyIndexedSkeletonNode } from "#src/skeleton/api.js";
import { SpatialSkeletonCommandHistory } from "#src/skeleton/command_history.js";
import {
findSpatiallyIndexedSkeletonNode,
getSpatiallyIndexedSkeletonDirectChildren,
getSpatiallyIndexedSkeletonNodeParent,
} from "#src/skeleton/node_traversal.js";
} from "#src/skeleton/spatial_skeleton_commands.js";
import { SpatialSkeletonState } from "#src/skeleton/spatial_skeleton_manager.js";
import { StatusMessage } from "#src/status.js";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import type { SegmentationUserLayer } from "#src/layer/segmentation/index.js";
import {
SpatialSkeletonActions,
type SpatialSkeletonAction,
Expand All @@ -32,6 +31,7 @@ import { getSpatialSkeletonActionErrorMessage } from "#src/skeleton/edit_errors.
import {
getEditableSpatiallyIndexedSkeletonSource,
getSpatialSkeletonEditCommandFactoryForAction,
type SpatialSkeletonLayerContext,
} from "#src/skeleton/spatial_skeleton_manager.js";
import { StatusMessage } from "#src/status.js";

Expand All @@ -40,7 +40,7 @@ interface SpatialSkeletonSourceAccess {
}

function getEditSource(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
): EditableSpatiallyIndexedSkeletonSource {
const source = getEditableSpatiallyIndexedSkeletonSource(
layer.getSpatiallyIndexedSkeletonLayer(),
Expand All @@ -63,7 +63,7 @@ export function getSpatialSkeletonEditCommandFactory(
}

function executeCommand(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
command: SpatialSkeletonCommand,
) {
return layer.spatialSkeletonState.commandHistory.execute(command);
Expand All @@ -88,7 +88,7 @@ export function showSpatialSkeletonActionError(action: string, error: unknown) {
}

function createSpatialSkeletonCommand(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
action: SpatialSkeletonAction,
payload: SpatialSkeletonCommandPayload,
unsupportedMessage: string,
Expand All @@ -101,7 +101,10 @@ function createSpatialSkeletonCommand(
if (commandFactory === undefined) {
throw new Error(unsupportedMessage);
}
return commandFactory.createCommand(layer, payload);
// The concrete createCommand implementations expect the full layer type; the
// cast is safe because in practice layer always satisfies those requirements.

return commandFactory.createCommand(layer as any, payload);
}

interface SpatialSkeletonExecutionMetadata {
Expand Down Expand Up @@ -198,7 +201,7 @@ const spatialSkeletonExecutionMetadata = new Map<
]);

function executeSpatialSkeletonAction(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
action: SpatialSkeletonAction,
payload: SpatialSkeletonCommandPayload,
) {
Expand All @@ -219,7 +222,7 @@ function executeSpatialSkeletonAction(
}

export function executeSpatialSkeletonAddNode(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
options: SpatialSkeletonCommandPayload,
) {
return executeSpatialSkeletonAction(
Expand All @@ -230,7 +233,7 @@ export function executeSpatialSkeletonAddNode(
}

export function executeSpatialSkeletonInsertNode(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
options: SpatialSkeletonCommandPayload,
) {
return executeSpatialSkeletonAction(
Expand All @@ -241,7 +244,7 @@ export function executeSpatialSkeletonInsertNode(
}

export function executeSpatialSkeletonMoveNode(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
options: SpatialSkeletonCommandPayload,
) {
return executeSpatialSkeletonAction(
Expand All @@ -252,7 +255,7 @@ export function executeSpatialSkeletonMoveNode(
}

export function executeSpatialSkeletonDeleteNode(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
node: SpatiallyIndexedSkeletonNode,
) {
return executeSpatialSkeletonAction(
Expand All @@ -263,7 +266,7 @@ export function executeSpatialSkeletonDeleteNode(
}

export function executeSpatialSkeletonNodeDescriptionUpdate(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
options: SpatialSkeletonCommandPayload,
) {
return executeSpatialSkeletonAction(
Expand All @@ -274,7 +277,7 @@ export function executeSpatialSkeletonNodeDescriptionUpdate(
}

export function executeSpatialSkeletonNodeTrueEndUpdate(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
options: SpatialSkeletonCommandPayload,
) {
return executeSpatialSkeletonAction(
Expand All @@ -285,7 +288,7 @@ export function executeSpatialSkeletonNodeTrueEndUpdate(
}

export function executeSpatialSkeletonNodeRadiusUpdate(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
options: SpatialSkeletonCommandPayload,
) {
return executeSpatialSkeletonAction(
Expand All @@ -296,7 +299,7 @@ export function executeSpatialSkeletonNodeRadiusUpdate(
}

export function executeSpatialSkeletonNodeConfidenceUpdate(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
options: SpatialSkeletonCommandPayload,
) {
return executeSpatialSkeletonAction(
Expand All @@ -307,7 +310,7 @@ export function executeSpatialSkeletonNodeConfidenceUpdate(
}

export function executeSpatialSkeletonReroot(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
node: SpatialSkeletonCommandPayload,
) {
return executeSpatialSkeletonAction(
Expand All @@ -318,7 +321,7 @@ export function executeSpatialSkeletonReroot(
}

export function executeSpatialSkeletonSplit(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
node: SpatialSkeletonCommandPayload,
) {
return executeSpatialSkeletonAction(
Expand All @@ -329,7 +332,7 @@ export function executeSpatialSkeletonSplit(
}

export function executeSpatialSkeletonMerge(
layer: SegmentationUserLayer,
layer: SpatialSkeletonLayerContext,
firstNode: SpatialSkeletonCommandPayload,
secondNode: SpatialSkeletonCommandPayload,
) {
Expand All @@ -340,15 +343,19 @@ export function executeSpatialSkeletonMerge(
);
}

export async function undoSpatialSkeletonCommand(layer: SegmentationUserLayer) {
export async function undoSpatialSkeletonCommand(
layer: SpatialSkeletonLayerContext,
) {
const changed = await layer.spatialSkeletonState.commandHistory.undo();
if (!changed) {
return false;
}
return true;
}

export async function redoSpatialSkeletonCommand(layer: SegmentationUserLayer) {
export async function redoSpatialSkeletonCommand(
layer: SpatialSkeletonLayerContext,
) {
const changed = await layer.spatialSkeletonState.commandHistory.redo();
if (!changed) {
return false;
Expand Down
5 changes: 5 additions & 0 deletions src/skeleton/spatial_skeleton_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -911,3 +911,8 @@ export class SpatialSkeletonState extends RefCounted {
this.cachedNodesById.clear();
}
}

export interface SpatialSkeletonLayerContext {
getSpatiallyIndexedSkeletonLayer(): SpatiallyIndexedSkeletonLayer | undefined;
readonly spatialSkeletonState: SpatialSkeletonState;
}
13 changes: 8 additions & 5 deletions src/ui/skeleton_edit_tools.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ import { afterEach, describe, expect, it, vi } from "vitest";

import { makeCatmaidNodeSourceState } from "#src/datasource/catmaid/api.js";
import { CatmaidSpatialSkeletonEditCommands } from "#src/datasource/catmaid/spatial_skeleton_commands.js";
import {
executeSpatialSkeletonAddNode,
executeSpatialSkeletonMerge,
} from "#src/layer/segmentation/spatial_skeleton_commands.js";
import {
SpatialSkeletonActions,
type SpatialSkeletonAction,
} from "#src/skeleton/actions.js";
import type { SpatiallyIndexedSkeletonNode } from "#src/skeleton/api.js";
import { SpatialSkeletonCommandHistory } from "#src/skeleton/command_history.js";
import {
executeSpatialSkeletonAddNode,
executeSpatialSkeletonMerge,
} from "#src/skeleton/spatial_skeleton_commands.js";
import { StatusMessage } from "#src/status.js";

if (!("WebGL2RenderingContext" in globalThis)) {
Expand Down Expand Up @@ -737,7 +737,10 @@ describe("spatial_skeleton_edit_tool", () => {
},
},
spatialSkeletonMergeMode: makeModeWatchable(),
selectedSpatialSkeletonNodeInfo: { value: selectedNode, changed: makeChangedSignal() },
selectedSpatialSkeletonNodeInfo: {
value: selectedNode,
changed: makeChangedSignal(),
},
spatialSkeletonState: {
mergeAnchorNodeId,
getCachedNode: vi.fn(),
Expand Down
Loading