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 option to hide tree edges #7296

Merged
merged 17 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from 13 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
17 changes: 17 additions & 0 deletions frontend/javascripts/oxalis/api/api_latest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
setTreeVisibilityAction,
setTreeGroupAction,
setTreeGroupsAction,
setTreeEdgeVisibilityAction as setTreeEdgeVisibilityAction,
} from "oxalis/model/actions/skeletontracing_actions";
import {
bucketPositionToGlobalAddress,
Expand Down Expand Up @@ -383,6 +384,22 @@ class TracingApi {
Store.dispatch(setTreeNameAction(name, treeId));
}

/**
* Sets the visibility of the edges for a tree. If no tree id is given, the active tree is used.
*
* @example
* api.tracing.setTreeEdgeVisibility(false, 1);
*/
setTreeEdgeVisibility(edgesAreVisible: boolean, treeId: number | null | undefined) {
const skeletonTracing = assertSkeleton(Store.getState().tracing);

if (treeId == null) {
treeId = skeletonTracing.activeTreeId;
}

Store.dispatch(setTreeEdgeVisibilityAction(treeId, edgesAreVisible));
}

/**
* Makes the specified tree active. Within the tree, the node with the highest ID will be activated.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ void main() {
));

color = texture(treeColors, treeIdToTextureCoordinate).rgb;
bool isVisible = texture(treeColors, treeIdToTextureCoordinate).a == 1.0;
float alpha = texture(treeColors, treeIdToTextureCoordinate).a;
bool isVisible = alpha == 1.0 || alpha == 0.5 ;

// DELETED OR INVISIBLE NODE
if (type == ${NodeTypes.INVALID.toFixed(1)} || !isVisible) {
Expand Down
30 changes: 21 additions & 9 deletions frontend/javascripts/oxalis/geometries/skeleton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,14 +359,20 @@ class Skeleton {
}

case "createTree": {
this.updateTreeColor(update.value.id, update.value.color, update.value.isVisible);
this.updateTreeColor(
update.value.id,
update.value.color,
update.value.isVisible,
update.value.edgesAreVisible,
);
break;
}

case "updateTreeVisibility": {
case "updateTreeVisibility":
case "updateTreeEdgesVisibility": {
const { treeId } = update.value;
const tree = skeletonTracing.trees[treeId];
this.updateTreeColor(treeId, tree.color, tree.isVisible);
this.updateTreeColor(treeId, tree.color, tree.isVisible, tree.edgesAreVisible);
break;
}

Expand Down Expand Up @@ -399,7 +405,7 @@ class Skeleton {
});

if (tree.color !== prevTree.color) {
this.updateTreeColor(treeId, update.value.color, tree.isVisible);
this.updateTreeColor(treeId, update.value.color, tree.isVisible, tree.edgesAreVisible);
}

break;
Expand Down Expand Up @@ -499,7 +505,7 @@ class Skeleton {
this.createEdge(tree.treeId, source, target);
}

this.updateTreeColor(tree.treeId, tree.color, tree.isVisible);
this.updateTreeColor(tree.treeId, tree.color, tree.isVisible, tree.edgesAreVisible);
}

/**
Expand Down Expand Up @@ -679,14 +685,20 @@ class Skeleton {
* Updates a node/edge's color based on the tree color. Colors are stored in
* a texture shared between the node and edge shader.
*/
updateTreeColor(treeId: number, color: Vector3, isVisible: boolean = true) {
const rgba = this.getTreeRGBA(color, isVisible);
updateTreeColor(
treeId: number,
color: Vector3,
isVisible: boolean = true,
edgesAreVisible: boolean = true,
) {
const rgba = this.getTreeRGBA(color, isVisible, edgesAreVisible);
this.treeColorTexture.image.data.set(rgba, treeId * 4);
this.treeColorTexture.needsUpdate = true;
}

getTreeRGBA(color: Vector3, isVisible: boolean): Vector4 {
return [...color, isVisible ? 1 : 0];
getTreeRGBA(color: Vector3, isVisible: boolean, areEdgesVisible: boolean): Vector4 {
const alpha = isVisible ? (areEdgesVisible ? 1 : 0.5) : 0;
return [...color, alpha];
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type ToggleInactiveTreesAction = ReturnType<typeof toggleInactiveTreesAction>;
type ToggleTreeGroupAction = ReturnType<typeof toggleTreeGroupAction>;
type RequestDeleteBranchPointAction = ReturnType<typeof requestDeleteBranchPointAction>;
type CreateTreeAction = ReturnType<typeof createTreeAction>;
type SetEdgeVisibilityAction = ReturnType<typeof setTreeEdgeVisibilityAction>;
type AddTreesAndGroupsAction = ReturnType<typeof addTreesAndGroupsAction>;
type DeleteTreeAction = ReturnType<typeof deleteTreeAction>;
type ResetSkeletonTracingAction = ReturnType<typeof resetSkeletonTracingAction>;
Expand Down Expand Up @@ -85,6 +86,7 @@ export type SkeletonTracingAction =
| DeleteBranchpointByIdAction
| RequestDeleteBranchPointAction
| CreateTreeAction
| SetEdgeVisibilityAction
| AddTreesAndGroupsAction
| DeleteTreeAction
| ResetSkeletonTracingAction
Expand Down Expand Up @@ -126,6 +128,7 @@ export const SkeletonTracingSaveRelevantActions = [
"DELETE_BRANCHPOINT_BY_ID",
"DELETE_BRANCHPOINT",
"CREATE_TREE",
"SET_EDGES_ARE_VISIBLE",
"ADD_TREES_AND_GROUPS",
"DELETE_TREE",
"SET_ACTIVE_TREE",
Expand Down Expand Up @@ -275,6 +278,16 @@ export const createTreeAction = (timestamp: number = Date.now()) =>
timestamp,
} as const);

export const setTreeEdgeVisibilityAction = (
treeId: number | null | undefined,
edgesAreVisible: boolean,
) =>
({
type: "SET_EDGES_ARE_VISIBLE",
treeId,
edgesAreVisible,
} as const);

export const addTreesAndGroupsAction = (
trees: MutableTreeMap,
treeGroups: Array<TreeGroup> | null | undefined,
Expand Down
2 changes: 2 additions & 0 deletions frontend/javascripts/oxalis/model/helpers/nml_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ function splitTreeIntoComponents(
isVisible: tree.isVisible,
groupId: newGroupId,
type: tree.type,
edgesAreVisible: tree.edgesAreVisible,
};
newTrees.push(newTree);
}
Expand Down Expand Up @@ -721,6 +722,7 @@ export function parseNml(nmlString: string): Promise<{
isVisible: _parseFloat(attr, "color.a") !== 0,
groupId: groupId >= 0 ? groupId : DEFAULT_GROUP_ID,
type: _parseTreeType(attr, "type", TreeTypeEnum.DEFAULT),
edgesAreVisible: _parseBool(attr, "edgesAreVisible", true),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is only for parsing right? what about serialization? can be invoked via the trees-tab ("save selected trees as nml" or sth like that)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I did not take care of serialization. Internally we agreed to postpone the support of the new edgesAreVisible field in nmls. Thus I did not implement any NML support besides what is needed for typescript. This line is needed because the created object currentTree in line 712, because the field edgesAreVisible is required by this object. Alternatively, I could set the value to always be true, if you prefer this variation over already trying to parse a value that is not yet serialized, I would also be fine with that :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see, makes sense :)

};
if (trees[currentTree.treeId] != null)
throw new NmlParseError(`${messages["nml.duplicate_tree_id"]} ${currentTree.treeId}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -956,6 +956,26 @@ function SkeletonTracingReducer(state: OxalisState, action: Action): OxalisState
.getOrElse(state);
}

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

case "CREATE_COMMENT": {
const { commentText, nodeId, treeId } = action;
return getNodeAndTree(skeletonTracing, nodeId, treeId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ function splitTreeByNodes(
isVisible: true,
groupId: activeTree.groupId,
type: activeTree.type,
edgesAreVisible: true,
};
} else {
const immutableNewTree = createTree(
Expand Down Expand Up @@ -482,6 +483,7 @@ export function createTree(
addToActiveGroup: boolean = true,
name?: string,
type: TreeType = TreeTypeEnum.DEFAULT,
edgesAreVisible: boolean = true,
): Maybe<Tree> {
return getSkeletonTracing(state.tracing).chain((skeletonTracing) => {
// Create a new tree id and name
Expand Down Expand Up @@ -510,6 +512,7 @@ export function createTree(
isVisible: true,
groupId,
type,
edgesAreVisible,
};
return Maybe.Just(tree);
});
Expand Down Expand Up @@ -845,6 +848,7 @@ export function createMutableTreeMapFromTreeArray(
timestamp: tree.createdTimestamp,
groupId: tree.groupId,
type: tree.type != null ? tree.type : TreeTypeEnum.DEFAULT,
edgesAreVisible: tree.edgesAreVisible != null ? tree.edgesAreVisible : true,
}),
),
"treeId",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
deleteNode,
deleteTree,
updateTreeVisibility,
updateTreeEdgesVisibility,
updateNode,
updateSkeletonTracing,
updateUserBoundingBoxes,
Expand Down Expand Up @@ -583,6 +584,9 @@ export function* diffTrees(
if (prevTree.isVisible !== tree.isVisible) {
yield updateTreeVisibility(tree);
}
if (prevTree.edgesAreVisible !== tree.edgesAreVisible) {
yield updateTreeEdgesVisibility(tree);
}
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions frontend/javascripts/oxalis/model/sagas/update_actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type MergeTreeUpdateAction = ReturnType<typeof mergeTree>;
export type CreateNodeUpdateAction = ReturnType<typeof createNode>;
export type UpdateNodeUpdateAction = ReturnType<typeof updateNode>;
export type UpdateTreeVisibilityUpdateAction = ReturnType<typeof updateTreeVisibility>;
export type UpdateTreeEdgesVisibilityUpdateAction = ReturnType<typeof updateTreeEdgesVisibility>;
export type UpdateTreeGroupVisibilityUpdateAction = ReturnType<typeof updateTreeGroupVisibility>;
export type DeleteNodeUpdateAction = ReturnType<typeof deleteNode>;
export type CreateEdgeUpdateAction = ReturnType<typeof createEdge>;
Expand Down Expand Up @@ -64,6 +65,7 @@ export type UpdateAction =
| DeleteSegmentUpdateAction
| UpdateBucketUpdateAction
| UpdateTreeVisibilityUpdateAction
| UpdateTreeEdgesVisibilityUpdateAction
| UpdateTreeGroupVisibilityUpdateAction
| RevertToVersionUpdateAction
| UpdateSegmentGroupsUpdateAction
Expand Down Expand Up @@ -115,6 +117,7 @@ export function createTree(tree: Tree) {
groupId: tree.groupId,
isVisible: tree.isVisible,
type: tree.type,
edgesAreVisible: tree.edgesAreVisible,
},
} as const;
}
Expand All @@ -140,6 +143,7 @@ export function updateTree(tree: Tree) {
groupId: tree.groupId,
isVisible: tree.isVisible,
type: tree.type,
edgesAreVisible: tree.edgesAreVisible,
},
} as const;
}
Expand All @@ -153,6 +157,16 @@ export function updateTreeVisibility(tree: Tree) {
},
} as const;
}
export function updateTreeEdgesVisibility(tree: Tree) {
const { treeId, edgesAreVisible } = tree;
return {
name: "updateTreeEdgesVisibility",
value: {
treeId,
edgesAreVisible,
},
} as const;
}
export function updateTreeGroupVisibility(groupId: number | null | undefined, isVisible: boolean) {
return {
name: "updateTreeGroupVisibility",
Expand Down
2 changes: 2 additions & 0 deletions frontend/javascripts/oxalis/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ export type MutableTree = {
isVisible: boolean;
nodes: MutableNodeMap;
type: TreeType;
edgesAreVisible: boolean;
};
export type Tree = {
readonly treeId: number;
Expand All @@ -154,6 +155,7 @@ export type Tree = {
readonly isVisible: boolean;
readonly nodes: NodeMap;
readonly type: TreeType;
readonly edgesAreVisible: boolean;
};
export type TreeGroupTypeFlat = {
readonly name: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ const synapseTreeCreator = (synapseId: number, synapseType: string): MutableTree
isVisible: true,
groupId: null,
type: TreeTypeEnum.DEFAULT,
edgesAreVisible: true,
});

const synapseNodeCreator = (synapseId: number, synapsePosition: Vector3): MutableNode => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import {
deleteTreeAction,
toggleInactiveTreesAction,
shuffleAllTreeColorsAction,
setTreeEdgeVisibilityAction,
} from "oxalis/model/actions/skeletontracing_actions";
import messages from "messages";
import { formatNumberToLength, formatLengthAsVx } from "libs/format_utils";
Expand Down Expand Up @@ -89,6 +90,7 @@ type Props = OwnProps & {
onBatchActions: (arg0: Array<Action>, arg1: string) => void;
onToggleHideInactiveTrees: () => void;
onShuffleAllTreeColors: () => void;
onSetTreeEdgesVisibility: (treeId: number, edgesAreVisible: boolean) => void;
};
type State = {
prevProps: Props | null | undefined;
Expand Down Expand Up @@ -622,6 +624,19 @@ class TreeHierarchyView extends React.PureComponent<Props, State> {
icon: <i className="fas fa-eye" />,
label: "Hide/Show all other trees",
},
{
key: "hideTreeEdges",
onClick: () => {
this.props.onSetActiveTree(tree.treeId);
this.props.onSetTreeEdgesVisibility(tree.treeId, !tree.edgesAreVisible);
this.handleTreeDropdownMenuVisibility(tree.treeId, false);
},
title: "Hide/Show edges of this tree",
icon: (
<img src="/assets/images/hide-skeleton-edges-icon.svg" alt="Hide Tree Edges Icon" />
),
label: "Hide/Show edges of this tree",
},
],
};
};
Expand Down Expand Up @@ -798,6 +813,10 @@ const mapDispatchToProps = (dispatch: Dispatch<any>) => ({
dispatch(toggleTreeAction(treeId));
},

onSetTreeEdgesVisibility(treeId: number, edgesAreVisible: boolean) {
dispatch(setTreeEdgeVisibilityAction(treeId, edgesAreVisible));
},

onToggleTreeGroup(groupId: number) {
dispatch(toggleTreeGroupAction(groupId));
},
Expand Down
5 changes: 5 additions & 0 deletions frontend/javascripts/oxalis/view/version_entry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {
RevertToVersionUpdateAction,
UpdateNodeUpdateAction,
UpdateTreeVisibilityUpdateAction,
UpdateTreeEdgesVisibilityUpdateAction,
UpdateTreeGroupVisibilityUpdateAction,
CreateEdgeUpdateAction,
DeleteEdgeUpdateAction,
Expand Down Expand Up @@ -132,6 +133,10 @@ const descriptionFns: Record<ServerUpdateAction["name"], (...args: any) => Descr
description: `Updated the visibility of the tree with id ${action.value.treeId}.`,
icon: <EyeOutlined />,
}),
updateTreeEdgesVisibility: (action: UpdateTreeEdgesVisibilityUpdateAction): Description => ({
description: `Updated the visibility of the edges of the tree with id ${action.value.treeId}.`,
icon: <img src="/assets/images/hide-skeleton-edges-icon.svg" alt="Hide Tree Edges Icon" />,
}),
updateTreeGroupVisibility: (action: UpdateTreeGroupVisibilityUpdateAction): Description => ({
description: `Updated the visibility of the group with id ${
action.value.treeGroupId != null ? action.value.treeGroupId : MISSING_GROUP_ID
Expand Down
Loading