Skip to content

Commit

Permalink
feat: add search for graph
Browse files Browse the repository at this point in the history
  • Loading branch information
Rassl committed Jan 8, 2025
1 parent 13d9b81 commit b35b0d1
Show file tree
Hide file tree
Showing 12 changed files with 323 additions and 82 deletions.
64 changes: 38 additions & 26 deletions src/components/Universe/Graph/Connections/LineComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Billboard, Line, Text } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import gsap from 'gsap'
import { memo, useEffect, useRef } from 'react'
import { Group } from 'three'
import { Line2 } from 'three-stdlib'
import { useGraphStore } from '~/stores/useGraphStore'
import { LINE_WIDTH } from '../../constants'
Expand All @@ -22,6 +23,7 @@ type LineComponentProps = {
// eslint-disable-next-line no-underscore-dangle
const _LineComponent = (props: LineComponentProps) => {
const lineRef = useRef<Line2 | null>(null)
const groupRef = useRef<Group | null>(null)

const { label, source, target, sourceX, sourceY, sourceZ, targetX, targetY, targetZ } = props

Expand All @@ -38,42 +40,52 @@ const _LineComponent = (props: LineComponentProps) => {
},
)
}
}, [lineRef])
}, [lineRef, sourceX])

useFrame(() => {
if (!lineRef.current || !groupRef.current) {
return
}

// @todo-useframe
const { selectedNode, hoveredNode } = useGraphStore.getState()
const { hoveredNode } = useGraphStore.getState()

if (lineRef.current) {
const line = lineRef.current
const activeNode = selectedNode || hoveredNode

line.visible = !activeNode

if (activeNode?.ref_id === source || activeNode?.ref_id === target) {
line.visible = true

// Increase line width
gsap.to(line.material, {
linewidth: 6, // Target line width
duration: 0.5, // Smooth increase
ease: 'power1.out',
})
} else {
// Decrease line width back to default
gsap.to(line.material, {
linewidth: 1, // Default line width
duration: 0.5, // Smooth decrease
ease: 'power1.out',
})
}
const line = lineRef.current
const activeNode = hoveredNode

if (!activeNode) {
groupRef.current.visible = false

return
}

line.visible = !activeNode

if (activeNode?.ref_id === source || activeNode?.ref_id === target) {
line.visible = true
groupRef.current.visible = true

// Increase line width
gsap.to(line.material, {
linewidth: 6, // Target line width
duration: 0.5, // Smooth increase
ease: 'power1.out',
})
} else {
// Decrease line width back to default
gsap.to(line.material, {
linewidth: 1, // Default line width
duration: 0.5, // Smooth decrease
ease: 'power1.out',
})
}
})

return (
<group>
<group ref={groupRef}>
<Line
ref={lineRef}
color="white"
isLine2
lineWidth={2}
name="line"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// LineInstance.tsx
import { Segment as DreiSegment, SegmentObject } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import { memo, useRef } from 'react'
import { Vector3 } from 'three'

type LineComponentProps = {
sourceX: number
sourceY: number
sourceZ: number
targetX: number
targetY: number
targetZ: number
color: string
}

const vec = new Vector3(0, 0, 0)

export const LineInstance = memo((props: LineComponentProps) => {
const { sourceX, sourceY, sourceZ, targetX, targetY, targetZ, color } = props

const ref = useRef<SegmentObject | null>(null)

useFrame(() => {
if (ref.current) {
ref.current.start.set(sourceX || 0, sourceY || 0, sourceZ || 0)
ref.current.end.set(targetX, targetY, targetZ)
}
})

return (
<>
<DreiSegment ref={ref} color={color} end={vec} start={vec} />
</>
)
})

LineInstance.displayName = 'LineInstance'
97 changes: 68 additions & 29 deletions src/components/Universe/Graph/Connections/index.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,85 @@
import { Segments } from '@react-three/drei'
import { memo } from 'react'
import { useDataStore } from '~/stores/useDataStore'
import { useGraphStore } from '~/stores/useGraphStore'
import { useSchemaStore } from '~/stores/useSchemaStore'
import { Link } from '~/types'
import { LinkPosition } from '..'
import { LineComponent } from './LineComponent'
import { LineInstance } from './LineInstance.tsx'

type Props = {
linksPosition: Map<string, LinkPosition>
}

export const Connections = memo(({ linksPosition }: Props) => {
const data = useDataStore((s) => s.dataInitial)
const { showSelectionGraph } = useGraphStore((s) => s)
const [dataInitial, nodesNormalized] = useDataStore((s) => [s.dataInitial, s.nodesNormalized])
const [showSelectionGraph, searchQuery] = useGraphStore((s) => [s.showSelectionGraph, s.searchQuery])
const normalizedSchemasByType = useSchemaStore((s) => s.normalizedSchemasByType)

return (
<group name="simulation-3d-group__connections" visible={!showSelectionGraph}>
{data?.links.map((l: Link) => {
const position = linksPosition.get(l.ref_id) || {
sx: 0,
sy: 0,
sz: 0,
tx: 0,
ty: 0,
tz: 0,
}

return (
<LineComponent
key={l.ref_id}
label={l.edge_type}
source={l.source}
sourceX={position.sx}
sourceY={position.sy}
sourceZ={position.sz}
target={l.target}
targetX={position.tx}
targetY={position.ty}
targetZ={position.tz}
/>
)
})}
</group>
<>
<group name="simulation-3d-group__connections" visible={!showSelectionGraph}>
{dataInitial?.links.map((l: Link) => {
const position = linksPosition.get(l.ref_id) || {
sx: 0,
sy: 0,
sz: 0,
tx: 0,
ty: 0,
tz: 0,
}

return (
<LineComponent
key={l.ref_id}
label={l.edge_type}
source={l.source}
sourceX={position.sx}
sourceY={position.sy}
sourceZ={position.sz}
target={l.target}
targetX={position.tx}
targetY={position.ty}
targetZ={position.tz}
/>
)
})}
</group>
<group visible={!searchQuery}>
<Segments limit={1000} lineWidth={0.05}>
{dataInitial?.links.map((l: Link) => {
const position = linksPosition.get(l.ref_id) || {
sx: 0,
sy: 0,
sz: 0,
tx: 0,
ty: 0,
tz: 0,
}

const sourceNode = nodesNormalized.get(l.source) || ''

const color = sourceNode ? normalizedSchemasByType[sourceNode.node_type]?.primary_color : 'white'

const linkColor = color || 'white'

return (
<LineInstance
key={l.ref_id}
color={linkColor}
sourceX={position.sx}
sourceY={position.sy}
sourceZ={position.sz}
targetX={position.tx}
targetY={position.ty}
targetZ={position.tz}
/>
)
})}
</Segments>
</group>
</>
)
})

Expand Down
37 changes: 32 additions & 5 deletions src/components/Universe/Graph/Cubes/NodePoints/Point/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,39 @@
import { Billboard, Instance } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import { memo, useRef } from 'react'
import { Group } from 'three'
import { useGraphStore } from '~/stores/useGraphStore'

type Props = {
color: string
scale: number
name: string
}

export const Point = ({ color, scale }: Props) => (
<Billboard follow lockX={false} lockY={false} lockZ={false}>
<Instance color={color} scale={scale} />
</Billboard>
)
export const Point = memo(({ color, scale, name }: Props) => {
const nodeRef = useRef<Group | null>(null)

useFrame(() => {
const { searchQuery } = useGraphStore.getState()

if (!nodeRef.current) {
return
}

if (searchQuery) {
const dynamicScale = name.toLowerCase().includes(searchQuery.toLowerCase()) ? 1 : 0.1

nodeRef.current.scale.set(dynamicScale, dynamicScale, dynamicScale)
} else {
nodeRef.current.scale.set(1, 1, 1)
}
})

return (
<Billboard ref={nodeRef} follow lockX={false} lockY={false} lockZ={false}>
<Instance color={color} scale={scale} />
</Billboard>
)
})

Point.displayName = 'Point'
15 changes: 10 additions & 5 deletions src/components/Universe/Graph/Cubes/NodePoints/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Instances } from '@react-three/drei'
import { memo, useMemo } from 'react'
import { BufferGeometry, TorusGeometry } from 'three'
import { useDataStore, useNodeTypes } from '~/stores/useDataStore'
import { useSelectedNode } from '~/stores/useGraphStore'
import { useNodeTypes } from '~/stores/useDataStore'
import { useGraphStore, useSelectedNode } from '~/stores/useGraphStore'
import { useSchemaStore } from '~/stores/useSchemaStore'
import { NodeExtended } from '~/types'
import { colors } from '~/utils'
Expand Down Expand Up @@ -41,10 +41,11 @@ const COLORS_MAP = [
// eslint-disable-next-line no-underscore-dangle
const _NodePoints = () => {
const selectedNode = useSelectedNode()
const data = useDataStore((s) => s.dataInitial)
const { normalizedSchemasByType } = useSchemaStore((s) => s)
const [simulation] = useGraphStore((s) => [s.simulation])
const nodeTypes = useNodeTypes()
const ringGeometry = useMemo(() => new TorusGeometry(30, 2, 16, 100), [])
const { getNodeKeysByType } = useSchemaStore((s) => s)

return (
<>
Expand All @@ -55,11 +56,15 @@ const _NodePoints = () => {
visible={!selectedNode || true}
>
<meshBasicMaterial />
{data?.nodes.map((node: NodeExtended) => {
{simulation?.nodes().map((node: NodeExtended) => {
const primaryColor = normalizedSchemasByType[node.node_type]?.primary_color
const color = primaryColor ?? (COLORS_MAP[nodeTypes.indexOf(node.node_type)] || colors.white)
const scale = node.fx === undefined ? 0 : node.scale || 1
const keyProperty = getNodeKeysByType(node.node_type) || ''

return <Point key={node.ref_id} color={color} scale={node.scale || 1} />
const name = keyProperty && node?.properties ? node?.properties[keyProperty] || '' : ''

return <Point key={node.ref_id} color={color} name={name} scale={scale} />
})}
</Instances>
</>
Expand Down
26 changes: 18 additions & 8 deletions src/components/Universe/Graph/Cubes/Text/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,17 @@ export const TextNode = memo(

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

const keyProperty = getNodeKeysByType(node.node_type) || ''

const sanitizedNodeName =
keyProperty && node?.properties ? removeEmojis(String(node?.properties[keyProperty] || '')) : ''

useFrame(({ camera, clock }) => {
const { selectedNode, hoveredNode, activeEdge } = useGraphStore.getState()
if (!nodeRef.current) {
return
}

const { selectedNode, hoveredNode, activeEdge, searchQuery } = useGraphStore.getState()

const checkDistance = () => {
const nodePosition = nodePositionRef.current.setFromMatrixPosition(nodeRef.current!.matrixWorld)
Expand All @@ -67,11 +76,18 @@ export const TextNode = memo(
// Set visibility based on distance
}

if (searchQuery.length < 3) {
checkDistance()
} else {
nodeRef.current.visible = false
}

const isActive =
node.ref_id === selectedNode?.ref_id ||
node.ref_id === hoveredNode?.ref_id ||
activeEdge?.target === node.ref_id ||
activeEdge?.source === node.ref_id
activeEdge?.source === node.ref_id ||
(searchQuery && sanitizedNodeName.toLowerCase().includes(searchQuery.toLowerCase()))

if (isActive) {
if (nodeRef.current && !nodeRef.current.visible) {
Expand Down Expand Up @@ -103,8 +119,6 @@ export const TextNode = memo(
if (circleRef.current?.visible) {
circleRef.current.visible = false
}

checkDistance()
})

const primaryColor = normalizedSchemasByType[node.node_type]?.primary_color
Expand All @@ -114,10 +128,6 @@ export const TextNode = memo(

const Icon = primaryIcon ? Icons[primaryIcon] : null
const iconName = Icon ? primaryIcon : 'NodesIcon'
const keyProperty = getNodeKeysByType(node.node_type) || ''

const sanitizedNodeName =
keyProperty && node?.properties ? removeEmojis(String(node?.properties[keyProperty] || '')) : ''

const uniforms = {
u_texture: { value: texture },
Expand Down
Loading

0 comments on commit b35b0d1

Please sign in to comment.