Skip to content

Commit

Permalink
Merge branch 'feature/CELE-76' into feature/CELE-53
Browse files Browse the repository at this point in the history
  • Loading branch information
aranega authored Sep 18, 2024
2 parents eacf6a2 + 297b73b commit ba6052c
Show file tree
Hide file tree
Showing 10 changed files with 189 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useGlobalContext } from "../../../contexts/GlobalContext";
import { getFurthestIntersectedObject } from "../../../helpers/threeDHelpers";
import type { RootState } from "../../../layout-manager/layoutManagerFactory";
import type { Workspace } from "../../../models";
import { ViewerType } from "../../../models";
import { OUTLINE_COLOR, OUTLINE_THICKNESS } from "../../../settings/threeDSettings";

interface Props {
Expand All @@ -22,14 +23,21 @@ const STLMesh: FC<Props> = ({ id, color, opacity, renderOrder, isWireframe, stl
const { workspaces } = useGlobalContext();
const workspaceId = useSelector((state: RootState) => state.workspaceId);
const workspace: Workspace = workspaces[workspaceId];
const selectedNeurons = workspace.getViewerSelecedNeurons(ViewerType.Graph);
const isSelected = selectedNeurons.includes(id);

const onClick = (event: ThreeEvent<MouseEvent>) => {
const clicked = getFurthestIntersectedObject(event);
const { id } = clicked.userData;
if (clicked) {
workspace.toggleSelectedNeuron(clicked.userData.id);
if (isSelected) {
console.log(`Neurons selected: ${id}`);
} else {
console.log(`Neurons un selected: ${id}`);
}
}
};

const isSelected = id in workspace.selectedNeurons;
return (
<mesh userData={{ id }} onClick={onClick} frustumCulled={false} renderOrder={renderOrder}>
<primitive attach="geometry" object={stl} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ interface ContextMenuProps {
const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setSplitJoinState, openGroups, setOpenGroups, cy }) => {
const workspace = useSelectedWorkspace();
const [submenuAnchorEl, setSubmenuAnchorEl] = useState<null | HTMLElement>(null);
const selectedNeurons = workspace.getViewerSelecedNeurons(ViewerType.Graph);

const submenuOpen = Boolean(submenuAnchorEl);

Expand All @@ -55,14 +56,14 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setS
};

const handleAlignOption = (option: Alignment) => {
alignNeurons(option, Array.from(workspace.selectedNeurons), cy);
alignNeurons(option, selectedNeurons, cy);
setSubmenuAnchorEl(null);
onClose();
setSubmenuAnchorEl(null);
};

const handleDistributeOption = (option: Alignment) => {
distributeNeurons(option, Array.from(workspace.selectedNeurons), cy);
distributeNeurons(option, selectedNeurons, cy);
setSubmenuAnchorEl(null);
onClose();
};
Expand All @@ -75,13 +76,14 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setS
draft.visibilities[neuronId][ViewerType.Graph].visibility = Visibility.Hidden;
}
}
draft.selectedNeurons.clear();
draft.clearSelection(ViewerType.Graph);
});
onClose();
};

const handleGroup = () => {
const { newGroupId, newGroup, groupsToDelete } = groupNeurons(workspace.selectedNeurons, workspace);
const selectedNeuronsSet = new Set(selectedNeurons);
const { newGroupId, newGroup, groupsToDelete } = groupNeurons(selectedNeuronsSet, workspace);

workspace.customUpdate((draft) => {
// Add the new group
Expand All @@ -95,8 +97,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setS
}

// Clear the current selection and select the new group
draft.selectedNeurons.clear();
draft.selectedNeurons.add(newGroupId);
draft.setSelection([newGroupId], ViewerType.Graph);
});

