Skip to content

Commit

Permalink
fix selection sync between tree-view and 3d-view
Browse files Browse the repository at this point in the history
  • Loading branch information
Krande committed Nov 9, 2024
1 parent 5fc4ec5 commit 1cba678
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 24 deletions.
5 changes: 3 additions & 2 deletions examples/scripts/gxml_develop/flat_plate.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ def main():
# pl5 = ada.Plate("pl5", [(0, 0), (10, 0), (10, 10), (0, 10)], 0.01, n=(1, 0, 0), xdir=(0, 1, 0))
# plates.append(pl5)
# beams += ada.Beam.array_from_list_of_segments(pl5.poly.segments3d, 'IPE300')
beams += ada.Beam.array_from_list_of_segments(pl.poly.segments3d, 'IPE300')
beams += ada.Beam.array_from_list_of_segments(pl3.poly.segments3d, 'IPE300')
bm_gen = ada.Counter(prefix="bm")
beams += ada.Beam.array_from_list_of_segments(pl.poly.segments3d, 'IPE300', name_gen=bm_gen)
beams += ada.Beam.array_from_list_of_segments(pl3.poly.segments3d, 'IPE300', name_gen=bm_gen)

# beams.append(ada.Beam("bm_yUP", (0, 0, 0), (0, 0, 10), 'IPE300', up=(0, 1, 0)))
# beams.append(ada.Beam("bm_xUP", (0, 0, 0), (0, 0, 10), 'IPE300', up=(1, 0, 0)))
Expand Down
Binary file modified src/ada/visit/rendering/resources/index.zip
Binary file not shown.
6 changes: 5 additions & 1 deletion src/frontend/src/components/tree_view/CustomNode.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';
import React, {useEffect, useState} from 'react';
import {NodeRendererProps} from 'react-arborist';
import {handleClickedNode} from "../../utils/tree_view/handleClickedNode";
import {useSelectedObjectStore} from "../../state/useSelectedObjectStore";

