Open Hann1bal opened 2 years ago
Ok i fix it for half. i found problem in data structure because my graph is heterogenus and one child node have link as child to another root child node. This bug because nodes not compare between and try to render two times same node. How fix it?
import React, {FC, useCallback, useEffect, useMemo, useRef, useState} from "react";
import {ForceGraph3D} from "react-force-graph";
import {observer} from "mobx-react-lite";
import {IGraph} from "../models/users/IGraph";
import SpriteText from "three-spritetext";
import * as THREE from "three";
import _ from "lodash";
import {INodes} from "../models/users/INodes";
import dat from "dat-gui";
const useForceUpdate = () => {
const setToggle = useState(false)[1];
return () => setToggle(b => !b);
};
const ExpandableGraph: FC<IGraph> = (graphsData: IGraph, ids: number) => {
const forceUpdate = useForceUpdate();
const fgRef = useRef();
const graphData = useMemo(
() => ({
Id: _.cloneDeep(graphsData.Id),
Name: _.cloneDeep(graphsData.Name),
nodes: _.cloneDeep(graphsData.nodes),
rootId: _.cloneDeep(graphsData.rootId),
links: _.cloneDeep(graphsData.links)
}),
[graphsData.Id, graphsData.Name, graphsData.nodes, graphsData.rootId, graphsData.links]
);
const [graphDataCopy, setGraphObject] = useState<IGraph>(graphData)
const [controls] = useState({'NodeId': 1});
const [controlsname] = useState({'Show Node As Text': false});
useEffect(()=>{
setGraphObject({
Id: _.cloneDeep(graphsData.Id),
Name: _.cloneDeep(graphsData.Name),
nodes: _.cloneDeep(graphsData.nodes),
rootId: _.cloneDeep(graphsData.rootId),
links: _.cloneDeep(graphsData.links)
})
}, [])
useEffect(() => {
const gui = new dat.GUI();
gui.add(controls, 'NodeId', graphData.nodes.map(node => node.id))
.onChange(forceUpdate);
gui.add(controlsname, 'Show Node As Text', [true, false])
.onChange(forceUpdate);
}, [])
const nodesById = useMemo(() => {
const nodesById = Object.fromEntries(graphDataCopy.nodes.map(node => [node.id, node]));
// link parent/children
graphDataCopy.nodes.forEach(node => {
node.collapsed = node.id !== controls["NodeId"];
node.childLinks = [];
});
graphDataCopy.links.forEach(link =>
nodesById[link.source].childLinks.push(link)
);
return nodesById;
}, [graphDataCopy.links, graphDataCopy.nodes, graphDataCopy.rootId]);
const getPrunedTree = useCallback(() => {
const visibleNodes = [];
const visibleLinks = [];
console.log(graphDataCopy.rootId);
(function traverseTree(node: INodes = nodesById[controls["NodeId"]]) {
visibleNodes.push(node);
if (node.collapsed) return;
visibleLinks.push(...node.childLinks);
node.childLinks
.map(link => ((typeof link.target) === 'object') ? link.target : nodesById[link.target]) // get child node
.forEach(traverseTree);
})();
return {nodes: visibleNodes, links: visibleLinks};
}, [nodesById]);
const [prunedTree, setPrunedTree] = useState(getPrunedTree());
const handleNodeClick = useCallback(node => {
node.collapsed = !node.collapsed; // toggle collapse state
setPrunedTree(getPrunedTree());
forceUpdate();
}, []);
return <ForceGraph3D
enablePointerInteraction={true}
ref={fgRef}
backgroundColor={'#000000'}
nodeThreeObjectExtend={true}
linkDirectionalArrowLength={5.5}
linkDirectionalArrowRelPos={1.3}
linkWidth={1.2}
linkDirectionalParticles={1}
linkDirectionalParticleWidth={2}
linkDirectionalParticleColor={"#ffe202"}
nodeThreeObject={(node) => {
const myText = new SpriteText(node.name);
const myText2 = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({color: 0x00ff00});
const cube = new THREE.Mesh(myText2, material);
const myScene = new THREE.Scene();
const myScene2 = new THREE.Scene();
myScene.add(myText);
myScene2.add(cube)
return controlsname['Show Node As Text'] ? myScene : myScene2
}}
graphData={prunedTree}
onNodeClick={handleNodeClick}
/>;
};
export default observer(ExpandableGraph);
But i did not understand how rerender graph without functions onClickHandler, because when i change rootId, to me need a click on node for refresh prunedTree? And when i change views default node or spritetext from node to sprite text returned good, but when i try make a rollback state doesn't changing and spritetext not change back to ball or cube.
Windows 10 Chrome React 17.0.2 Typescript
And how to move gui bar to render window?
Can add in previews how add controls menu. Because i'll try to add menu and nowhere can't find examples how to use. And One more questions: how to reuse data in collapsed graph?
but give that error
Uncaught TypeError: Cannot read properties of undefined (reading 'collapsed') at traverseTree (Graph3D.tsx:64:1) at Graph3D.tsx:69:1 at ExpandableGraph (Graph3D.tsx:74:1) at observer.ts:57:1 at useObserver.ts:115:1 at trackDerivedFunction (derivation.ts:179:1) at Reaction.track (reaction.ts:137:1) at useObserver (useObserver.ts:113:1) at wrappedComponent (observer.ts:57:1) at renderWithHooks (react-dom.development.js:15020:1)
Because data memorized and after changing incoming data state and if income new values copy data stay same?