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

feat: improved perfomance, decreased rerenders, added color support f… #2393

Merged
merged 2 commits into from
Oct 28, 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
64 changes: 33 additions & 31 deletions src/components/Universe/Graph/Connections/LineComponent.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,44 @@
import { Line } from '@react-three/drei'
import gsap from 'gsap'
import { forwardRef, useEffect } from 'react'
import { memo, useEffect, useRef } from 'react'
import { Color, Vector3 } from 'three'
import { Line2 } from 'three-stdlib'

type LineComponentProps = {
source: Vector3
target: Vector3
isSelected: boolean
lineWidth: number
visible: boolean
}

const LineComponent = forwardRef<Line2, LineComponentProps>(
({ source, target, isSelected, lineWidth, visible }, ref) => {
useEffect(() => {
const line = (ref as React.MutableRefObject<Line2 | null>).current

if (line) {
gsap.fromTo(
line.material,
{ linewidth: 5 },
{
linewidth: isSelected ? 2 : lineWidth,
duration: 1,
},
)
}
}, [isSelected, lineWidth, ref])

const color = new Color(0xff0000)

return (
<Line ref={ref} color={color} isLine2 lineWidth={2} opacity={0.5} points={[source, target]} visible={visible} />
)
},
)

LineComponent.displayName = 'LineComponent'

export default LineComponent
const VECTOR = new Vector3(0, 0, 0)

// eslint-disable-next-line no-underscore-dangle
export const _LineComponent = (props: LineComponentProps) => {
const { isSelected, lineWidth, visible } = props
const ref = useRef<Line2>(null)

useEffect(() => {
const line = (ref as React.MutableRefObject<Line2 | null>).current

if (line) {
gsap.fromTo(
line.material,
{ linewidth: 5 },
{
linewidth: isSelected ? 2 : lineWidth,
duration: 1,
},
)
}
}, [isSelected, lineWidth, ref])

const color = new Color(0xff0000)

return (
<Line ref={ref} color={color} isLine2 lineWidth={2} opacity={0.5} points={[VECTOR, VECTOR]} visible={visible} />
)
}

_LineComponent.displayName = 'LineComponent'

export const LineComponent = memo(_LineComponent)
41 changes: 6 additions & 35 deletions src/components/Universe/Graph/Connections/index.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,25 @@
import { memo, useRef } from 'react'
import { Vector3 } from 'three'
import { Line2 } from 'three-stdlib'
import { memo } from 'react'
import { useDataStore } from '~/stores/useDataStore'
import { useGraphStore, useSelectedNode } from '~/stores/useGraphStore'
import { Link } from '~/types'
import { LinkPosition } from '..'
import LineComponent from './LineComponent'
import { LineComponent } from './LineComponent'

type Props = {
linksPositions: LinkPosition[]
}

