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

Add color picker to select tree color #4907

Merged
merged 12 commits into from
Nov 16, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
- The total length of skeletons can now be measured using the dropdown in the tree list tab. Also, the frontend API received the methods `api.tracing.measureTreeLength` and `api.tracing.measureAllTrees`. [#4898](https://github.com/scalableminds/webknossos/pull/4898)
- Introduced an indeterminate visibility state for groups in the tree tab if not all but only some of the group's children are visible. Before, the visibility of those groups was shown as not visible which made it hard to find the visible trees. [#4897](https://github.com/scalableminds/webknossos/pull/4897)
- Dataset uploads on a specific Datastore can now be restricted to a single organization. [#4892](https://github.com/scalableminds/webknossos/pull/4892)
- Added a button to set the color of a tree in the trees tab view. [#4907](https://github.com/scalableminds/webknossos/pull/4907)
- Added multi-resolution volume annotations. Note that already existing volume tracings will still only contain data in the first magnification. If you want to migrate an old volume tracing, you can download and re-import it. [#4755](https://github.com/scalableminds/webknossos/pull/4755)

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ type SetTreeColorIndexAction = {
colorIndex: number,
};
type ShuffleTreeColorAction = { type: "SHUFFLE_TREE_COLOR", treeId?: number };
type SetTreeColorAction = { type: "SET_TREE_COLOR", treeId: number, color: Vector3 };
type ShuffleAllTreeColorsAction = { type: "SHUFFLE_ALL_TREE_COLORS", treeId?: number };
type CreateCommentAction = {
type: "CREATE_COMMENT",
Expand Down Expand Up @@ -148,6 +149,7 @@ export type SkeletonTracingAction =
| MergeTreesAction
| SetTreeNameAction
| SelectNextTreeAction
| SetTreeColorAction
| ShuffleTreeColorAction
| ShuffleAllTreeColorsAction
| SetTreeColorIndexAction
Expand Down Expand Up @@ -196,6 +198,7 @@ export const SkeletonTracingSaveRelevantActions = [
"TOGGLE_TREE_GROUP",
"TOGGLE_ALL_TREES",
"TOGGLE_INACTIVE_TREES",
"SET_TREE_COLOR",
// Composited actions, only dispatched using `batchActions`
"DELETE_GROUP_AND_TREES",
];
Expand Down Expand Up @@ -418,6 +421,12 @@ export const shuffleTreeColorAction = (treeId: number): ShuffleTreeColorAction =
treeId,
});

export const setTreeColorAction = (treeId: number, color: Vector3): SetTreeColorAction => ({
type: "SET_TREE_COLOR",
treeId,
color,
});

export const shuffleAllTreeColorsAction = (): ShuffleAllTreeColorsAction => ({
type: "SHUFFLE_ALL_TREE_COLORS",
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,17 @@ function SkeletonTracingReducer(state: OxalisState, action: Action): OxalisState
.getOrElse(state);
}

case "SET_TREE_COLOR": {
const { color, treeId } = action;
return getTree(skeletonTracing, treeId)
.map(tree =>
update(state, {
tracing: { skeleton: { trees: { [tree.treeId]: { color: { $set: color } } } } },
}),
)
.getOrElse(state);
}

case "SHUFFLE_TREE_COLOR": {
return getTree(skeletonTracing, action.treeId)
.chain(tree => shuffleTreeColor(skeletonTracing, tree))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// @flow

import { AutoSizer } from "react-virtualized";
import { Checkbox, Dropdown, Icon, Menu, Modal } from "antd";
import { Checkbox, Dropdown, Icon, Menu, Modal, notification } from "antd";
import { connect } from "react-redux";
import { batchActions } from "redux-batched-actions";
import * as React from "react";
import SortableTree from "react-sortable-tree";
import _ from "lodash";
import type { Dispatch } from "redux";
import { type Action } from "oxalis/model/actions/actions";
import type { Vector3 } from "oxalis/constants";
import * as Utils from "libs/utils";
import {
MISSING_GROUP_ID,
TYPE_GROUP,
Expand All @@ -28,12 +30,16 @@ import { getMaximumGroupId } from "oxalis/model/reducers/skeletontracing_reducer
import {
setActiveTreeAction,
setActiveGroupAction,
setTreeColorAction,
toggleTreeAction,
toggleTreeGroupAction,
toggleAllTreesAction,
setTreeGroupsAction,
shuffleTreeColorAction,
setTreeGroupAction,
} from "oxalis/model/actions/skeletontracing_actions";
import { formatNumberToLength } from "libs/format_utils";
import api from "oxalis/api/internal_api";

const CHECKBOX_STYLE = { verticalAlign: "middle" };
const CHECKBOX_PLACEHOLDER_STYLE = { width: 24, display: "inline-block" };
Expand All @@ -52,10 +58,12 @@ type OwnProps = {|

type Props = {
...OwnProps,
onShuffleTreeColor: number => void,
onSetActiveTree: number => void,
onSetActiveGroup: number => void,
onToggleTree: number => void,
onToggleAllTrees: () => void,
onSetTreeColor: (number, Vector3) => void,
onToggleTreeGroup: number => void,
onUpdateTreeGroups: (Array<TreeGroup>) => void,
onBatchActions: (Array<Action>, string) => void,
Expand All @@ -66,6 +74,7 @@ type State = {
expandedGroupIds: { [number]: boolean },
groupTree: Array<TreeNode>,
searchFocusOffset: number,
activeTreeDropdownId: ?number,
};

class TreeHierarchyView extends React.PureComponent<Props, State> {
Expand All @@ -74,6 +83,7 @@ class TreeHierarchyView extends React.PureComponent<Props, State> {
groupTree: [],
prevProps: null,
searchFocusOffset: 0,
activeTreeDropdownId: null,
};

static getDerivedStateFromProps(nextProps: Props, prevState: State) {
Expand Down Expand Up @@ -261,6 +271,14 @@ class TreeHierarchyView extends React.PureComponent<Props, State> {
}
};

handleTreeDropdownMenuVisibility = (treeId: number, isVisible: boolean) => {
if (isVisible) {
this.setState({ activeTreeDropdownId: treeId });
return;
}
this.setState({ activeTreeDropdownId: null });
};

getNodeStyleClassForBackground = (id: number) => {
const isTreeSelected = this.props.selectedTrees.includes(id);
if (isTreeSelected) {
Expand All @@ -269,6 +287,14 @@ class TreeHierarchyView extends React.PureComponent<Props, State> {
return null;
};

handleMeasureSkeletonLength = (treeId: number, treeName: string) => {
const length = api.tracing.measureTreeLength(treeId);
notification.open({
message: `The tree ${treeName} has a total length of ${formatNumberToLength(length)}.`,
icon: <i className="fas fa-ruler" />,
});
};

renderGroupActionsDropdown = (node: TreeNode) => {
// The root group must not be removed or renamed
const { id, name } = node;
Expand Down Expand Up @@ -349,6 +375,65 @@ class TreeHierarchyView extends React.PureComponent<Props, State> {
const rgbColorString = tree.color.map(c => Math.round(c * 255)).join(",");
// Defining background color of current node
const styleClass = this.getNodeStyleClassForBackground(node.id);
const menu = (
<Menu>
<Menu.Item key="changeTreeColor">
<div style={{ position: "relative", display: "inline-block", width: "100%" }}>
<i
className="fas fa-eye-dropper fa-sm"
style={{
cursor: "pointer",
}}
/>{" "}
Select Tree Color
<input
type="color"
value={Utils.rgbToHex(Utils.map3(value => value * 255, tree.color))}
style={{
position: "absolute",
left: 0,
top: 0,
width: "100%",
opacity: 0,
cursor: "pointer",
}}
onChange={event => {
let color = Utils.hexToRgb(event.target.value);
color = Utils.map3(component => component / 255, color);
this.props.onSetTreeColor(tree.treeId, color);
}}
/>
</div>
</Menu.Item>
<Menu.Item
key="shuffleTreeColor"
onClick={() => this.props.onShuffleTreeColor(tree.treeId)}
title="Shuffle Tree Color"
>
<i className="fas fa-adjust" /> Shuffle Tree Color
</Menu.Item>
<Menu.Item
key="measureSkeleton"
onClick={() => this.handleMeasureSkeletonLength(tree.treeId, tree.name)}
title="Measure Skeleton Length"
>
<i className="fas fa-ruler" /> Measure Skeleton Length
</Menu.Item>
</Menu>
);
const dropdownMenu = (
<Dropdown
overlay={menu}
placement="bottomCenter"
visible={this.state.activeTreeDropdownId === tree.treeId}
onVisibleChange={isVisible =>
this.handleTreeDropdownMenuVisibility(tree.treeId, isVisible)
}
>
<Icon type="setting" className="group-actions-icon" />
</Dropdown>
);

nodeProps.title = (
<div className={styleClass}>
<Checkbox
Expand All @@ -362,6 +447,7 @@ class TreeHierarchyView extends React.PureComponent<Props, State> {
style={{ marginLeft: 9, display: "inline" }}
onClick={this.onSelectTree}
>{`(${tree.nodes.size()}) ${tree.name}`}</div>
{dropdownMenu}
</div>
);
nodeProps.className = "tree-type";
Expand Down Expand Up @@ -438,6 +524,12 @@ const mapDispatchToProps = (dispatch: Dispatch<*>) => ({
onSetActiveGroup(groupId) {
dispatch(setActiveGroupAction(groupId));
},
onSetTreeColor(treeId, color) {
dispatch(setTreeColorAction(treeId, color));
},
onShuffleTreeColor(treeId) {
dispatch(shuffleTreeColorAction(treeId));
},
onToggleTree(treeId) {
dispatch(toggleTreeAction(treeId));
},
Expand Down
49 changes: 0 additions & 49 deletions frontend/javascripts/oxalis/view/right-menu/trees_tab_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ import {
createTreeAction,
deleteTreeAction,
deleteTreeAsUserAction,
shuffleTreeColorAction,
shuffleAllTreeColorsAction,
selectNextTreeAction,
toggleAllTreesAction,
Expand Down Expand Up @@ -107,7 +106,6 @@ type OwnProps = {|
portalKey: string,
|};
type StateProps = {|
onShuffleTreeColor: number => void,
onShuffleAllTreeColors: () => void,
onSortTree: boolean => void,
onSelectNextTreeForward: () => void,
Expand Down Expand Up @@ -446,15 +444,6 @@ class TreesTabView extends React.PureComponent<Props, State> {
}
};

shuffleTreeColor = () => {
if (!this.props.skeletonTracing) {
return;
}
getActiveTree(this.props.skeletonTracing).map(activeTree =>
this.props.onShuffleTreeColor(activeTree.treeId),
);
};

shuffleAllTreeColors = () => {
this.props.onShuffleAllTreeColors();
};
Expand Down Expand Up @@ -610,13 +599,6 @@ class TreesTabView extends React.PureComponent<Props, State> {
getActionsDropdown() {
return (
<Menu>
<Menu.Item
key="shuffleTreeColor"
onClick={this.shuffleTreeColor}
title="Shuffle Tree Color"
>
<i className="fas fa-adjust" /> Shuffle Tree Color
</Menu.Item>
<Menu.Item
key="shuffleAllTreeColors"
onClick={this.shuffleAllTreeColors}
Expand All @@ -634,14 +616,6 @@ class TreesTabView extends React.PureComponent<Props, State> {
<Menu.Item key="importNml" onClick={this.props.showDropzoneModal} title="Import NML files">
<Icon type="upload" /> Import NML
</Menu.Item>

<Menu.Item
key="measureSkeleton"
onClick={this.handleMeasureSkeletonLength}
title="Measure Skeleton Length"
>
<i className="fas fa-ruler" /> Measure Skeleton Length
</Menu.Item>
<Menu.Item
key="measureAllSkeletons"
onClick={this.handleMeasureAllSkeletonsLength}
Expand All @@ -668,26 +642,6 @@ class TreesTabView extends React.PureComponent<Props, State> {
/>
) : null;

handleMeasureSkeletonLength = () => {
const { skeletonTracing } = this.props;
if (!skeletonTracing) {
return;
}

const notificationText = getActiveTree(skeletonTracing)
.map(tree => api.tracing.measureTreeLength(tree.treeId))
.map(
length =>
`The currently active tree has a total length of ${formatNumberToLength(length)}.`,
)
.getOrElse("Please ensure that a tree is selected.");

notification.open({
message: notificationText,
icon: <i className="fas fa-ruler" />,
});
};

handleMeasureAllSkeletonsLength = () => {
const totalLength = api.tracing.measureAllTrees();

Expand Down Expand Up @@ -838,9 +792,6 @@ const mapStateToProps = (state: OxalisState) => ({
});

const mapDispatchToProps = (dispatch: Dispatch<*>) => ({
onShuffleTreeColor(treeId) {
dispatch(shuffleTreeColorAction(treeId));
},
onShuffleAllTreeColors() {
dispatch(shuffleAllTreeColorsAction());
},
Expand Down