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

Feature/selection view path #2575

Merged
merged 4 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
30 changes: 16 additions & 14 deletions src/components/Universe/Controls/CameraAnimations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const useCameraAnimations = (

const isUserDragging = useControlStore((s) => s.isUserDragging)

const { graphStyle, graphRadius, disableCameraRotation } = useGraphStore((s) => s)
const { graphRadius, disableCameraRotation } = useGraphStore((s) => s)

useEffect(() => {
if (!enabled) {
Expand All @@ -33,22 +33,24 @@ export const useCameraAnimations = (
}
}, [enabled])

useEffect(() => {
if (cameraControlsRef.current && graphRadius) {
if (graphStyle === 'sphere') {
cameraControlsRef.current.maxDistance = 8000
cameraControlsRef.current.minDistance = 200
cameraControlsRef.current.setTarget(0, 0, 500, true)
} else {
cameraControlsRef.current.maxDistance = cameraControlsRef.current.getDistanceToFitSphere(graphRadius + 200)
cameraControlsRef.current.minDistance = 100
}
}
}, [graphRadius, graphStyle, cameraControlsRef])
// 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(initialCameraPosition.x, initialCameraPosition.y, graphRadius, 0, 0, 0, true)
cameraControlsRef.current.setLookAt(
initialCameraPosition.x,
initialCameraPosition.y,
graphRadius + 200,
0,
0,
0,
true,
)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedNode, graphRadius])
Expand Down
5 changes: 3 additions & 2 deletions src/components/Universe/Graph/Connections/LineComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { memo, useEffect, useRef } from 'react'
import { Line2 } from 'three-stdlib'
import { useGraphStore } from '~/stores/useGraphStore'
import { LINE_WIDTH } from '../../constants'
import { fontProps } from '../Cubes/Text/constants'

type LineComponentProps = {
label: string
Expand Down Expand Up @@ -78,8 +79,8 @@ const _LineComponent = (props: LineComponentProps) => {
points={[sourceX, sourceY, sourceZ, targetX, targetY, targetZ]}
/>
<Billboard>
<Text anchorX="center" anchorY="middle" color="white" fontSize={10}>
{label}1
<Text anchorX="center" anchorY="middle" color="white" {...fontProps} fontSize={10}>
{label}
</Text>
</Billboard>
</group>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Line, Text } from '@react-three/drei'
import { memo, useRef } from 'react'
import { Line2 } from 'three-stdlib'
import { fontProps } from '../../../Text/constants'

type LineComponentProps = {
label: string
Expand Down Expand Up @@ -29,7 +30,9 @@ const _Connection = (props: LineComponentProps) => {
points={[sourceX, sourceY, sourceZ, targetX, targetY, targetZ]}
/>
<mesh>
<Text anchorX="center" anchorY="middle" color="white" fontSize={10}>
<planeGeometry args={[label.length * 6, 12]} />
<meshBasicMaterial color="black" />
<Text anchorX="center" anchorY="middle" color="white" {...fontProps} fontSize={6}>
{label}
</Text>
</mesh>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,16 @@ export const Node = ({ onClick, node, selected, rounded = true }: Props) => {
)
}

const Wrapper = styled(Flex)``
const Wrapper = styled(Flex)`
background: black;
`

const Text = styled(Flex)`
color: ${colors.white};
margin-left: 16px;
font-weight: 700;
width: 100px;
font-size: 16px;
`

const Tag = styled(Flex)<TagProps>`
Expand Down Expand Up @@ -90,8 +94,11 @@ const Tag = styled(Flex)<TagProps>`
`

const Selected = styled(Tag)`
width: 300px;
height: 150px;
width: 200px;
height: 100px;
flex-direction: row;
justify-content: center;
align-items: center;
`

const IconButton = styled(Flex)`
Expand Down
175 changes: 126 additions & 49 deletions src/components/Universe/Graph/Cubes/SelectionDataNodes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import { Html } from '@react-three/drei'
import { forceLink, forceManyBody, forceRadial, forceSimulation } from 'd3-force-3d'
import { memo, useCallback, useEffect, useRef, useState } from 'react'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Box3, Color, Group, Sphere, Vector3 } from 'three'
import { Line2 } from 'three-stdlib'
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, NodeExtended } from '~/types'
import { GraphData, Link, Node, NodeExtended } from '~/types'
import { LinkPosition } from '../..'
import { Connections } from './Connections'
import { Node } from './Node'
import { Node as GraphNode } from './Node'

const MAX_LENGTH = 6

export const SelectionDataNodes = memo(() => {
const [simulation2d, setSimulation2D] = useState<ForceSimulation | null>(null)

const { dataInitial } = useDataStore((s) => s)
const { dataInitial, nodesNormalized, addNewNode } = useDataStore((s) => s)
const selectedNode = useSelectedNode()
const groupRef = useRef<Group>(null)

Expand All @@ -31,28 +32,87 @@ export const SelectionDataNodes = memo(() => {

const { normalizedSchemasByType } = useSchemaStore((s) => s)

const { selectionGraphData, setSelectionData, setSelectedNode, setSelectionGraphRadius } = useGraphStore(
useShallow((s) => s),
)
const { selectionGraphData, selectionPath, setSelectionData, setSelectedNode, setSelectionGraphRadius } =
useGraphStore(useShallow((s) => s))

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, fx: 0, fy: -(index + 1) * 200, fz: 0, x: 0, y: 0, z: 0 }
})

return nodes
}, [nodesNormalized, selectionPath])