export const Connections = memo(({ linksPositions }: Props) => {
export const Connections = memo(() => {
const data = useDataStore((s) => s.dataInitial)
const { showSelectionGraph } = useGraphStore((s) => s)
const selectedNode = useSelectedNode()

const lineRefs = useRef<Line2[]>([])
console.log('connection')

Check warning on line 12 in src/components/Universe/Graph/Connections/index.tsx

View workflow job for this annotation

GitHub Actions / eslint-run

Unexpected console statement

return (
<group name="simulation-3d-group__connections">
{data?.links.map((l: Link, index) => {
const source = new Vector3(
linksPositions[index]?.sx || 0,
linksPositions[index]?.sy || 0,
linksPositions[index]?.sz || 0,
)

const target = new Vector3(
linksPositions[index]?.tx || 0,
linksPositions[index]?.ty || 0,
linksPositions[index]?.tz || 0,
)

{data?.links.map((l: Link) => {
const isSelected = selectedNode?.ref_id === l.source || selectedNode?.ref_id === l.target

const lineWidth = selectedNode ? 0 : 0.5

return (
<LineComponent
key={l.ref_id}
ref={(el) => {
lineRefs.current[index] = el as Line2
}}
isSelected={isSelected}
lineWidth={lineWidth}
source={source}
target={target}
visible={!showSelectionGraph}
/>
<LineComponent key={l.ref_id} isSelected={isSelected} lineWidth={lineWidth} visible={!showSelectionGraph} />
)
})}
</group>
Expand Down
3 changes: 1 addition & 2 deletions src/components/Universe/Graph/Cubes/NodePoints/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const _NodePoints = () => {
const data = useDataStore((s) => s.dataInitial)
const { normalizedSchemasByType } = useSchemaStore((s) => s)
const nodeTypes = useNodeTypes()
const ringGeometry = useMemo(() => new TorusGeometry(30, 4, 16, 100), [])
const ringGeometry = useMemo(() => new TorusGeometry(30, 2, 16, 100), [])

return (
<>
Expand All @@ -53,7 +53,6 @@ const _NodePoints = () => {
limit={1000} // Optional: max amount of items (for calculating buffer size)
range={1000}
visible={!selectedNode}
// Optional: draw-range
>
<meshStandardMaterial />
{data?.nodes.map((node: NodeExtended) => {
Expand Down
19 changes: 15 additions & 4 deletions src/components/Universe/Graph/Cubes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const POINTER_IN_DELAY = 200
export const Cubes = memo(() => {
const selectedNode = useSelectedNode()
const hoveredNode = useHoveredNode()

const relativeIds = useSelectedNodeRelativeIds()
const { selectionGraphData, showSelectionGraph, setHoveredNode, setIsHovering } = useGraphStore((s) => s)

Expand Down Expand Up @@ -56,30 +57,40 @@ export const Cubes = memo(() => {
const onPointerOut = useCallback(
(e: ThreeEvent<PointerEvent>) => {
e.stopPropagation()
setIsHovering(false)
setHoveredNode(null)

if (hoverTimeoutRef.current) {
clearTimeout(hoverTimeoutRef.current)
hoverTimeoutRef.current = null
}

if (!hoveredNode) {
return
}

setIsHovering(false)
setHoveredNode(null)
},
[setIsHovering, setHoveredNode],
[setIsHovering, setHoveredNode, hoveredNode],
)

const onPointerIn = useCallback(
(e: ThreeEvent<PointerEvent>) => {
const objects = e.intersections.map((i) => i.object)
const object = objects[0]

if (hoverTimeoutRef.current) {
clearTimeout(hoverTimeoutRef.current)
hoverTimeoutRef.current = null
}

if (object?.userData?.ref_id) {
const node = object.userData as NodeExtended

if (!ignoreNodeEvent(node)) {
e.stopPropagation()
setIsHovering(true)

hoverTimeoutRef.current = setTimeout(() => {
setIsHovering(true)
setHoveredNode(node)
}, POINTER_IN_DELAY)
}
Expand Down
72 changes: 40 additions & 32 deletions src/components/Universe/Graph/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Box3, Color, Group, Sphere, Vector3 } from 'three'
import { Line2 } from 'three-stdlib'
import { useDataStore } from '~/stores/useDataStore'
import { useGraphStore } from '~/stores/useGraphStore'
import { useSchemaStore } from '~/stores/useSchemaStore'
import { NodeExtended } from '~/types'
import { Connections } from './Connections'
import { Cubes } from './Cubes'
Expand All @@ -25,6 +26,7 @@ export const Graph = () => {
const groupRef = useRef<Group>(null)
const cameraSettled = useRef<boolean>(false)
const linksPositionRef = useRef<LinkPosition[]>([])
const { normalizedSchemasByType } = useSchemaStore((s) => s)

const { setData, simulation, simulationCreate, simulationHelpers, graphStyle, setGraphRadius } = useGraphStore(
(s) => s,
Expand Down Expand Up @@ -88,13 +90,15 @@ export const Graph = () => {
const grPoints = groupRef.current.getObjectByName('simulation-3d-group__node-points') as Group
const grConnections = groupRef.current.getObjectByName('simulation-3d-group__connections') as Group

gr.children.forEach((mesh, index) => {
const simulationNode = simulation.nodes()[index]
if (gr) {
gr.children.forEach((mesh, index) => {
const simulationNode = simulation.nodes()[index]

if (simulationNode) {
mesh.position.set(simulationNode.x, simulationNode.y, simulationNode.z)
}
})
if (simulationNode) {
mesh.position.set(simulationNode.x, simulationNode.y, simulationNode.z)
}
})
}

if (grPoints) {
grPoints.children[0].children.forEach((mesh, index) => {
Expand All @@ -106,35 +110,39 @@ export const Graph = () => {
})
}

grConnections.children.forEach((r, i) => {
const link = dataInitial?.links[i]
const Line = r as Line2
if (grConnections) {
grConnections.children.forEach((r, i) => {
const link = dataInitial?.links[i]
const Line = r as Line2

if (link) {
const sourceNode = simulation.nodes().find((n: NodeExtended) => n.ref_id === link.source)
const targetNode = simulation.nodes().find((n: NodeExtended) => n.ref_id === link.target)
if (link) {
const sourceNode = simulation.nodes().find((n: NodeExtended) => n.ref_id === link.source)
const targetNode = simulation.nodes().find((n: NodeExtended) => n.ref_id === link.target)

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

linksPositionRef.current[i] = {
sx,
sy,
sz,
tx,
ty,
tz,
}
linksPositionRef.current[i] = {
sx,
sy,
sz,
tx,
ty,
tz,
}

const lineColor = normalizedSchemasByType[sourceNode.node_type]?.primary_color || 'white'

Line.geometry.setPositions([sx, sy, sz, tx, ty, tz])
Line.geometry.setPositions([sx, sy, sz, tx, ty, tz])

const { material } = Line
const { material } = Line

material.color = new Color('white')
material.transparent = true
material.opacity = 0.1
}
})
material.color = new Color(lineColor)
material.transparent = true
material.opacity = 0.2
}
})
}
}
})

Expand All @@ -153,7 +161,7 @@ export const Graph = () => {

cameraSettled.current = false
})
}, [dataInitial, simulation, setGraphRadius])
}, [dataInitial, simulation, setGraphRadius, normalizedSchemasByType])

if (!simulation) {
return null
Expand All @@ -162,11 +170,11 @@ export const Graph = () => {
return (
<group ref={groupRef}>
<Cubes />
<Earth />
{graphStyle === 'earth' && <Earth />}

{(isLoadingNew || isFetching) && <LoadingNodes />}

{graphStyle !== 'earth' && <Connections linksPositions={linksPositionRef.current} />}
{graphStyle !== 'earth' && <Connections />}
<NodeDetailsPanel />
</group>
)
Expand Down
25 changes: 25 additions & 0 deletions src/hooks/useTraceUpdate/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// @ts-nocheck
// @ts-ignore

import { useEffect, useRef } from 'react'

export function useTraceUpdate(props: { [s: string]: unknown } | ArrayLike<unknown>) {
const prev = useRef(props)

useEffect(() => {
const changedProps = Object.entries(props).reduce((ps, [k, v]) => {
if (prev.current[k] !== v) {
// eslint-disable-next-line no-param-reassign
ps[k] = [prev.current[k], v]
}

return ps
}, {})

if (Object.keys(changedProps).length > 0) {
console.log('Changed props:', changedProps)
}

prev.current = props
})
}
Loading