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

Remove segment from list and add undo/redo for segments #6944

Merged
merged 14 commits into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
7 changes: 5 additions & 2 deletions frontend/javascripts/libs/diffable_map.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
const defaultItemsPerBatch = 1000;
let idCounter = 0;
const idSymbol = Symbol("id"); // DiffableMap is an immutable key-value data structure which supports fast diffing.
const idSymbol = Symbol("id");

// DiffableMap is an immutable key-value data structure which supports fast diffing.
// Updating a DiffableMap returns a new DiffableMap, in which case the Maps are
// derived from each other ("dependent").
// Diffing is very fast when the given Maps are dependent, since the separate chunks
// can be compared shallowly.
// The insertion order of the DiffableMap is not guaranteed.
// Stored values may be null. However, `undefined` is equal to "does not exist".
// Stored values may be null. However, be careful when dealing with `undefined`, as
// it's interpreted as "does not exist", but can still be listed during enumeration.

class DiffableMap<K extends number, V> {
chunks: Array<Map<K, V>>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,18 @@ export async function loadSynapsesOfAgglomerateAtPosition(position: Vector3) {
const isConnectomeEnabled = hasConnectomeFile(state);

if (mappingName && isConnectomeEnabled.value) {
const cellId = await getSegmentIdForPositionAsync(position);
Store.dispatch(setActiveConnectomeAgglomerateIdsAction(segmentation.name, [cellId]));
const segmentId = await getSegmentIdForPositionAsync(position);
Store.dispatch(setActiveConnectomeAgglomerateIdsAction(segmentation.name, [segmentId]));
} else {
Toast.error(isConnectomeEnabled.reason);
}
}
export function handleClickSegment(clickPosition: Point2) {
const state = Store.getState();
const globalPosition = calculateGlobalPos(state, clickPosition);
const cellId = getSegmentIdForPosition(globalPosition);
const segmentId = getSegmentIdForPosition(globalPosition);

if (cellId > 0) {
Store.dispatch(clickSegmentAction(cellId, globalPosition));
if (segmentId > 0) {
Store.dispatch(clickSegmentAction(segmentId, globalPosition));
}
}
28 changes: 14 additions & 14 deletions frontend/javascripts/oxalis/model/actions/annotation_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,37 +260,37 @@ export const maybeFetchMeshFilesAction = (
callback,
} as const);

export const triggerIsosurfaceDownloadAction = (cellName: string, cellId: number) =>
export const triggerIsosurfaceDownloadAction = (cellName: string, segmentId: number) =>
({
type: "TRIGGER_ISOSURFACE_DOWNLOAD",
cellName,
cellId,
segmentId,
} as const);

export const refreshIsosurfacesAction = () =>
({
type: "REFRESH_ISOSURFACES",
} as const);

export const refreshIsosurfaceAction = (layerName: string, cellId: number) =>
export const refreshIsosurfaceAction = (layerName: string, segmentId: number) =>
({
type: "REFRESH_ISOSURFACE",
layerName,
cellId,
segmentId,
} as const);

export const startedLoadingIsosurfaceAction = (layerName: string, cellId: number) =>
export const startedLoadingIsosurfaceAction = (layerName: string, segmentId: number) =>
({
type: "STARTED_LOADING_ISOSURFACE",
layerName,
cellId,
segmentId,
} as const);

export const finishedLoadingIsosurfaceAction = (layerName: string, cellId: number) =>
export const finishedLoadingIsosurfaceAction = (layerName: string, segmentId: number) =>
({
type: "FINISHED_LOADING_ISOSURFACE",
layerName,
cellId,
segmentId,
} as const);

export const updateMeshFileListAction = (layerName: string, meshFiles: Array<APIMeshFile>) =>
Expand All @@ -317,39 +317,39 @@ export const importIsosurfaceFromStlAction = (layerName: string, buffer: ArrayBu
buffer,
} as const);

export const removeIsosurfaceAction = (layerName: string, cellId: number) =>
export const removeIsosurfaceAction = (layerName: string, segmentId: number) =>
({
type: "REMOVE_ISOSURFACE",
layerName,
cellId,
segmentId,
} as const);

export const addAdHocIsosurfaceAction = (
layerName: string,
cellId: number,
segmentId: number,
seedPosition: Vector3,
mappingName: string | null | undefined,
mappingType: MappingType | null | undefined,
) =>
({
type: "ADD_AD_HOC_ISOSURFACE",
layerName,
cellId,
segmentId,
seedPosition,
mappingName,
mappingType,
} as const);