interface TreeNodeData {
id: string;
Expand All @@ -11,6 +12,9 @@ interface TreeNodeData {

export const CustomNode: React.FC<NodeRendererProps<TreeNodeData>> = ({style, node, dragHandle}) => {
const {data, isSelected, isOpen, children} = node;


// data.visuallySelected = false;
let hasChildren = false;
if (children && children.length > 0) {
hasChildren = true;
Expand Down
39 changes: 30 additions & 9 deletions src/frontend/src/components/tree_view/TreeViewComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import React, {useEffect, useRef, useState} from 'react';
import {useTreeViewStore} from '../../state/treeViewStore';
import {Tree} from "react-arborist";
import {NodeApi, Tree} from "react-arborist";
import {CustomNode} from './CustomNode';
import {handleSelectionChange} from "../../utils/tree_view/handleClickedNode";

const TreeViewComponent: React.FC = () => {
const { treeData, selectedNodeId, setSelectedNodeId } = useTreeViewStore();
const {treeData, setTree} = useTreeViewStore();
const containerRef = useRef<HTMLDivElement | null>(null);
const [treeHeight, setTreeHeight] = useState<number>(800); // Default height
const treeRef = useRef<any>(); // Use 'any' to allow custom properties
const isProgrammaticChange = useRef(false);

const treeNodes = treeData ? [{id: 'root', name: 'scene', children: [treeData]}] : [{id: 'root', name: 'root', children: []}];
const treeNodes = treeData ? [{id: 'root', name: 'scene', children: [treeData]}] : [{
id: 'root',
name: 'root',
children: []
}];

// Update the tree height based on the container size using ResizeObserver
useEffect(() => {
Expand All @@ -33,6 +40,23 @@ const TreeViewComponent: React.FC = () => {
};
}, []);

useEffect(() => {
if (treeRef.current) {
// Access the Tree API
const tree = treeRef.current
console.log(tree);
setTree(tree);
}

/* See the Tree API reference for all you can do with it. */
}, []);

const handleSelect = (ids: NodeApi[]) => {
if (!treeRef.current?.isProgrammaticChange) {
handleSelectionChange(ids);
}
};

return (
<div ref={containerRef} className="h-full max-h-screen overflow-y-auto pr-2">
<Tree
Expand All @@ -41,19 +65,16 @@ const TreeViewComponent: React.FC = () => {
height={treeHeight} // Use the dynamic height
selectionFollowsFocus={true}
data={treeNodes}
selection={selectedNodeId ? selectedNodeId : "root"}
ref={treeRef}
disableDrag={true}
disableDrop={true}
disableEdit={true}
openByDefault={false}
disableMultiSelection={false}

// If I use this, it will also trigger when I modify the selection programmatically. And bad things happen.
onSelect={(ids) => {
if (ids.length > 0) {
setSelectedNodeId(ids[0].id);
} else {
setSelectedNodeId(null);
}
handleSelect(ids);
}}
>
{CustomNode}
Expand Down
5 changes: 5 additions & 0 deletions src/frontend/src/state/treeViewStore.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {create} from 'zustand';
import {TreeApi} from "react-arborist";

export interface TreeNode {
id: string;
Expand All @@ -8,16 +9,20 @@ export interface TreeNode {

interface TreeViewState {
treeData: TreeNode | null;
tree: TreeApi<any> | null;
selectedNodeId: string | null;
setTreeData: (data: TreeNode) => void;
clearTreeData: () => void;
setSelectedNodeId: (id: string | null) => void;
isCollapsed: boolean;
setIsCollapsed: (collapsed: boolean) => void;
setTree: (tree: TreeApi<any>) => void;
}

export const useTreeViewStore = create<TreeViewState>((set) => ({
treeData: null,
tree: null,
setTree: (tree) => set({tree: tree}),
selectedNodeId: null,
setTreeData: (data) => set({treeData: data}),
clearTreeData: () => set({treeData: null}),
Expand Down
4 changes: 3 additions & 1 deletion src/frontend/src/state/useSelectedObjectStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ export const useSelectedObjectStore = create<SelectedObjectState>(
newSet.delete(drawRangeId);

if (newSet.size === 0) {
mesh.deselect();
newMap.delete(mesh); // Remove the entry if the Set is empty
} else {
newMap.set(mesh, newSet); // Update with the modified Set
mesh.highlightDrawRanges(Array.from(newSet || []));
}

}
mesh.highlightDrawRanges(Array.from(existingSet || []));
return {selectedObjects: newMap};
}),
clearSelectedObjects: () =>
Expand Down
35 changes: 25 additions & 10 deletions src/frontend/src/utils/mesh_select/handleClickMesh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,34 @@ export function handleClickMesh(event: ThreeEvent<PointerEvent>) {

perform_selection(mesh, shiftKey, rangeId);

// Update object info and tree view selection
const scene = useModelStore.getState().scene;
const hierarchy: Record<string, [string, string | number]> = scene?.userData['id_hierarchy'];
const [nodeName] = hierarchy[rangeId];

if (nodeName) {
useObjectInfoStore.getState().setName(nodeName);
const treeViewStore = useTreeViewStore.getState();
if (treeViewStore.treeData) {
const selectedNode = findNodeById(treeViewStore.treeData, nodeName);
if (selectedNode) {
treeViewStore.setSelectedNodeId(selectedNode.id);
const [last_selected] = hierarchy[rangeId];

// Update object info
useObjectInfoStore.getState().setName(last_selected);
const treeViewStore = useTreeViewStore.getState();
// Update tree view selection

if (treeViewStore.treeData && treeViewStore.tree) {

treeViewStore.tree.isProgrammaticChange = true; // Set flag
const node_ids: string[] = [];
for (let selectedObjects of useSelectedObjectStore.getState().selectedObjects) {
const [mesh, selectedRanges] = selectedObjects;
for (let rangeId of selectedRanges) {
const [nodeName] = hierarchy[rangeId];
if (nodeName) {
const selectedNode = findNodeById(treeViewStore.treeData, nodeName);
if (selectedNode) {
node_ids.push(selectedNode.id);
}
}
}
}
let last_selected_node = findNodeById(treeViewStore.treeData, last_selected);
treeViewStore.tree.setSelection({ids: node_ids, mostRecent: last_selected_node, anchor: last_selected_node});
treeViewStore.tree.isProgrammaticChange = false; // Clear flag after update
}
}

Expand All @@ -75,6 +89,7 @@ export function perform_selection(mesh: CustomBatchedMesh, shiftKey: boolean, ra
useSelectedObjectStore.getState().addSelectedObject(mesh, rangeId);
}
} else {
// Clear the selection if the draw range is already selected
useSelectedObjectStore.getState().clearSelectedObjects();
// Select the new draw range
useSelectedObjectStore.getState().addSelectedObject(mesh, rangeId);
Expand Down
32 changes: 31 additions & 1 deletion src/frontend/src/utils/tree_view/handleClickedNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {getDrawRangeByName} from "../mesh_select/getDrawRangeByName";
import {deselectObject} from "../mesh_select/deselectObject";
import {perform_selection} from "../mesh_select/handleClickMesh";
import {useObjectInfoStore} from "../../state/objectInfoStore";
import {NodeApi} from "react-arborist";
import {useSelectedObjectStore} from "../../state/useSelectedObjectStore";

export function handleClickedNode(event: React.MouseEvent, itemIds: string | null) {
if (itemIds !== null) {
Expand All @@ -25,7 +27,6 @@ export function handleClickedNode(event: React.MouseEvent, itemIds: string | nul

// if mesh is not null and mesh is instance of THREE.Mesh
if (mesh && !(mesh instanceof THREE.LineSegments) && !(mesh instanceof THREE.Points)) {
// console.log("mesh", mesh);
let shiftKey = event.shiftKey;
useObjectInfoStore.getState().setName(node_name);
perform_selection(mesh, shiftKey, rangeId);
Expand All @@ -34,4 +35,33 @@ export function handleClickedNode(event: React.MouseEvent, itemIds: string | nul
}
useTreeViewStore.getState().setSelectedNodeId(itemIds);
}
}

export function handleSelectionChange(ids: NodeApi[]) {
if (ids.length > 0) {
useSelectedObjectStore.getState().clearSelectedObjects();
for (let node of ids) {
let node_name = node.data.name;
console.log("node_name", node_name);

let draw_range_data = getDrawRangeByName(node_name);
if (!draw_range_data) {
console.error("Could not find draw range data for", node_name);
return;
}
const [key, rangeId, start, count] = draw_range_data;
let mesh_node_name = key.split("_")[2];

let mesh = getMeshFromName(mesh_node_name);
if (!mesh) {
console.error("Could not find mesh for", mesh_node_name);
return;
}
useObjectInfoStore.getState().setName(node_name);
useSelectedObjectStore.getState().addSelectedObject(mesh, rangeId);
}

} else {
useSelectedObjectStore.getState().clearSelectedObjects();
}
}

0 comments on commit 1cba678

Please sign in to comment.