useEffect(() => {
const init = async () => {
if (selectedNode?.ref_id && selectedNode.ref_id !== prevSelectedNodeId) {
try {
const data = await fetchNodeEdges(selectedNode.ref_id, 0, 5)

if (data) {
const filteredNodes: NodeExtended[] = data.nodes.filter(
(node, index) => node.ref_id !== selectedNode.ref_id && index < 7,
)

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 links = data.edges.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()

//

addNewNode({ nodes: filteredNodes, edges: links })
}
} catch (error) {
console.error(error)
}
}
}

if (selectedNode) {
init()
}
}, [addNewNode, prevSelectedNodeId, selectedNode, setSelectionData])

useEffect(() => {
const structuredNodes = structuredClone(dataInitial?.nodes || [])
return

const structuredLinks = structuredClone(dataInitial?.links || [])

if (prevSelectedNodeId === selectedNode?.ref_id) {
return
}

const nodes = structuredNodes
.filter(
(f: NodeExtended) => f.ref_id === selectedNode?.ref_id || selectedNodeRelativeIds.includes(f?.ref_id || ''),
)
.map((n: NodeExtended) => {
const fixedPosition = n.ref_id === selectedNode?.ref_id ? { fx: 0, fy: 0, fz: 0 } : {}
const graphNodes: NodeExtended[] = selectedNodeRelativeIds
.filter((id) => !!nodesNormalized.get(id))
.map((id: string) => {
const node = nodesNormalized.get(id) as unknown as Node

return { ...n, x: 0, y: 0, z: 0, ...fixedPosition }
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) =>
Expand All @@ -64,7 +124,7 @@ export const SelectionDataNodes = memo(() => {
setSimulation2D(null)
linksPositionRef.current = new Map()
}
}, [dataInitial, selectedNode, selectedNodeRelativeIds, setSelectionData, prevSelectedNodeId])
}, [dataInitial, selectedNode, selectedNodeRelativeIds, setSelectionData, prevSelectedNodeId, nodesNormalized])

useEffect(() => {
if (simulation2d || !selectionGraphData.nodes.length) {
Expand All @@ -84,8 +144,8 @@ export const SelectionDataNodes = memo(() => {
.id((d: NodeExtended) => d.ref_id)
.distance(() => 150),
)
.force('radial', forceRadial(500, 0, 0, 0).strength(0))
.force('charge', forceManyBody().strength(-1000))
.force('radial', forceRadial(20, 0, 0, 0).strength(0))
.force('charge', forceManyBody().strength(-500))
.alpha(1)
.restart()

Expand All @@ -94,13 +154,6 @@ export const SelectionDataNodes = memo(() => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectionGraphData, simulation2d])

useEffect(
() => () => {
setSelectionData({ nodes: [], links: [] })
},
[setSelectionData],
)

useEffect(() => {
if (!simulation2d) {
return
Expand All @@ -124,8 +177,8 @@ export const SelectionDataNodes = memo(() => {
const grConnections = groupRef.current.getObjectByName('simulation-3d-group__connections') as Group

grConnections.children.forEach((g, i) => {
const r = g.children[0] // Assuming Line is the first child
const text = g.children[1] // Assuming Text is the second child
const r = g.children[0]
const text = g.children[1]

if (r instanceof Line2) {
const Line = r as Line2
Expand All @@ -139,26 +192,31 @@ export const SelectionDataNodes = memo(() => {
return
}

const { x: sx, y: sy, z: sz } = sourceNode
const { x: tx, y: ty, z: tz } = targetNode
const { x: sx, y: sy } = sourceNode
const { x: tx, y: ty } = targetNode

// Set positions for the link
linksPositionRef.current.set(link.ref_id, {
sx,
sy,
sz,
tx,
ty,
tz,
sz: 0,
tz: 0,
})

const midPoint = new Vector3((sx + tx) / 2, (sy + ty) / 2, (sz + tz) / 2)
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
}

// Position the text
text.position.set(midPoint.x, midPoint.y, midPoint.z)
text.rotation.set(0, 0, angle)

// Set the line positions
Line.geometry.setPositions([sx, sy, sz, tx, ty, tz])
Line.geometry.setPositions([sx, sy, 0, tx, ty, 0])

const { material } = Line

Expand Down Expand Up @@ -191,18 +249,37 @@ export const SelectionDataNodes = memo(() => {
)

return (
<group ref={groupRef} name="simulation-2d-group">
{selectionGraphData?.nodes.map((node) => (
<mesh key={node.ref_id}>
<Html center sprite zIndexRange={[0, 0]}>
<Node node={node} onClick={() => handleSelect(node)} selected={node.ref_id === selectedNode?.ref_id} />
</Html>

<mesh />
</mesh>
))}
<Connections linksPosition={linksPositionRef.current} />
</group>
<>
<group ref={groupRef} name="simulation-2d-group">
{selectionGraphData?.nodes.map((node) => (
<mesh key={node.ref_id}>
<Html center sprite zIndexRange={[0, 0]}>
<GraphNode
node={node}
onClick={() => handleSelect(node)}
selected={node.ref_id === selectedNode?.ref_id}
/>
</Html>
</mesh>
))}
<Connections linksPosition={linksPositionRef.current} />
</group>
{false && (
<group>
{pathNodes.map((node) => (
<mesh key={node.ref_id} position={[node.fx || 0, node.fy || 0, node.fz || 0]}>
<Html center sprite zIndexRange={[0, 0]}>
<GraphNode
node={node}
onClick={() => handleSelect(node)}
selected={node.ref_id === selectedNode?.ref_id}
/>
</Html>
</mesh>
))}
</group>
)}
</>
)
})

Expand Down
Loading
Loading