export const addPrecomputedIsosurfaceAction = (
layerName: string,
cellId: number,
segmentId: number,
seedPosition: Vector3,
meshFileName: string,
) =>
({
type: "ADD_PRECOMPUTED_ISOSURFACE",
layerName,
cellId,
segmentId,
seedPosition,
meshFileName,
} as const);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,28 @@ export type LoadPrecomputedMeshAction = ReturnType<typeof loadPrecomputedMeshAct
export type SegmentationAction = LoadAdHocMeshAction | LoadPrecomputedMeshAction;

export const loadAdHocMeshAction = (
cellId: number,
segmentId: number,
seedPosition: Vector3,
extraInfo?: AdHocIsosurfaceInfo,
layerName?: string,
) =>
({
type: "LOAD_AD_HOC_MESH_ACTION",
cellId,
segmentId,
seedPosition,
extraInfo,
layerName,
} as const);

export const loadPrecomputedMeshAction = (
cellId: number,
segmentId: number,
seedPosition: Vector3,
meshFileName: string,
layerName?: string,
) =>
({
type: "LOAD_PRECOMPUTED_MESH_ACTION",
cellId,
segmentId,
seedPosition,
meshFileName,
layerName,
Expand Down
27 changes: 21 additions & 6 deletions frontend/javascripts/oxalis/model/actions/volumetracing_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type ImportVolumeTracingAction = ReturnType<typeof importVolumeTracingAct
export type SetLargestSegmentIdAction = ReturnType<typeof setLargestSegmentIdAction>;
export type SetSegmentsAction = ReturnType<typeof setSegmentsAction>;
export type UpdateSegmentAction = ReturnType<typeof updateSegmentAction>;
export type RemoveSegmentAction = ReturnType<typeof removeSegmentAction>;
export type SetMappingIsEditableAction = ReturnType<typeof setMappingIsEditableAction>;

export type ComputeQuickSelectForRectAction = ReturnType<typeof computeQuickSelectForRectAction>;
Expand All @@ -61,6 +62,7 @@ export type VolumeTracingAction =
| SetContourTracingModeAction
| SetSegmentsAction
| UpdateSegmentAction
| RemoveSegmentAction
| AddBucketToUndoAction
| ImportVolumeTracingAction
| SetLargestSegmentIdAction
Expand All @@ -76,6 +78,7 @@ export const VolumeTracingSaveRelevantActions = [
"SET_ACTIVE_CELL",
"FINISH_ANNOTATION_STROKE",
"UPDATE_SEGMENT",
"REMOVE_SEGMENT",
"SET_SEGMENTS",
...AllUserBoundingBoxActions,
// Note that the following two actions are defined in settings_actions.ts
Expand Down Expand Up @@ -141,17 +144,17 @@ export const finishEditingAction = () =>
type: "FINISH_EDITING",
} as const);

export const setActiveCellAction = (cellId: number, somePosition?: Vector3) =>
export const setActiveCellAction = (segmentId: number, somePosition?: Vector3) =>
({
type: "SET_ACTIVE_CELL",
cellId,
segmentId,
somePosition,
} as const);

export const clickSegmentAction = (cellId: number, somePosition: Vector3) =>
export const clickSegmentAction = (segmentId: number, somePosition: Vector3) =>
({
type: "CLICK_SEGMENT",
cellId,
segmentId,
somePosition,
} as const);

Expand All @@ -176,6 +179,18 @@ export const updateSegmentAction = (
timestamp,
} as const);

export const removeSegmentAction = (
segmentId: number,
layerName: string,
timestamp: number = Date.now(),
) =>
({
type: "REMOVE_SEGMENT",
segmentId,
layerName,
timestamp,
} as const);

export const interpolateSegmentationLayerAction = () =>
({
type: "INTERPOLATE_SEGMENTATION_LAYER",
Expand Down Expand Up @@ -236,10 +251,10 @@ export const importVolumeTracingAction = () =>
type: "IMPORT_VOLUMETRACING",
} as const);

export const setLargestSegmentIdAction = (cellId: number) =>
export const setLargestSegmentIdAction = (segmentId: number) =>
({
type: "SET_LARGEST_SEGMENT_ID",
cellId,
segmentId,
} as const);