setOpenGroups((prevOpenGroups: Set<string>) => {
Expand Down Expand Up @@ -126,7 +127,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setS
workspace.customUpdate((draft) => {
const nextSelected = new Set<string>();

for (const elementId of draft.selectedNeurons) {
for (const elementId of selectedNeurons) {
if (draft.neuronGroups[elementId]) {
// Handle the case where the selected element is a group
const group = draft.neuronGroups[elementId];
Expand Down Expand Up @@ -158,7 +159,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setS
}
}

draft.selectedNeurons = nextSelected;
draft.setSelection(Array.from(nextSelected), ViewerType.Graph);
});

// Remove groups from the openGroups set
Expand Down Expand Up @@ -189,7 +190,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setS

const handleAddToWorkspace = () => {
workspace.customUpdate((draft) => {
for (const neuronId of workspace.selectedNeurons) {
for (const neuronId of selectedNeurons) {
const group = workspace.neuronGroups[neuronId];
if (group) {
for (const groupedNeuronId of group.neurons) {
Expand All @@ -206,7 +207,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setS
};

const handleOpenGroup = () => {
for (const neuronId of workspace.selectedNeurons) {
for (const neuronId of selectedNeurons) {
if (workspace.neuronGroups[neuronId] && !openGroups.has(neuronId)) {
// Mark the group as open
setOpenGroups((prevOpenGroups: Set<string>) => {
Expand All @@ -219,7 +220,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setS
onClose();
};
const handleCloseGroup = () => {
for (const neuronId of workspace.selectedNeurons) {
for (const neuronId of selectedNeurons) {
if (workspace.neuronGroups[neuronId] && openGroups.has(neuronId)) {
// Mark the group as closed
setOpenGroups((prevOpenGroups: Set<string>) => {
Expand All @@ -235,8 +236,7 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setS
const groupEnabled = useMemo(() => {
const groupOrPartOfGroupSet = new Set<string>();
let nonGroupOrPartCount = 0;

for (const neuronId of Array.from(workspace.selectedNeurons)) {
for (const neuronId of selectedNeurons) {
const isGroup = Boolean(workspace.neuronGroups[neuronId]);
const isPartOfGroup = Object.entries(workspace.neuronGroups).find(([, group]) => group.neurons.has(neuronId));

Expand All @@ -251,10 +251,10 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setS

// Enable grouping if there are neurons not in any group and at most one group or part of a group is selected.
return nonGroupOrPartCount > 0 && groupOrPartOfGroupSet.size <= 1;
}, [workspace.selectedNeurons, workspace.neuronGroups]);
}, [selectedNeurons, workspace.neuronGroups]);

const ungroupEnabled = useMemo(() => {
return Array.from(workspace.selectedNeurons).some((neuronId) => {
return selectedNeurons.some((neuronId) => {
// Check if the neuronId is a group itself
const isGroup = Boolean(workspace.neuronGroups[neuronId]);

Expand All @@ -264,29 +264,29 @@ const ContextMenu: React.FC<ContextMenuProps> = ({ open, onClose, position, setS
// Enable ungroup if the neuron is a group or is part of a group
return isGroup || isPartOfGroup;
});
}, [workspace.selectedNeurons, workspace.neuronGroups]);
}, [selectedNeurons, workspace.neuronGroups]);

const splitEnabled = useMemo(() => {
return Array.from(workspace.selectedNeurons).some((neuronId) => {
return selectedNeurons.some((neuronId) => {
const neuron = workspace.availableNeurons[neuronId];
return neuron && neuron.name === neuron.nclass;
});
}, [workspace.selectedNeurons, workspace.availableNeurons]);
}, [selectedNeurons, workspace.availableNeurons]);

const joinEnabled = useMemo(() => {
return Array.from(workspace.selectedNeurons).some((neuronId) => {
return selectedNeurons.some((neuronId) => {
const neuron = workspace.availableNeurons[neuronId];
return neuron && neuron.name !== neuron.nclass;
});
}, [workspace.selectedNeurons, workspace.availableNeurons]);
}, [selectedNeurons, workspace.availableNeurons]);

const openGroupEnabled = useMemo(() => {
return Array.from(workspace.selectedNeurons).some((neuronId) => workspace.neuronGroups[neuronId] && !openGroups.has(neuronId));
}, [workspace.selectedNeurons, workspace.neuronGroups, openGroups]);
return selectedNeurons.some((neuronId) => workspace.neuronGroups[neuronId] && !openGroups.has(neuronId));
}, [selectedNeurons, workspace.neuronGroups, openGroups]);

const closeGroupEnabled = useMemo(() => {
return Array.from(workspace.selectedNeurons).some((neuronId) => workspace.neuronGroups[neuronId] && openGroups.has(neuronId));
}, [workspace.selectedNeurons, workspace.neuronGroups, openGroups]);
return selectedNeurons.some((neuronId) => workspace.neuronGroups[neuronId] && openGroups.has(neuronId));
}, [selectedNeurons, workspace.neuronGroups, openGroups]);
const handleContextMenu = (event: React.MouseEvent) => {
event.preventDefault(); // Prevent default context menu
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const TwoDMenu = ({
if (neuron) {
const currentVisibility = neuron[ViewerType.Graph]?.visibility;
neuron[ViewerType.Graph].visibility = currentVisibility === Visibility.Visible ? Visibility.Hidden : Visibility.Visible;
draft.selectedNeurons.delete(neuronId);
draft.removeSelection(neuronId, ViewerType.Graph);
}
});
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ const TwoDViewer = () => {
reportedNeurons: new Set<string>(),
unreportedNeurons: new Set<string>(),
});

const selectedNeurons = workspace.getViewerSelecedNeurons(ViewerType.Graph);

const visibleActiveNeurons = useMemo(() => {
return getVisibleActiveNeuronsIn2D(workspace);
}, [
Expand Down Expand Up @@ -183,9 +186,9 @@ const TwoDViewer = () => {

useEffect(() => {
if (cyRef.current) {
updateHighlighted(cyRef.current, Array.from(visibleActiveNeurons), Array.from(workspace.selectedNeurons), legendHighlights);
updateHighlighted(cyRef.current, Array.from(visibleActiveNeurons), selectedNeurons, legendHighlights);
}
}, [legendHighlights, workspace.selectedNeurons, workspace.neuronGroups]);
}, [legendHighlights, selectedNeurons, workspace.neuronGroups]);

// Update layout when layout setting changes
useEffect(() => {
Expand Down Expand Up @@ -246,19 +249,21 @@ const TwoDViewer = () => {

const handleNodeClick = (event) => {
const neuronId = event.target.id();
const isSelected = workspace.selectedNeurons.has(neuronId);
workspace.toggleSelectedNeuron(neuronId);
const selectedNeurons = workspace.getSelection(ViewerType.Graph);
const isSelected = selectedNeurons.includes(neuronId);

if (isSelected) {
workspace.removeSelection(neuronId, ViewerType.Graph);
event.target.removeClass(SELECTED_CLASS);
} else {
workspace.addSelection(neuronId, ViewerType.Graph);
event.target.addClass(SELECTED_CLASS);
}
};

const handleBackgroundClick = (event) => {
if (event.target === cy) {
workspace.clearSelectedNeurons();
workspace.clearSelection(ViewerType.Graph);
cy.nodes(`.${SELECTED_CLASS}`).removeClass(SELECTED_CLASS);

setLegendHighlights(new Map()); // Reset legend highlights
Expand All @@ -270,8 +275,8 @@ const TwoDViewer = () => {

const cyEvent = event as any; // Cast to any to access originalEvent
const originalEvent = cyEvent.originalEvent as MouseEvent;

if (workspace.selectedNeurons.size > 0) {
const selectedNeurons = workspace.getViewerSelecedNeurons(ViewerType.Graph);
if (selectedNeurons.length > 0) {
setMousePosition({
mouseX: originalEvent.clientX,
mouseY: originalEvent.clientY,
Expand Down Expand Up @@ -383,7 +388,7 @@ const TwoDViewer = () => {
});

updateNodeColors();
updateHighlighted(cy, Array.from(visibleActiveNeurons), Array.from(workspace.selectedNeurons), legendHighlights);
updateHighlighted(cy, Array.from(visibleActiveNeurons), selectedNeurons, legendHighlights);
checkSplitNeuronsInGraph();
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export const computeGraphDifferences = (
includePostEmbryonic: boolean,
) => {
const visibleActiveNeurons = getVisibleActiveNeuronsIn2D(workspace);
const selectedNeurons = workspace.getViewerSelecedNeurons(ViewerType.Graph);

// Current nodes and edges in the Cytoscape instance
const currentNodes = new Set(cy.nodes().map((node) => node.id()));
Expand Down Expand Up @@ -115,15 +116,7 @@ export const computeGraphDifferences = (
}
const groupPosition = calculateMeanPosition(groupNeurons, workspace);
nodesToAdd.push(
createNode(
nodeId,
workspace.selectedNeurons.has(nodeId),
Array.from(attributes),
groupPosition,
true,
undefined,
workspace.activeNeurons.has(nodeId),
),
createNode(nodeId, selectedNeurons.includes(nodeId), Array.from(attributes), groupPosition, true, undefined, workspace.activeNeurons.has(nodeId)),
);
} else {
let parent = undefined;
Expand All @@ -135,7 +128,8 @@ export const computeGraphDifferences = (
break;
}
}
const attributes = extractNeuronAttributes(workspace.availableNeurons[nodeId]);
const neuron = workspace.availableNeurons[nodeId];
const attributes = extractNeuronAttributes(neuron);
const neuronVisibility = workspace.visibilities[nodeId];
const position = neuronVisibility?.[ViewerType.Graph]?.defaultPosition ?? null;
nodesToAdd.push(createNode(nodeId, workspace.selectedNeurons.has(nodeId), attributes, position, false, parent, workspace.activeNeurons.has(nodeId)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ interface SplitJoinState {
export const processNeuronSplit = (workspace: Workspace, splitJoinState: SplitJoinState): SplitJoinState => {
const newSplit = new Set(splitJoinState.split);
const newJoin = new Set(splitJoinState.join);
const selectedNeurons = workspace.getViewerSelecedNeurons(ViewerType.Graph);

const newSelectedNeurons = new Set(workspace.selectedNeurons);
const newSelectedNeurons = new Set(selectedNeurons);
const graphViewDataUpdates: Record<string, Partial<GraphViewerData>> = {};

const groupModifications: Record<string, Set<string>> = {};
const groupsToDelete = new Set<string>();

for (const neuronId of workspace.selectedNeurons) {
for (const neuronId of selectedNeurons) {
if (!isNeuronClass(neuronId, workspace)) {
return;
}
Expand Down Expand Up @@ -53,7 +54,7 @@ export const processNeuronSplit = (workspace: Workspace, splitJoinState: SplitJo
}

workspace.customUpdate((draft) => {
draft.selectedNeurons = newSelectedNeurons;
draft.setSelection(Array.from(newSelectedNeurons), ViewerType.Graph);

for (const [groupId, neurons] of Object.entries(groupModifications)) {
if (neurons.size === 0) {
Expand Down Expand Up @@ -82,14 +83,15 @@ export const processNeuronSplit = (workspace: Workspace, splitJoinState: SplitJo
export const processNeuronJoin = (workspace: Workspace, splitJoinState: SplitJoinState): SplitJoinState => {
const newJoin = new Set(splitJoinState.join);
const newSplit = new Set(splitJoinState.split);
const selectedNeurons = workspace.getViewerSelecedNeurons(ViewerType.Graph);

const newSelectedNeurons = new Set(workspace.selectedNeurons);
const newSelectedNeurons = new Set(selectedNeurons);
const graphViewDataUpdates: Record<string, Partial<GraphViewerData>> = {};

const groupModifications: Record<string, Set<string>> = {};
const groupsToDelete = new Set<string>();

for (const neuronId of workspace.selectedNeurons) {
for (const neuronId of selectedNeurons) {
if (!isNeuronCell(neuronId, workspace)) {
return;
}
Expand Down Expand Up @@ -130,7 +132,7 @@ export const processNeuronJoin = (workspace: Workspace, splitJoinState: SplitJoi
}

workspace.customUpdate((draft) => {
draft.selectedNeurons = newSelectedNeurons;
draft.setSelection(Array.from(newSelectedNeurons), ViewerType.Graph);

for (const [groupId, neurons] of Object.entries(groupModifications)) {
if (neurons.size === 0) {
Expand Down
13 changes: 5 additions & 8 deletions applications/visualizer/frontend/src/helpers/twoD/twoDHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,14 @@ export const createEdge = (id: string, conn: Connection, workspace: Workspace, i
const label = createEdgeLabel(workspace, synapses);
const longLabel = createEdgeLongLabel(workspace, synapses);

let annotationClasses: string[] = [];
const annotationClasses: string[] = annotations.map((annotation) => annotationLegend[annotation]?.id).filter(Boolean);

if (includeAnnotations) {
annotationClasses = annotations.map((annotation) => annotationLegend[annotation]?.id).filter(Boolean);
if (annotationClasses.length === 0) {
annotationClasses.push(annotationLegend.notClassified.id);
}
} else {
annotationClasses.push(conn.type);
if (includeAnnotations && annotationClasses.length === 0) {
annotationClasses.push(annotationLegend.notClassified.id);
}

annotationClasses.push(conn.type);

const classes = annotationClasses.join(" ");
return {
group: "edges",
Expand Down
Loading

0 comments on commit ba6052c

Please sign in to comment.