From 850bd7b2ef631fcd599c05ba704d596017a4be05 Mon Sep 17 00:00:00 2001 From: Philipp Otto Date: Wed, 1 Aug 2018 13:48:12 +0200 Subject: [PATCH 1/3] Expose setActiveTree and setTreeColorIndex in API (fixes #2996) --- .../javascripts/oxalis/api/api_latest.js | 27 +++++++++++++++++++ .../model/actions/skeletontracing_actions.js | 15 +++++++++++ .../model/reducers/skeletontracing_reducer.js | 11 ++++++++ .../skeletontracing_reducer_helpers.js | 11 ++++++-- 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/oxalis/api/api_latest.js b/app/assets/javascripts/oxalis/api/api_latest.js index b2d50bf6217..538d259fb12 100644 --- a/app/assets/javascripts/oxalis/api/api_latest.js +++ b/app/assets/javascripts/oxalis/api/api_latest.js @@ -20,6 +20,8 @@ import { deleteTreeAction, setNodeRadiusAction, setTreeNameAction, + setActiveTreeAction, + setTreeColorIndexAction, } from "oxalis/model/actions/skeletontracing_actions"; import { findTreeByNodeId, @@ -243,6 +245,31 @@ class TracingApi { }); } + /** + * Makes the specified tree active. Within the tree, the node with the highest ID will be activated. + * + * @example + * api.tracing.setActiveTree(3); + */ + setActiveTree(treeId: number) { + const tracing = Store.getState().tracing; + assertSkeleton(tracing); + Store.dispatch(setActiveTreeAction(treeId)); + } + + /** + * Changes the color of the referenced tree. Internally, a pre-defined array of colors is used which is + * why this function uses a colorIndex (between 0 and 500) instead of a proper color. + * + * @example + * api.tracing.setTreeColorIndex(3, 10); + */ + setTreeColorIndex(treeId?: number, colorIndex: number) { + const tracing = Store.getState().tracing; + assertSkeleton(tracing); + Store.dispatch(setTreeColorIndexAction(treeId, colorIndex)); + } + /** * Returns the name for a given tree id. If none is given, the name of the active tree is returned. * diff --git a/app/assets/javascripts/oxalis/model/actions/skeletontracing_actions.js b/app/assets/javascripts/oxalis/model/actions/skeletontracing_actions.js index d2db78403d2..a3c6befa270 100644 --- a/app/assets/javascripts/oxalis/model/actions/skeletontracing_actions.js +++ b/app/assets/javascripts/oxalis/model/actions/skeletontracing_actions.js @@ -72,6 +72,11 @@ type SetActiveTreeActionType = { type: "SET_ACTIVE_TREE", treeId: number }; type MergeTreesActionType = { type: "MERGE_TREES", sourceNodeId: number, targetNodeId: number }; type SetTreeNameActionType = { type: "SET_TREE_NAME", name: ?string, treeId: ?number }; type SelectNextTreeActionType = { type: "SELECT_NEXT_TREE", forward: ?boolean }; +type SetTreeColorIndexActionType = { + type: "SET_TREE_COLOR_INDEX", + treeId?: number, + colorIndex: number, +}; type ShuffleTreeColorActionType = { type: "SHUFFLE_TREE_COLOR", treeId?: number }; type ShuffleAllTreeColorsActionType = { type: "SHUFFLE_ALL_TREE_COLORS", treeId?: number }; type CreateCommentActionType = { @@ -106,6 +111,7 @@ export type SkeletonTracingActionType = | SelectNextTreeActionType | ShuffleTreeColorActionType | ShuffleAllTreeColorsActionType + | SetTreeColorIndexActionType | CreateCommentActionType | DeleteCommentActionType | ToggleTreeActionType @@ -310,6 +316,15 @@ export const selectNextTreeAction = (forward: ?boolean = true): SelectNextTreeAc forward, }); +export const setTreeColorIndexAction = ( + treeId?: number, + colorIndex: number, +): SetTreeColorIndexActionType => ({ + type: "SET_TREE_COLOR_INDEX", + treeId, + colorIndex, +}); + export const shuffleTreeColorAction = (treeId: number): ShuffleTreeColorActionType => ({ type: "SHUFFLE_TREE_COLOR", treeId, diff --git a/app/assets/javascripts/oxalis/model/reducers/skeletontracing_reducer.js b/app/assets/javascripts/oxalis/model/reducers/skeletontracing_reducer.js index 2d0405fe5b2..b07e6ef9b87 100644 --- a/app/assets/javascripts/oxalis/model/reducers/skeletontracing_reducer.js +++ b/app/assets/javascripts/oxalis/model/reducers/skeletontracing_reducer.js @@ -16,6 +16,7 @@ import { deleteNode, deleteEdge, shuffleTreeColor, + setTreeColorIndex, createComment, deleteComment, mergeTrees, @@ -376,6 +377,16 @@ function SkeletonTracingReducer(state: OxalisState, action: ActionType): OxalisS }); } + case "SET_TREE_COLOR_INDEX": { + const { colorIndex } = action; + return getTree(skeletonTracing, action.treeId) + .chain(tree => setTreeColorIndex(skeletonTracing, tree, colorIndex)) + .map(([tree, treeId]) => + update(state, { tracing: { trees: { [treeId]: { $set: tree } } } }), + ) + .getOrElse(state); + } + case "SHUFFLE_TREE_COLOR": { return getTree(skeletonTracing, action.treeId) .chain(tree => shuffleTreeColor(skeletonTracing, tree)) diff --git a/app/assets/javascripts/oxalis/model/reducers/skeletontracing_reducer_helpers.js b/app/assets/javascripts/oxalis/model/reducers/skeletontracing_reducer_helpers.js index 147315cd572..253e5b6abaa 100644 --- a/app/assets/javascripts/oxalis/model/reducers/skeletontracing_reducer_helpers.js +++ b/app/assets/javascripts/oxalis/model/reducers/skeletontracing_reducer_helpers.js @@ -605,8 +605,15 @@ export function shuffleTreeColor( tree: TreeType, ): Maybe<[TreeType, number]> { const randomId = _.random(0, 10000, false); - // ColorGenerator fails to produce distinct color for huge ids (Infinity) - const newTree = update(tree, { color: { $set: ColorGenerator.distinctColorForId(randomId) } }); + return setTreeColorIndex(skeletonTracing, tree, randomId); +} + +export function setTreeColorIndex( + skeletonTracing: SkeletonTracingType, + tree: TreeType, + colorIndex: number, +): Maybe<[TreeType, number]> { + const newTree = update(tree, { color: { $set: ColorGenerator.distinctColorForId(colorIndex) } }); return Maybe.Just([newTree, tree.treeId]); } From 05ed393fa656ab78aef9f4c92023a8900a0afdb9 Mon Sep 17 00:00:00 2001 From: Philipp Otto Date: Wed, 1 Aug 2018 13:57:54 +0200 Subject: [PATCH 2/3] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0eb77e01758..04d0e038fce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.md). - Added placeholders and functionality hints to (nearly) empty lists and tables in the admin views. [#2969](https://github.com/scalableminds/webknossos/pull/2969) - Added the possibility to import multiple NML files into the active tracing. This can be done by dragging and dropping the files directly into the tracing view. [#2908](https://github.com/scalableminds/webknossos/pull/2908) - During the import of multiple NML files, the user can select an option to automatically create a group per file so that the imported trees are organized in a hierarchy. [#2908](https://github.com/scalableminds/webknossos/pull/2908) +- Added functions to the front-end API to activate a tree and to change the color of a tree. [#2997](https://github.com/scalableminds/webknossos/pull/2997) ### Changed From 6002a0a71e87ba10ba7e4d146bd119d1bc246f97 Mon Sep 17 00:00:00 2001 From: Philipp Otto Date: Thu, 2 Aug 2018 11:49:23 +0200 Subject: [PATCH 3/3] add test for setTreeColor action and adapt typing of the corresponding function --- app/assets/javascripts/libs/color_generator.js | 2 +- app/assets/javascripts/oxalis/api/api_latest.js | 2 +- .../oxalis/model/actions/skeletontracing_actions.js | 4 ++-- .../test/reducers/skeletontracing_reducer.spec.js | 12 ++++++++++++ 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/assets/javascripts/libs/color_generator.js b/app/assets/javascripts/libs/color_generator.js index 5d1d9e9660a..169493c2305 100644 --- a/app/assets/javascripts/libs/color_generator.js +++ b/app/assets/javascripts/libs/color_generator.js @@ -1509,7 +1509,7 @@ const rawRgbs = [ 9, ]; -const rgbs: Array = chunk3(rawRgbs).map(rgb => +export const rgbs: Array = chunk3(rawRgbs).map(rgb => // $FlowFixMe Flow has troubles with understanding that mapping a tuple, returns another tuple rgb.map(el => el / 255), ); diff --git a/app/assets/javascripts/oxalis/api/api_latest.js b/app/assets/javascripts/oxalis/api/api_latest.js index 538d259fb12..7be339f838f 100644 --- a/app/assets/javascripts/oxalis/api/api_latest.js +++ b/app/assets/javascripts/oxalis/api/api_latest.js @@ -264,7 +264,7 @@ class TracingApi { * @example * api.tracing.setTreeColorIndex(3, 10); */ - setTreeColorIndex(treeId?: number, colorIndex: number) { + setTreeColorIndex(treeId: ?number, colorIndex: number) { const tracing = Store.getState().tracing; assertSkeleton(tracing); Store.dispatch(setTreeColorIndexAction(treeId, colorIndex)); diff --git a/app/assets/javascripts/oxalis/model/actions/skeletontracing_actions.js b/app/assets/javascripts/oxalis/model/actions/skeletontracing_actions.js index a3c6befa270..62a7f0d612a 100644 --- a/app/assets/javascripts/oxalis/model/actions/skeletontracing_actions.js +++ b/app/assets/javascripts/oxalis/model/actions/skeletontracing_actions.js @@ -74,7 +74,7 @@ type SetTreeNameActionType = { type: "SET_TREE_NAME", name: ?string, treeId: ?nu type SelectNextTreeActionType = { type: "SELECT_NEXT_TREE", forward: ?boolean }; type SetTreeColorIndexActionType = { type: "SET_TREE_COLOR_INDEX", - treeId?: number, + treeId: ?number, colorIndex: number, }; type ShuffleTreeColorActionType = { type: "SHUFFLE_TREE_COLOR", treeId?: number }; @@ -317,7 +317,7 @@ export const selectNextTreeAction = (forward: ?boolean = true): SelectNextTreeAc }); export const setTreeColorIndexAction = ( - treeId?: number, + treeId: ?number, colorIndex: number, ): SetTreeColorIndexActionType => ({ type: "SET_TREE_COLOR_INDEX", diff --git a/app/assets/javascripts/test/reducers/skeletontracing_reducer.spec.js b/app/assets/javascripts/test/reducers/skeletontracing_reducer.spec.js index 90068cc5434..41fef33285e 100644 --- a/app/assets/javascripts/test/reducers/skeletontracing_reducer.spec.js +++ b/app/assets/javascripts/test/reducers/skeletontracing_reducer.spec.js @@ -13,6 +13,7 @@ import ChainReducer from "test/helpers/chainReducer"; import update from "immutability-helper"; import DiffableMap from "libs/diffable_map"; import EdgeCollection from "oxalis/model/edge_collection"; +import { rgbs as colors } from "libs/color_generator"; mock.stopAll(); mock("app", { currentUser: { firstName: "SCM", lastName: "Boy" } }); @@ -1368,3 +1369,14 @@ test("SkeletonTracing should delete a comment for a specified node (1/2)", t => t.deepEqual(newState.tracing.trees[1].comments[0].nodeId, 1); t.deepEqual(newState.tracing.trees[1].comments[1].nodeId, 3); }); + +test("SkeletonTracing should change the color of a specified tree", t => { + const colorIndex = 10; + const newState = SkeletonTracingReducer( + initialState, + SkeletonTracingActions.setTreeColorIndexAction(1, colorIndex), + ); + + t.not(newState, initialState); + t.is(newState.tracing.trees[1].color, colors[colorIndex - 1]); +});