export const dispatchFloodfillAsync = async (
Expand Down
28 changes: 14 additions & 14 deletions frontend/javascripts/oxalis/model/bucket_data_handling/data_cube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ class DataCube {
async _labelVoxelInAllResolutions_DEPRECATED(
voxel: Vector3,
label: number,
activeCellId?: number | null | undefined,
activeSegmentId?: number | null | undefined,
): Promise<void> {
// This function is only provided for the wK front-end api and should not be used internally,
// since it only operates on one voxel and therefore is not performance-optimized.
Expand All @@ -360,7 +360,7 @@ class DataCube {

for (const [resolutionIndex] of this.resolutionInfo.getResolutionsWithIndices()) {
promises.push(
this._labelVoxelInResolution_DEPRECATED(voxel, label, resolutionIndex, activeCellId),
this._labelVoxelInResolution_DEPRECATED(voxel, label, resolutionIndex, activeSegmentId),
);
}

Expand All @@ -372,7 +372,7 @@ class DataCube {
voxel: Vector3,
label: number,
zoomStep: number,
activeCellId: number | null | undefined,
activeSegmentId: number | null | undefined,
): Promise<void> {
let voxelInCube = true;

Expand All @@ -388,9 +388,9 @@ class DataCube {
const voxelIndex = this.getVoxelIndex(voxel, zoomStep);
let shouldUpdateVoxel = true;

if (activeCellId != null) {
if (activeSegmentId != null) {
const voxelValue = this.getMappedDataValue(voxel, zoomStep);
shouldUpdateVoxel = activeCellId === voxelValue;
shouldUpdateVoxel = activeSegmentId === voxelValue;
}

if (shouldUpdateVoxel) {
Expand All @@ -406,7 +406,7 @@ class DataCube {

async floodFill(
globalSeedVoxel: Vector3,
cellIdNumber: number,
segmentIdNumber: number,
dimensionIndices: DimensionMap,
floodfillBoundingBox: BoundingBoxType,
zoomStep: number,
Expand Down Expand Up @@ -460,11 +460,11 @@ class DataCube {

const seedVoxelIndex = this.getVoxelIndex(globalSeedVoxel, zoomStep);
const seedBucketData = seedBucket.getOrCreateData();
const sourceCellId = seedBucketData[seedVoxelIndex];
const sourceSegmentId = seedBucketData[seedVoxelIndex];

const cellId = castForArrayType(cellIdNumber, seedBucketData);
const segmentId = castForArrayType(segmentIdNumber, seedBucketData);

if (sourceCellId === cellId) {
if (sourceSegmentId === segmentId) {
return {
bucketsWithLabeledVoxelsMap,
wasBoundingBoxExceeded: false,
Expand Down Expand Up @@ -534,15 +534,15 @@ class DataCube {
const bucketData = await currentBucket.getDataForMutation();
const initialVoxelIndex = this.getVoxelIndexByVoxelOffset(initialXyzVoxelInBucket);

if (bucketData[initialVoxelIndex] !== sourceCellId) {
// Ignoring neighbour buckets whose cellId at the initial voxel does not match the source cell id.
if (bucketData[initialVoxelIndex] !== sourceSegmentId) {
// Ignoring neighbour buckets whose segmentId at the initial voxel does not match the source cell id.
continue;
}

// Add the bucket to the current volume undo batch, if it isn't already part of it.
currentBucket.startDataMutation();
// Mark the initial voxel.
bucketData[initialVoxelIndex] = cellId;
bucketData[initialVoxelIndex] = segmentId;
// Create an array saving the labeled voxel of the current slice for the current bucket, if there isn't already one.
const currentLabeledVoxelMap =
bucketsWithLabeledVoxelsMap.get(currentBucket.zoomedAddress) || new Map();
Expand Down Expand Up @@ -609,8 +609,8 @@ class DataCube {
// Label the current neighbour and add it to the neighbourVoxelStackUvw to iterate over its neighbours.
const neighbourVoxelIndex = this.getVoxelIndexByVoxelOffset(neighbourVoxelXyz);

if (bucketData[neighbourVoxelIndex] === sourceCellId) {
bucketData[neighbourVoxelIndex] = cellId;
if (bucketData[neighbourVoxelIndex] === sourceSegmentId) {
bucketData[neighbourVoxelIndex] = segmentId;
markUvwInSliceAsLabeled(neighbourVoxelUvw);
neighbourVoxelStackUvw.pushVoxel(neighbourVoxelUvw);
labeledVoxelCount++;
Expand Down
Loading