diff --git a/src/components/Universe/Controls/CameraAnimations/index.ts b/src/components/Universe/Controls/CameraAnimations/index.ts index bc8fca123..ccc163e6e 100644 --- a/src/components/Universe/Controls/CameraAnimations/index.ts +++ b/src/components/Universe/Controls/CameraAnimations/index.ts @@ -22,9 +22,7 @@ export const useCameraAnimations = ( useAutoNavigate(cameraControlsRef) - const isUserDragging = useControlStore((s) => s.isUserDragging) - - const { graphRadius, disableCameraRotation } = useGraphStore((s) => s) + const { graphRadius } = useGraphStore((s) => s) useEffect(() => { if (!enabled) { @@ -33,13 +31,6 @@ export const useCameraAnimations = ( } }, [enabled]) - // useEffect(() => { - // if (cameraControlsRef.current && graphRadius) { - // cameraControlsRef.current.maxDistance = cameraControlsRef.current.getDistanceToFitSphere(graphRadius + 200) - // cameraControlsRef.current.minDistance = 100 - // } - // }, [graphRadius, cameraControlsRef]) - useEffect(() => { if (!selectedNode && cameraControlsRef.current) { cameraControlsRef.current.setLookAt( @@ -58,6 +49,9 @@ export const useCameraAnimations = ( useFrame((_, delta) => { if (cameraControlsRef.current) { // do camera rotation + const { disableCameraRotation } = useGraphStore.getState() + const { isUserDragging } = useControlStore.getState() + if (!disableCameraRotation && !isUserDragging) { cameraControlsRef.current.azimuthAngle += autoRotateSpeed * delta * MathUtils.DEG2RAD } diff --git a/src/components/Universe/Graph/Cubes/SelectionDataNodes/Connections/Connection/index.tsx b/src/components/Universe/Graph/Cubes/SelectionDataNodes/Connections/Connection/index.tsx index 8c5f202d5..c6d9b2b61 100644 --- a/src/components/Universe/Graph/Cubes/SelectionDataNodes/Connections/Connection/index.tsx +++ b/src/components/Universe/Graph/Cubes/SelectionDataNodes/Connections/Connection/index.tsx @@ -30,9 +30,9 @@ const _Connection = (props: LineComponentProps) => { points={[sourceX, sourceY, sourceZ, targetX, targetY, targetZ]} /> - + - + {label} diff --git a/src/components/Universe/Graph/Cubes/SelectionDataNodes/Connections/index.tsx b/src/components/Universe/Graph/Cubes/SelectionDataNodes/Connections/index.tsx index a58248659..6a6c49a80 100644 --- a/src/components/Universe/Graph/Cubes/SelectionDataNodes/Connections/index.tsx +++ b/src/components/Universe/Graph/Cubes/SelectionDataNodes/Connections/index.tsx @@ -1,20 +1,97 @@ -import { memo } from 'react' +import { memo, useEffect, useRef } from 'react' +import { Box3, Color, Group, Sphere, Vector3 } from 'three' +import { Line2 } from 'three-stdlib' +import { useShallow } from 'zustand/react/shallow' import { useGraphStore } from '~/stores/useGraphStore' -import { Link } from '~/types' +import { Link, NodeExtended } from '~/types' import { LinkPosition } from '../../..' import { Connection } from './Connection' type Props = { - linksPosition: Map + links: Link[] + nodes: NodeExtended[] } -export const Connections = memo(({ linksPosition }: Props) => { - const { selectionGraphData } = useGraphStore((s) => s) +export const Connections = memo(({ links, nodes }: Props) => { + const groupRef = useRef(null) + const linksPositionRef = useRef(new Map()) + const { setSelectionGraphRadius } = useGraphStore(useShallow((s) => s)) + + useEffect(() => { + if (!groupRef.current) { + return + } + + console.log('connection to updated') + + const grConnections = groupRef.current + + grConnections.children.forEach((g, i) => { + const r = g.children[0] + const text = g.children[1] + + if (r instanceof Line2) { + const Line = r as Line2 + const link = links[i] + + if (link) { + const sourceNode = nodes.find((n: NodeExtended) => n.ref_id === link.source) + const targetNode = nodes.find((n: NodeExtended) => n.ref_id === link.target) + + if (!sourceNode || !targetNode) { + return + } + + const { x: sx, y: sy } = sourceNode + const { x: tx, y: ty } = targetNode + + linksPositionRef.current.set(link.ref_id, { + sx, + sy, + tx, + ty, + sz: 0, + tz: 0, + }) + + const midPoint = new Vector3((sx + tx) / 2, (sy + ty) / 2, 0) + + text.position.set(midPoint.x, midPoint.y, 1) + + let angle = Math.atan2(ty - sy, tx - sx) + + if (tx < sx || (Math.abs(tx - sx) < 0.01 && ty < sy)) { + angle += Math.PI + } + + text.rotation.set(0, 0, angle) + + Line.geometry.setPositions([sx, sy, 0, tx, ty, 0]) + + const { material } = Line + + material.color = new Color('white') + } + } + }) + + console.log('connection updated') + + const nodesVector = nodes.map((i: NodeExtended) => new Vector3(i.x, i.y, i.z)) + + const boundingBox = new Box3().setFromPoints(nodesVector) + + const boundingSphere = new Sphere() + + boundingBox.getBoundingSphere(boundingSphere) + + setSelectionGraphRadius(boundingSphere.radius) + }, [links, setSelectionGraphRadius, nodes]) return ( - - {selectionGraphData?.links.map((l: Link) => { - const position = linksPosition.get(l.ref_id) || { + + {links?.map((l: Link) => { + const position = linksPositionRef.current?.get(l.ref_id) || { sx: 0, sy: 0, sz: 0, diff --git a/src/components/Universe/Graph/Cubes/SelectionDataNodes/Node/index.tsx b/src/components/Universe/Graph/Cubes/SelectionDataNodes/Node/index.tsx index 1f07b1446..117288b3e 100644 --- a/src/components/Universe/Graph/Cubes/SelectionDataNodes/Node/index.tsx +++ b/src/components/Universe/Graph/Cubes/SelectionDataNodes/Node/index.tsx @@ -1,4 +1,8 @@ +import { Html } from '@react-three/drei' +import { useFrame } from '@react-three/fiber' +import { useRef } from 'react' import styled from 'styled-components' +import { Mesh, Vector3 } from 'three' import { Flex } from '~/components/common/Flex' import { Icons } from '~/components/Icons' import CloseIcon from '~/components/Icons/CloseIcon' @@ -17,12 +21,25 @@ type Props = { node: NodeExtended rounded?: boolean selected: boolean - onClick: () => void + onClick: (id: string) => void + x: number + y: number + z: number + id: string } -export const Node = ({ onClick, node, selected, rounded = true }: Props) => { +export const Node = ({ onClick, node, selected, rounded = true, x, y, z, id }: Props) => { + const nodeRef = useRef(null) + const { normalizedSchemasByType, getNodeKeysByType } = useSchemaStore((s) => s) const setSelectedNode = useGraphStore((s) => s.setSelectedNode) + const targetPosition = new Vector3(x, y, z) + + useFrame(() => { + if (nodeRef.current) { + nodeRef.current.position.lerp(targetPosition, 0.05) + } + }) const primaryIcon = normalizedSchemasByType[node.node_type]?.icon @@ -34,26 +51,32 @@ export const Node = ({ onClick, node, selected, rounded = true }: Props) => { const titleShortened = title ? truncateText(title, 30) : '' return ( - - <> - {selected ? ( - - setSelectedNode(null)}> - - -
{Icon ? : }
- {titleShortened} -
- ) : ( + + + <> - -
{Icon ? : }
-
- {titleShortened} + {selected ? ( + + setSelectedNode(null)}> + + +
{Icon ? : }
+ {titleShortened} +
+ ) : ( + <> + onClick(id)} rounded={rounded}> + + {!node?.properties?.image_url ? {Icon ? : } : null} + + + {titleShortened} + + )} - )} - -
+
+ +
) } @@ -105,8 +128,8 @@ const IconButton = styled(Flex)` position: absolute; top: -10px; right: -10px; - width: 24px; - height: 24px; + width: 30px; + height: 30px; border-radius: 40px; display: flex; @@ -115,8 +138,22 @@ const IconButton = styled(Flex)` background: black; color: #ffffff; border-radius: 100%; - font-size: 16px; + font-size: 30px; cursor: pointer; transition: opacity 0.4s; box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.5); ` + +type AvatarProps = { + src: string +} + +const Avatar = styled(Flex)` + background-image: ${({ src }) => `url(${src})`}; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + width: 32px; + height: 32px; + border-radius: 50%; +` diff --git a/src/components/Universe/Graph/Cubes/SelectionDataNodes/index.tsx b/src/components/Universe/Graph/Cubes/SelectionDataNodes/index.tsx index b8e52c5fe..930036a8a 100644 --- a/src/components/Universe/Graph/Cubes/SelectionDataNodes/index.tsx +++ b/src/components/Universe/Graph/Cubes/SelectionDataNodes/index.tsx @@ -1,70 +1,117 @@ -import { Html } from '@react-three/drei' -import { forceLink, forceManyBody, forceRadial, forceSimulation } from 'd3-force-3d' import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { Box3, Color, Group, Sphere, Vector3 } from 'three' -import { Line2 } from 'three-stdlib' +import { Group } from 'three' import { useShallow } from 'zustand/react/shallow' import { usePrevious } from '~/hooks/usePrevious' import { fetchNodeEdges } from '~/network/fetchGraphData' import { useDataStore } from '~/stores/useDataStore' -import { useGraphStore, useSelectedNode, useSelectedNodeRelativeIds } from '~/stores/useGraphStore' -import { useSchemaStore } from '~/stores/useSchemaStore' -import { ForceSimulation } from '~/transformers/forceSimulation' -import { GraphData, Link, Node, NodeExtended } from '~/types' +import { useGraphStore, useSelectedNode } from '~/stores/useGraphStore' +import { Link, Node, NodeExtended } from '~/types' import { LinkPosition } from '../..' import { Connections } from './Connections' import { Node as GraphNode } from './Node' -const MAX_LENGTH = 6 +const radius = 50 +const MAX_LENGTH = 5 + +type GraphData = { + links: Link[] + nodes: NodeExtended[] +} export const SelectionDataNodes = memo(() => { - const [simulation2d, setSimulation2D] = useState(null) + const [selectionData, setSelectionData] = useState() - const { dataInitial, nodesNormalized, addNewNode } = useDataStore((s) => s) + const { addNewNode, nodesNormalized } = useDataStore((s) => s) const selectedNode = useSelectedNode() const groupRef = useRef(null) - const linksPositionRef = useRef(new Map()) + const [pathGraph, setPathGraph] = useState(null) - const selectedNodeRelativeIds = useSelectedNodeRelativeIds().slice(0, MAX_LENGTH) + const linksPositionRef = useRef(new Map()) const prevSelectedNodeId = usePrevious(selectedNode?.ref_id) - const { normalizedSchemasByType } = useSchemaStore((s) => s) + const { setSelectedNode } = useGraphStore(useShallow((s) => s)) + + const newData: GraphData = useMemo(() => { + if (!selectionData?.nodes.length) { + return { nodes: [], links: [] } + } + + const oldNodes = pathGraph?.nodes || [] + + // Combine `oldNodes` and `selectionData.nodes` for position calculations + const combinedNodes = [...oldNodes, ...selectionData.nodes] + + // Calculate the angular spacing considering both old and new nodes + const totalNodes = combinedNodes.length + const thetaSpan = (Math.PI * 2) / totalNodes // Angle between points + + const nodes = selectionData.nodes.map((node, index) => { + // Check if the node exists in oldNodes + const existingNode = oldNodes.find((oldNode) => oldNode.ref_id === node.ref_id) - const { selectionGraphData, selectionPath, setSelectionData, setSelectedNode, setSelectionGraphRadius } = - useGraphStore(useShallow((s) => s)) + if (existingNode) { + // Retain the old node's position + return existingNode + } + + // Calculate new position for nodes not in oldNodes + const theta = thetaSpan * (oldNodes.length + index) // Offset by oldNodes count + const x = node.ref_id === selectedNode?.ref_id ? 0 : Math.cos(theta) * radius + const y = node.ref_id === selectedNode?.ref_id ? 0 : Math.sin(theta) * radius + const z = node.ref_id === selectedNode?.ref_id ? 0 : 0 - const pathNodes = useMemo(() => { - const nodes: NodeExtended[] = selectionPath - .slice(-3, -1) - .filter((id) => !!nodesNormalized.get(id)) - .map((i, index) => { - const node = nodesNormalized.get(i) as unknown as Node + return { ...node, x, y, z } + }) - return { ...node, fx: 0, fy: -(index + 1) * 200, fz: 0, x: 0, y: 0, z: 0 } - }) + // Retain old nodes with their original positions + const retainedOldNodes = oldNodes.filter( + (oldNode) => !selectionData.nodes.some((node) => node.ref_id === oldNode.ref_id), + ) - return nodes - }, [nodesNormalized, selectionPath]) + // Merge the calculated nodes with retained old nodes + const mergedNodes = [...nodes, ...retainedOldNodes] + + return { nodes: mergedNodes, links: selectionData.links } + }, [selectionData, selectedNode, pathGraph?.nodes]) + + const graphData: GraphData = useMemo(() => { + if (newData?.nodes?.length) { + return newData + } + + console.log(pathGraph) + + if (pathGraph) { + return { + nodes: pathGraph.nodes, + links: pathGraph.links, + } + } + + const selected = selectedNode?.ref_id ? nodesNormalized.get(selectedNode?.ref_id || '') : null + + return { + nodes: selected ? [{ ...selected, x: 0, y: 0, z: 0 }] : [], + links: [], + } + }, [newData, pathGraph, selectedNode?.ref_id, nodesNormalized]) useEffect(() => { const init = async () => { if (selectedNode?.ref_id && selectedNode.ref_id !== prevSelectedNodeId) { try { - const data = await fetchNodeEdges(selectedNode.ref_id, 0, 5) + const data = await fetchNodeEdges(selectedNode.ref_id, 0, 5, { useSubGraph: false }) if (data) { - const filteredNodes: NodeExtended[] = data.nodes.filter( - (node, index) => node.ref_id !== selectedNode.ref_id && index < 7, + const filteredNodes: Node[] = data.nodes.filter( + (node, index) => node.ref_id !== selectedNode.ref_id && index < MAX_LENGTH, ) const graphNodes = filteredNodes.map((node: Node) => ({ ...node, x: 0, y: 0, z: 0 })) - const nodes: NodeExtended[] = [ - ...graphNodes, - { ...selectedNode, x: 0, y: 0, z: 0, fx: 0, fy: 0, fz: 0 } as NodeExtended, - ] + const nodes: NodeExtended[] = [...graphNodes, { ...selectedNode, x: 0, y: 0, z: 0, fx: 0, fy: 0, fz: 0 }] const links = data.edges.filter( (link: Link) => @@ -73,11 +120,8 @@ export const SelectionDataNodes = memo(() => { ) setSelectionData({ nodes, links: links as unknown as GraphData['links'] }) - setSimulation2D(null) linksPositionRef.current = new Map() - // - addNewNode({ nodes: filteredNodes, edges: links }) } } catch (error) { @@ -91,194 +135,56 @@ export const SelectionDataNodes = memo(() => { } }, [addNewNode, prevSelectedNodeId, selectedNode, setSelectionData]) - useEffect(() => { - return - - const structuredLinks = structuredClone(dataInitial?.links || []) - - if (prevSelectedNodeId === selectedNode?.ref_id) { - return - } - - const graphNodes: NodeExtended[] = selectedNodeRelativeIds - .filter((id) => !!nodesNormalized.get(id)) - .map((id: string) => { - const node = nodesNormalized.get(id) as unknown as Node - - return { ...node, x: 0, y: 0, z: 0 } - }) - - const nodes: NodeExtended[] = [ - ...graphNodes, - { ...selectedNode, x: 0, y: 0, z: 0, fx: 0, fy: 0, fz: 0 } as NodeExtended, - ] - - if (nodes) { - const links = structuredLinks.filter( - (link: Link) => - nodes.some((node: NodeExtended) => node.ref_id === link.target) && - nodes.some((node: NodeExtended) => node.ref_id === link.source), - ) - - setSelectionData({ nodes, links: links as unknown as GraphData['links'] }) - setSimulation2D(null) - linksPositionRef.current = new Map() - } - }, [dataInitial, selectedNode, selectedNodeRelativeIds, setSelectionData, prevSelectedNodeId, nodesNormalized]) - - useEffect(() => { - if (simulation2d || !selectionGraphData.nodes.length) { - return - } - - const structuredLinks = structuredClone(selectionGraphData.links) - - const simulation = forceSimulation([]) - .numDimensions(2) - .stop() - .nodes(selectionGraphData.nodes) - .force( - 'link', - forceLink() - .links(structuredLinks) - .id((d: NodeExtended) => d.ref_id) - .distance(() => 150), - ) - .force('radial', forceRadial(20, 0, 0, 0).strength(0)) - .force('charge', forceManyBody().strength(-500)) - .alpha(1) - .restart() - - setSimulation2D(simulation) - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectionGraphData, simulation2d]) - - useEffect(() => { - if (!simulation2d) { - return - } - - simulation2d.on('tick', () => { - if (!groupRef.current) { - return - } - - const gr = groupRef.current as Group - - gr.children.forEach((mesh, index) => { - const simulationNode = simulation2d.nodes()[index] - - if (simulationNode) { - mesh.position.set(simulationNode.x, simulationNode.y, simulationNode.z) - } - }) - - const grConnections = groupRef.current.getObjectByName('simulation-3d-group__connections') as Group - - grConnections.children.forEach((g, i) => { - const r = g.children[0] - const text = g.children[1] - - if (r instanceof Line2) { - const Line = r as Line2 - const link = selectionGraphData?.links[i] - - if (link) { - const sourceNode = simulation2d.nodes().find((n: NodeExtended) => n.ref_id === link.source) - const targetNode = simulation2d.nodes().find((n: NodeExtended) => n.ref_id === link.target) - - if (!sourceNode || !targetNode) { - return - } - - const { x: sx, y: sy } = sourceNode - const { x: tx, y: ty } = targetNode - - linksPositionRef.current.set(link.ref_id, { - sx, - sy, - tx, - ty, - sz: 0, - tz: 0, - }) - - const midPoint = new Vector3((sx + tx) / 2, (sy + ty) / 2, 0) - - text.position.set(midPoint.x, midPoint.y, 1) - - let angle = Math.atan2(ty - sy, tx - sx) - - if (tx < sx || (Math.abs(tx - sx) < 0.01 && ty < sy)) { - angle += Math.PI - } - - text.rotation.set(0, 0, angle) - - Line.geometry.setPositions([sx, sy, 0, tx, ty, 0]) - - const { material } = Line - - material.color = new Color('white') - } - } - }) - }) - - simulation2d.on('end', () => { - const nodesVector = simulation2d.nodes().map((i: NodeExtended) => new Vector3(i.x, i.y, i.z)) - - const boundingBox = new Box3().setFromPoints(nodesVector) - - const boundingSphere = new Sphere() - - boundingBox.getBoundingSphere(boundingSphere) - - const sphereRadius = Math.min(5000, boundingSphere.radius) - - setSelectionGraphRadius(sphereRadius) - }) - }, [normalizedSchemasByType, selectionGraphData?.links, simulation2d, setSelectionGraphRadius]) - const handleSelect = useCallback( - (node: NodeExtended) => { - setSelectedNode(node) + (id: string) => { + if (selectedNode) { + const newSelectedNode = graphData.nodes.find((i) => i.ref_id === id) + + const nodes = [ + { ...newSelectedNode, x: 0, y: 0, z: 0, fx: 0, fy: 0, fz: 0 }, + { + ...selectedNode, + ...(newSelectedNode?.x !== undefined ? { fx: -newSelectedNode.x, x: -newSelectedNode.x } : { x: 0 }), + ...(newSelectedNode?.y !== undefined ? { fy: -newSelectedNode.y, y: -newSelectedNode.y } : { y: 0 }), + ...(newSelectedNode?.z !== undefined ? { fz: newSelectedNode?.z, z: newSelectedNode.z } : { z: 0 }), + }, + ] + + const links = graphData.links.filter( + (i) => + (i.target === selectedNode?.ref_id && i.source === id) || + (i.source === selectedNode?.ref_id && i.target === id), + ) + + console.log(links, 'here') + setSelectionData(null) + setPathGraph({ nodes: nodes as NodeExtended[], links }) + + if (newSelectedNode) { + setSelectedNode(newSelectedNode) + } + } }, - [setSelectedNode], + [graphData.links, graphData.nodes, selectedNode, setSelectedNode], ) return ( <> - {selectionGraphData?.nodes.map((node) => ( - - - handleSelect(node)} - selected={node.ref_id === selectedNode?.ref_id} - /> - - + {graphData.nodes?.map((node) => ( + ))} - + - {false && ( - - {pathNodes.map((node) => ( - - - handleSelect(node)} - selected={node.ref_id === selectedNode?.ref_id} - /> - - - ))} - - )} ) }) diff --git a/src/components/Universe/SelectionContent/Controls/index.tsx b/src/components/Universe/SelectionContent/Controls/index.tsx index 898eade77..24b5d61d1 100644 --- a/src/components/Universe/SelectionContent/Controls/index.tsx +++ b/src/components/Universe/SelectionContent/Controls/index.tsx @@ -10,11 +10,15 @@ export const Controls = () => { const [smoothTime] = useState(0.8) useEffect(() => { + console.log(selectionGraphRadius, 'radius') + if (cameraControlsRef.current) { + const distance = cameraControlsRef.current.getDistanceToFitSphere(50 + 5) + cameraControlsRef.current.setLookAt( selectionGraphCameraPosition.x, selectionGraphCameraPosition.y, - selectionGraphRadius * 2, + distance, 0, 0, 0, @@ -31,7 +35,7 @@ export const Controls = () => { boundaryEnclosesCamera makeDefault maxDistance={12000} - minDistance={100} + minDistance={1} polarRotateSpeed={0} smoothTime={smoothTime} /> diff --git a/src/network/fetchGraphData/index.ts b/src/network/fetchGraphData/index.ts index 1c56acf55..ebc73b322 100644 --- a/src/network/fetchGraphData/index.ts +++ b/src/network/fetchGraphData/index.ts @@ -86,6 +86,7 @@ export const fetchNodeEdges = async ( include_properties: includeProperties.toString(), includeContent: includeContent.toString(), depth: depth.toString(), + top_node_count: '5', use_sub_graph: useSubGraph.toString(), ...(nodeType.length > 0 && { node_type: JSON.stringify(nodeType) }), // Add node_type if not empty }).toString() diff --git a/src/stores/useDataStore/index.ts b/src/stores/useDataStore/index.ts index 2dfb79131..93814fb5e 100644 --- a/src/stores/useDataStore/index.ts +++ b/src/stores/useDataStore/index.ts @@ -23,8 +23,8 @@ export type DataStore = { splashDataLoading: boolean abortRequest: boolean categoryFilter: NodeType | null - dataInitial: { nodes: NodeExtended[]; links: Link[] } | null - dataNew: { nodes: NodeExtended[]; links: Link[] } | null + dataInitial: { nodes: Node[]; links: Link[] } | null + dataNew: { nodes: Node[]; links: Link[] } | null filters: FilterParams isFetching: boolean isLoadingNew: boolean @@ -41,7 +41,7 @@ export type DataStore = { seedQuestions: string[] | null runningProjectId: string runningProjectMessages: string[] - nodesNormalized: Map + nodesNormalized: Map linksNormalized: Map setTrendingTopics: (trendingTopics: Trending[]) => void @@ -137,7 +137,7 @@ const defaultData: Omit< runningProjectId: '', hideNodeDetails: false, nodeTypes: [], - nodesNormalized: new Map(), + nodesNormalized: new Map(), linksNormalized: new Map(), } @@ -243,7 +243,7 @@ export const useDataStore = create()( nodesFilteredByFilters.forEach((node) => { if (!normalizedNodesMap.has(node.ref_id)) { - normalizedNodesMap.set(node.ref_id, node) + normalizedNodesMap.set(node.ref_id, { ...node, sources: [], targets: [] }) newNodes.push(node) } }) @@ -265,6 +265,24 @@ export const useDataStore = create()( ) { normalizedLinksMap.set(link.ref_id, link) newLinks.push(link) + + // Update sources and targets for the respective nodes + const sourceNode = normalizedNodesMap.get(link.source) + const targetNode = normalizedNodesMap.get(link.target) + + if (sourceNode && targetNode) { + if (sourceNode.targets) { + sourceNode.targets.push(link.target) + } else { + sourceNode.targets = [link.target] + } + + if (targetNode.sources) { + targetNode.sources.push(link.source) + } else { + targetNode.sources = [link.source] + } + } } }) @@ -320,7 +338,7 @@ export const useDataStore = create()( dataNew: null, runningProjectId: '', nodeTypes: [], - nodesNormalized: new Map(), + nodesNormalized: new Map(), linksNormalized: new Map(), }) }, diff --git a/src/types/index.ts b/src/types/index.ts index f33828606..3df5b7400 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -153,6 +153,8 @@ export type NodeExtended = Node & { fx?: number fy?: number fz?: number + sources?: string[] + targets?: string[] } export type Link = {