Skip to content

Commit

Permalink
Merge pull request #2428 from stakwork/feature/image-for-persons
Browse files Browse the repository at this point in the history
Feature/image for persons
  • Loading branch information
Rassl authored Nov 1, 2024
2 parents ce1e3dd + 9a248e5 commit 4f3fbe3
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 30 deletions.
13 changes: 12 additions & 1 deletion src/components/App/SideBar/Relevance/Episode/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Flex } from '~/components/common/Flex'
import { highlightSearchTerm } from '~/components/common/Highlight/Highlight'
import { Text } from '~/components/common/Text'
import { useAppStore } from '~/stores/useAppStore'
import { useGraphStore } from '~/stores/useGraphStore'
import { NodeExtended } from '~/types'
import { colors } from '~/utils/colors'
import { Default } from './Default'
Expand Down Expand Up @@ -70,6 +71,7 @@ export const Episode = ({
node,
}: Props) => {
const searchTerm = useAppStore((s) => s.currentSearch)
const { setHoveredNode } = useGraphStore((s) => s)
const text = highlightSearchTerm(String(newText), searchTerm) as string
const name = highlightSearchTerm(String(newName), searchTerm) as string
const subtitleSource = type === 'show' ? '' : showTitle
Expand All @@ -78,7 +80,16 @@ export const Episode = ({
const defaultViewTypes = ['Tweet', 'person', 'guest', 'topic', 'document']

return (
<EpisodeWrapper className={className} onClick={onClick}>
<EpisodeWrapper
className={className}
onClick={onClick}
onMouseLeave={() => {
setHoveredNode(null)
}}
onMouseOver={() => {
setHoveredNode(node)
}}
>
{!defaultViewTypes.includes(type) && (
<Default
boostCount={boostCount}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,5 +66,5 @@ export const useMaterial = (url: string, transparent: boolean) => {
[texture, material],
)

return material
return { material, texture }
}
2 changes: 1 addition & 1 deletion src/components/Universe/Graph/Cubes/Cube/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const Cube = memo(({ node, hide, animated }: Props) => {
const selectedNode = useSelectedNode()
const { showSelectionGraph } = useGraphStore((s) => s)
const isSelected = !!selectedNode && node.ref_id === selectedNode.ref_id
const material = useMaterial(node.image_url || 'noimage.jpeg', false)
const { material } = useMaterial(node.image_url || 'noimage.jpeg', false)

useFrame((_, delta) => {
if (animated && ref.current) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ type Props = {
}

export const Point = ({ color, scale }: Props) => (
<>
<Billboard follow lockX={false} lockY={false} lockZ={false}>
<Instance color={color} scale={scale} />
</Billboard>
</>
<Billboard follow lockX={false} lockY={false} lockZ={false}>
<Instance color={color} scale={scale} />
</Billboard>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { MeshStandardMaterial, TextureLoader } from 'three'
import { smoothness } from '../../../Cube/constants'

export const loader = new TextureLoader()

export const noImageTexture = loader.load('noimage.jpeg')

export const noImageMaterial = new MeshStandardMaterial({
...smoothness,
map: noImageTexture,
})

export const transparentValue = 0.4

export const noImageTransparentMaterial = new MeshStandardMaterial({
...smoothness,
map: noImageTexture,
transparent: true,
opacity: transparentValue,
})
53 changes: 53 additions & 0 deletions src/components/Universe/Graph/Cubes/Text/hooks/useTexture/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { useEffect, useState } from 'react'
import { Texture } from 'three'
import { loader } from './constants'

type materialRecord = {
texture: THREE.Texture
material: THREE.MeshStandardMaterial
}

const cachedMaterials: Record<string, materialRecord> = {}

export const useTexture = (url: string) => {
const [texture, setTexture] = useState<Texture | null>(null)

useEffect(() => {
if (!url) {
setTexture(null)

return
}

const cashPath = url

if (cachedMaterials[cashPath]) {
setTexture(cachedMaterials[cashPath].texture)

return
}

loader.load(
url,
(loadedTexture) => {
setTexture(loadedTexture)
},
undefined,
() => {
setTexture(null)
},
)
}, [url])

useEffect(
() =>
function cleanup() {
if (texture) {
texture.dispose()
}
},
[texture],
)

return { texture }
}
99 changes: 79 additions & 20 deletions src/components/Universe/Graph/Cubes/Text/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Billboard, Svg, Text } from '@react-three/drei'
import { Billboard, Plane, Svg, Text } from '@react-three/drei'
import { useFrame } from '@react-three/fiber'
import { memo, useMemo, useRef } from 'react'
import { Mesh, MeshBasicMaterial, Vector3 } from 'three'
Expand All @@ -11,6 +11,7 @@ import { colors } from '~/utils/colors'
import { removeEmojis } from '~/utils/removeEmojisFromText'
import { truncateText } from '~/utils/truncateText'
import { fontProps } from './constants'
import { useTexture } from './hooks/useTexture'

const COLORS_MAP = [
'#fff',
Expand Down Expand Up @@ -69,17 +70,20 @@ function splitStringIntoThreeParts(text: string): string {
export const TextNode = memo(({ node, hide, isHovered }: Props) => {
const svgRef = useRef<Mesh | null>(null)
const ringRef = useRef<Mesh | null>(null)
const circleRef = useRef<Mesh | null>(null)
const selectedNode = useSelectedNode()

const nodePositionRef = useRef(new Vector3())

const { texture } = useTexture(node.properties?.image_url || '')

const selectedNodeRelativeIds = useSelectedNodeRelativeIds()
const isRelative = selectedNodeRelativeIds.includes(node?.ref_id || '')
const isSelected = !!selectedNode && selectedNode?.ref_id === node.ref_id
const showSelectionGraph = useGraphStore((s) => s.showSelectionGraph)
const { normalizedSchemasByType } = useSchemaStore((s) => s)

useFrame(({ camera }) => {
useFrame(({ camera, clock }) => {
const checkDistance = () => {
const nodePosition = nodePositionRef.current.setFromMatrixPosition(ringRef.current!.matrixWorld)

Expand All @@ -90,6 +94,20 @@ export const TextNode = memo(({ node, hide, isHovered }: Props) => {
// Set visibility based on distance
}

if (isHovered) {
if (ringRef.current) {
ringRef.current.visible = true
}

const scale = 1 + 0.2 * Math.sin(clock.getElapsedTime() * 2) // Adjust frequency and amplitude

if (circleRef.current) {
circleRef.current.scale.set(scale, scale, scale)
}

return
}

checkDistance()
})

Expand Down Expand Up @@ -134,27 +152,68 @@ export const TextNode = memo(({ node, hide, isHovered }: Props) => {
const iconName = Icon ? primaryIcon : 'NodesIcon'
const sanitizedNodeName = removeEmojis(String(node.name))

const uniforms = {
u_texture: { value: texture },
u_radius: { value: 0.5 }, // Radius of the circular mask
}

return (
<Billboard follow lockX={false} lockY={false} lockZ={false} name="billboard" userData={node}>
<mesh ref={ringRef} name={node.id} userData={node} visible={!hide}>
<Svg
ref={svgRef}
name="svg"
onUpdate={(svg) => {
svg.traverse((child) => {
if (child instanceof Mesh) {
// Apply dynamic color to meshes
// eslint-disable-next-line no-param-reassign
child.material = new MeshBasicMaterial({ color })
}
})
}}
position={[-15, 15, 0]}
scale={2}
src={`svg-icons/${iconName}.svg`}
strokeMaterial={{ color: 'yellow' }}
userData={node}
/>
{isHovered ? (
<mesh ref={circleRef} position={[0, 0, -2]}>
<circleGeometry args={[30, 32]} />
<meshBasicMaterial color={color} opacity={0.5} transparent />
</mesh>
) : null}
{node.properties?.image_url && node.node_type === 'Person' && texture ? (
<Plane args={[10 * 2, 10 * 2]} scale={2}>
<shaderMaterial
fragmentShader={`
uniform sampler2D u_texture;
uniform float u_radius;
varying vec2 vUv;
void main() {
vec2 center = vec2(0.5, 0.5); // Center of the circle
float dist = distance(vUv, center);
if (dist < u_radius) {
gl_FragColor = texture2D(u_texture, vUv);
} else {
discard; // Discard pixels outside the circle
}
}
`}
uniforms={uniforms}
vertexShader={`
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`}
/>
</Plane>
) : (
<Svg
ref={svgRef}
name="svg"
onUpdate={(svg) => {
svg.traverse((child) => {
if (child instanceof Mesh) {
// Apply dynamic color to meshes
// eslint-disable-next-line no-param-reassign
child.material = new MeshBasicMaterial({ color })
}
})
}}
position={[-15, 15, 0]}
scale={2}
src={`svg-icons/${iconName}.svg`}
strokeMaterial={{ color: 'yellow' }}
userData={node}
/>
)}

{node.name && (
<Text
Expand Down
2 changes: 1 addition & 1 deletion src/components/Universe/Graph/Cubes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const Cubes = memo(() => {
<TextNode
key={node.ref_id || node.id}
hide={hideUniverse || hide}
isHovered={!!hoveredNode && hoveredNode.id === node.ref_id}
isHovered={!!hoveredNode && hoveredNode.ref_id === node.ref_id}
node={node}
/>
</mesh>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Universe/Graph/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export const Graph = () => {

material.color = new Color(lineColor)
material.transparent = true
material.opacity = 0.5
material.opacity = 0.3
}
})
}
Expand Down

0 comments on commit 4f3fbe3

Please sign in to comment.