Skip to content

Commit 477cc44

Browse files
committed
# This is a combination of 5 commits.
# This is the 1st commit message: show error in context menu if clicked node wasn't found (instead of crashing whole page); remove maybe container from findTreeById # This is the commit message #2: remove maybe container from findTreeByName; remove dead REMOVE_AGGLOMERATE_SKELETON action code # This is the commit message #3: fix incorrect encoding/decoding of node ids between webGL and JS # This is the commit message #4: update changelog # This is the commit message #5: Update config.yml
1 parent 5995054 commit 477cc44

File tree

13 files changed

+68
-74
lines changed

13 files changed

+68
-74
lines changed

.circleci/config.yml

+2
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ workflows:
289289
circleci_build:
290290
jobs:
291291
- build_test_deploy:
292+
context:
293+
- DockerHub
292294
filters:
293295
tags:
294296
only: /.*/

CHANGELOG.unreleased.md

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
1616
- Improved performance of opening a dataset or annotation. [#6711](https://github.com/scalableminds/webknossos/pull/6711)
1717

1818
### Fixed
19+
- Fixed node selection and context menu for node ids greater than 130813. [#6724](https://github.com/scalableminds/webknossos/pull/6724)
1920

2021
### Removed
2122

frontend/javascripts/oxalis/api/api_latest.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ class TracingApi {
311311
const tree =
312312
treeId != null
313313
? skeletonTracing.trees[treeId]
314-
: findTreeByNodeId(skeletonTracing.trees, nodeId).get();
314+
: findTreeByNodeId(skeletonTracing.trees, nodeId);
315315
assertExists(tree, `Couldn't find node ${nodeId}.`);
316316
Store.dispatch(createCommentAction(commentText, nodeId, tree.treeId));
317317
} else {

frontend/javascripts/oxalis/api/api_v2.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ class TracingApi {
159159
const tree =
160160
treeId != null
161161
? skeletonTracing.trees[treeId]
162-
: findTreeByNodeId(skeletonTracing.trees, nodeId).get();
162+
: findTreeByNodeId(skeletonTracing.trees, nodeId);
163163
assertExists(tree, `Couldn't find node ${nodeId}.`);
164164
Store.dispatch(createCommentAction(commentText, nodeId, tree.treeId));
165165
} else {

frontend/javascripts/oxalis/controller/combinations/skeleton_handlers.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,8 @@ export function maybeGetNodeIdFromPosition(
331331
// compute the index of the pixel under the cursor,
332332
// while inverting along the y-axis, because WebGL has its origin bottom-left :/
333333
const index = (x + (height - y) * width) * 4;
334-
// the nodeId can be reconstructed by interpreting the RGB values of the pixel as a base-255 number
335-
const nodeId = buffer.subarray(index, index + 3).reduce((a, b) => a * 255 + b, 0);
334+
// the nodeId can be reconstructed by interpreting the RGB values of the pixel as a base-256 number
335+
const nodeId = buffer.subarray(index, index + 3).reduce((a, b) => a * 256 + b, 0);
336336
skeleton.stopPicking();
337337
// prevent flickering sometimes caused by picking
338338
planeView.renderFunction(true);

frontend/javascripts/oxalis/geometries/materials/node_shader.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,12 @@ void main() {
185185
186186
// NODE COLOR FOR PICKING
187187
if (isPicking == 1) {
188-
// the nodeId is encoded in the RGB channels as a 3 digit base-255 number in a number of steps:
189-
// - nodeId is divided by the first three powers of 255.
188+
// the nodeId is encoded in the RGB channels as a 3 digit base-256 number in a number of steps:
189+
// - nodeId is divided by the first three powers of 256.
190190
// - each quotient is rounded down to the nearest integer (since the fractional part of each quotient is covered by a less significant digit)
191-
// - each digit is divided by 255 again, since color values in OpenGL must be in the range [0, 1]
191+
// - each digit is divided by 256 again, since color values in OpenGL must be in the range [0, 1]
192192
// - finally, the non-fractional part of each digit is removed (since it is covered by a more significant digit)
193-
color = fract(floor(nodeId / vec3(255.0 * 255.0, 255.0, 1.0)) / 255.0);
193+
color = fract(floor(nodeId / vec3(256.0 * 256.0, 256.0, 1.0)) / 256.0);
194194
// Enlarge the nodes on mobile, so they're easier to select
195195
gl_PointSize = isTouch == 1 ? max(gl_PointSize * 1.5, 30.0) : max(gl_PointSize * 1.5, 15.0);
196196
return;

frontend/javascripts/oxalis/model/accessors/skeletontracing_accessor.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,11 @@ export function getActiveNodeFromTree(skeletonTracing: SkeletonTracing, tree: Tr
9797

9898
return Maybe.Nothing();
9999
}
100-
export function findTreeByNodeId(trees: TreeMap, nodeId: number): Maybe<Tree> {
101-
return Maybe.fromNullable(_.values(trees).find((tree) => tree.nodes.has(nodeId)));
100+
export function findTreeByNodeId(trees: TreeMap, nodeId: number): Tree | undefined {
101+
return _.values(trees).find((tree) => tree.nodes.has(nodeId));
102102
}
103-
export function findTreeByName(trees: TreeMap, treeName: string): Maybe<Tree> {
104-
return Maybe.fromNullable(_.values(trees).find((tree: Tree) => tree.name === treeName));
103+
export function findTreeByName(trees: TreeMap, treeName: string): Tree | undefined {
104+
return _.values(trees).find((tree: Tree) => tree.name === treeName);
105105
}
106106
export function getTree(
107107
skeletonTracing: SkeletonTracing,

frontend/javascripts/oxalis/model/actions/skeletontracing_actions.tsx

+1-15
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ type SetShowSkeletonsAction = ReturnType<typeof setShowSkeletonsAction>;
5656
type SetMergerModeEnabledAction = ReturnType<typeof setMergerModeEnabledAction>;
5757
type UpdateNavigationListAction = ReturnType<typeof updateNavigationListAction>;
5858
export type LoadAgglomerateSkeletonAction = ReturnType<typeof loadAgglomerateSkeletonAction>;
59-
export type RemoveAgglomerateSkeletonAction = ReturnType<typeof removeAgglomerateSkeletonAction>;
6059
type NoAction = ReturnType<typeof noAction>;
6160

6261
export type SkeletonTracingAction =
@@ -102,8 +101,7 @@ export type SkeletonTracingAction =
102101
| SetShowSkeletonsAction
103102
| SetMergerModeEnabledAction
104103
| UpdateNavigationListAction
105-
| LoadAgglomerateSkeletonAction
106-
| RemoveAgglomerateSkeletonAction;
104+
| LoadAgglomerateSkeletonAction;
107105

108106
export const SkeletonTracingSaveRelevantActions = [
109107
"INITIALIZE_SKELETONTRACING",
@@ -528,15 +526,3 @@ export const loadAgglomerateSkeletonAction = (
528526
mappingName,
529527
agglomerateId,
530528
} as const);
531-
532-
export const removeAgglomerateSkeletonAction = (
533-
layerName: string,
534-
mappingName: string,
535-
agglomerateId: number,
536-
) =>
537-
({
538-
type: "REMOVE_AGGLOMERATE_SKELETON",
539-
layerName,
540-
mappingName,
541-
agglomerateId,
542-
} as const);

frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer.ts

+21-20
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ function SkeletonTracingReducer(state: OxalisState, action: Action): OxalisState
5353
const activeTreeIdMaybe = activeNodeIdMaybe
5454
.chain((nodeId) => {
5555
// use activeNodeId to find active tree
56-
const treeIdMaybe = findTreeByNodeId(trees, nodeId).map((tree) => tree.treeId);
56+
const treeIdMaybe = findTreeByNodeId(trees, nodeId)?.treeId;
5757

58-
if (treeIdMaybe.isNothing) {
58+
if (treeIdMaybe == null) {
5959
// There is an activeNodeId without a corresponding tree.
6060
// Warn the user, since this shouldn't happen, but clear the activeNodeId
6161
// so that wk is usable.
@@ -68,7 +68,7 @@ function SkeletonTracingReducer(state: OxalisState, action: Action): OxalisState
6868
activeNodeId = null;
6969
}
7070

71-
return treeIdMaybe;
71+
return Maybe.fromNullable(treeIdMaybe);
7272
})
7373
.orElse(() => {
7474
// use last tree for active tree
@@ -127,25 +127,26 @@ function SkeletonTracingReducer(state: OxalisState, action: Action): OxalisState
127127
switch (action.type) {
128128
case "SET_ACTIVE_NODE": {
129129
const { nodeId } = action;
130-
return findTreeByNodeId(skeletonTracing.trees, nodeId)
131-
.map((tree) =>
132-
update(state, {
133-
tracing: {
134-
skeleton: {
135-
activeNodeId: {
136-
$set: nodeId,
137-
},
138-
activeTreeId: {
139-
$set: tree.treeId,
140-
},
141-
activeGroupId: {
142-
$set: null,
143-
},
130+
const tree = findTreeByNodeId(skeletonTracing.trees, nodeId);
131+
if (tree) {
132+
return update(state, {
133+
tracing: {
134+
skeleton: {
135+
activeNodeId: {
136+
$set: nodeId,
137+
},
138+
activeTreeId: {
139+
$set: tree.treeId,
140+
},
141+
activeGroupId: {
142+
$set: null,
144143
},
145144
},
146-
}),
147-
)
148-
.getOrElse(state);
145+
},
146+
});
147+
} else {
148+
return state;
149+
}
149150
}
150151

151152
case "SET_NODE_RADIUS": {

frontend/javascripts/oxalis/model/reducers/skeletontracing_reducer_helpers.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,10 @@ export function deleteNode(
215215

216216
const newActiveNodeId = neighborIds.length > 0 ? Math.min(...neighborIds) : null;
217217
const newActiveTree =
218-
newActiveNodeId != null ? findTreeByNodeId(newTrees, newActiveNodeId).get() : activeTree;
218+
newActiveNodeId != null ? findTreeByNodeId(newTrees, newActiveNodeId) : activeTree;
219+
if (newActiveTree == null) {
220+
throw new Error(`Could not find node with id ${newActiveNodeId}`);
221+
}
219222
const newActiveTreeId = newActiveTree.treeId;
220223
return Maybe.Just([newTrees, newActiveTreeId, newActiveNodeId, newMaxNodeId]);
221224
});
@@ -227,7 +230,7 @@ export function deleteEdge(
227230
targetTree: Tree,
228231
targetNode: Node,
229232
timestamp: number,
230-
): Maybe<[TreeMap, number | null]> {
233+
): Maybe<[TreeMap, number | null | undefined]> {
231234
return getSkeletonTracing(state.tracing).chain((skeletonTracing) => {
232235
if (sourceTree.treeId !== targetTree.treeId) {
233236
// The two selected nodes are in different trees
@@ -257,9 +260,7 @@ export function deleteEdge(
257260
);
258261
// The treeId of the tree the active node belongs to could have changed
259262
const activeNodeId = skeletonTracing.activeNodeId;
260-
const newActiveTreeId = activeNodeId
261-
? findTreeByNodeId(newTrees, activeNodeId).get().treeId
262-
: null;
263+
const newActiveTreeId = activeNodeId ? findTreeByNodeId(newTrees, activeNodeId)?.treeId : null;
263264
return Maybe.Just([newTrees, newActiveTreeId]);
264265
});
265266
}
@@ -647,8 +648,8 @@ export function mergeTrees(
647648
targetNodeId: number,
648649
): Maybe<[TreeMap, number, number]> {
649650
const { trees } = skeletonTracing;
650-
const sourceTree = findTreeByNodeId(trees, sourceNodeId).get();
651-
const targetTree = findTreeByNodeId(trees, targetNodeId).get(); // should be activeTree
651+
const sourceTree = findTreeByNodeId(trees, sourceNodeId);
652+
const targetTree = findTreeByNodeId(trees, targetNodeId); // should be activeTree
652653

653654
if (sourceTree == null || targetTree == null || sourceTree === targetTree) {
654655
return Maybe.Nothing();

frontend/javascripts/oxalis/model/sagas/proofread_saga.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ function* splitOrMergeOrMinCutAgglomerate(
206206
const skeletonTracing = yield* select((state) => enforceSkeletonTracing(state.tracing));
207207

208208
const { trees } = skeletonTracing;
209-
const sourceTree = findTreeByNodeId(trees, sourceNodeId).getOrElse(null);
210-
const targetTree = findTreeByNodeId(trees, targetNodeId).getOrElse(null);
209+
const sourceTree = findTreeByNodeId(trees, sourceNodeId);
210+
const targetTree = findTreeByNodeId(trees, targetNodeId);
211211

212212
if (sourceTree == null || targetTree == null) {
213213
yield* put(setBusyBlockingInfoAction(false));

frontend/javascripts/oxalis/model/sagas/skeletontracing_saga.ts

+5-17
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ export function* watchAgglomerateLoading(): Saga<void> {
214214
yield* take("INITIALIZE_SKELETONTRACING");
215215
yield* take("WK_READY");
216216
yield* takeEvery(channel, loadAgglomerateSkeletonWithId);
217-
yield* takeEvery("REMOVE_AGGLOMERATE_SKELETON", removeAgglomerateSkeletonWithId);
218217
}
219218
export function* watchConnectomeAgglomerateLoading(): Saga<void> {
220219
// Buffer actions since they might be dispatched before WK_READY
@@ -345,7 +344,7 @@ export function* loadAgglomerateSkeletonWithId(
345344

346345
const treeName = getTreeNameForAgglomerateSkeleton(agglomerateId, mappingName);
347346
const trees = yield* select((state) => enforceSkeletonTracing(state.tracing).trees);
348-
const maybeTree = findTreeByName(trees, treeName).getOrElse(null);
347+
const maybeTree = findTreeByName(trees, treeName);
349348

350349
if (maybeTree != null) {
351350
console.warn(
@@ -425,16 +424,6 @@ function* loadConnectomeAgglomerateSkeletonWithId(
425424
}
426425
}
427426

428-
function* removeAgglomerateSkeletonWithId(action: LoadAgglomerateSkeletonAction): Saga<void> {
429-
const allowUpdate = yield* select((state) => state.tracing.restrictions.allowUpdate);
430-
if (!allowUpdate) return;
431-
const { mappingName, agglomerateId } = action;
432-
const treeName = getTreeNameForAgglomerateSkeleton(agglomerateId, mappingName);
433-
const trees = yield* select((state) => enforceSkeletonTracing(state.tracing).trees);
434-
// @ts-expect-error ts-migrate(2552) FIXME: Cannot find name '_all'. Did you mean 'all'?
435-
yield _all(findTreeByName(trees, treeName).map((tree) => put(deleteTreeAction(tree.treeId))));
436-
}
437-
438427
function* removeConnectomeAgglomerateSkeletonWithId(
439428
action: LoadAgglomerateSkeletonAction,
440429
): Saga<void> {
@@ -445,11 +434,10 @@ function* removeConnectomeAgglomerateSkeletonWithId(
445434
);
446435
if (skeleton == null) return;
447436
const { trees } = skeleton;
448-
yield* all(
449-
findTreeByName(trees, treeName).map((tree) =>
450-
put(deleteConnectomeTreesAction([tree.treeId], layerName)),
451-
),
452-
);
437+
const tree = findTreeByName(trees, treeName);
438+
if (tree) {
439+
yield* put(deleteConnectomeTreesAction([tree.treeId], layerName));
440+
}
453441
}
454442

455443
export function* watchSkeletonTracingAsync(): Saga<void> {

frontend/javascripts/oxalis/view/context_menu.tsx

+16-1
Original file line numberDiff line numberDiff line change
@@ -372,7 +372,22 @@ function NodeContextMenuOptions({
372372

373373
const { userBoundingBoxes } = skeletonTracing;
374374
const { activeTreeId, trees, activeNodeId } = skeletonTracing;
375-
const clickedTree = findTreeByNodeId(trees, clickedNodeId).get();
375+
const clickedTree = findTreeByNodeId(trees, clickedNodeId);
376+
377+
if (clickedTree == null) {
378+
return (
379+
<Menu
380+
onClick={hideContextMenu}
381+
style={{
382+
borderRadius: 6,
383+
}}
384+
mode="vertical"
385+
>
386+
<Menu.Item disabled>Error: Could not find clicked node</Menu.Item>
387+
</Menu>
388+
);
389+
}
390+
376391
const areInSameTree = activeTreeId === clickedTree.treeId;
377392
const isBranchpoint = clickedTree.branchPoints.find((bp) => bp.nodeId === clickedNodeId) != null;
378393
const isTheSameNode = activeNodeId === clickedNodeId;

0 commit comments

Comments
 (0)