From ee7573ceef80d10c1d342ca23965dd4a3509b2fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Thu, 24 Oct 2024 00:49:40 +0300 Subject: [PATCH 1/4] feat: updated layout --- .../Body/Graph/Nodes/Node/index.tsx | 2 +- .../Graph/Connections/LineComponent.tsx | 15 +-- .../Universe/Graph/Cubes/Text/constants.ts | 2 +- .../Universe/Graph/Cubes/Text/index.tsx | 114 ++++++++++-------- src/components/Universe/Graph/Cubes/index.tsx | 9 +- src/components/Universe/Graph/index.tsx | 2 - src/components/Universe/index.tsx | 65 ++++++---- 7 files changed, 115 insertions(+), 94 deletions(-) diff --git a/src/components/ModalsContainer/BlueprintModal/Body/Graph/Nodes/Node/index.tsx b/src/components/ModalsContainer/BlueprintModal/Body/Graph/Nodes/Node/index.tsx index 538b2e9fd..276ee661e 100644 --- a/src/components/ModalsContainer/BlueprintModal/Body/Graph/Nodes/Node/index.tsx +++ b/src/components/ModalsContainer/BlueprintModal/Body/Graph/Nodes/Node/index.tsx @@ -6,9 +6,9 @@ import styled from 'styled-components' import { BoxGeometry, Mesh, Vector3 } from 'three' import { SchemaExtended } from '~/components/ModalsContainer/BlueprintModal/types' import { fontProps } from '~/components/Universe/Graph/Cubes/Text/constants' +import { useSchemaStore } from '~/stores/useSchemaStore' import { truncateText } from '~/utils/truncateText' import { NODE_RADIUS } from '../../constants' -import { useSchemaStore } from '~/stores/useSchemaStore' export const NODE_TYPE_COLORS = ['#ff13c9', '#5af0ff', '#3233ff', '#c2f0c2', '#ff6666', '#99ccff', '#ffb3b3'] diff --git a/src/components/Universe/Graph/Connections/LineComponent.tsx b/src/components/Universe/Graph/Connections/LineComponent.tsx index 027ff1da7..9de348843 100644 --- a/src/components/Universe/Graph/Connections/LineComponent.tsx +++ b/src/components/Universe/Graph/Connections/LineComponent.tsx @@ -1,7 +1,7 @@ import { Line } from '@react-three/drei' import gsap from 'gsap' import { forwardRef, useEffect } from 'react' -import { Vector3 } from 'three' +import { Color, Vector3 } from 'three' import { Line2 } from 'three-stdlib' type LineComponentProps = { @@ -29,17 +29,10 @@ const LineComponent = forwardRef( } }, [isSelected, lineWidth, ref]) + const color = new Color(0xff0000) + return ( - + ) }, ) diff --git a/src/components/Universe/Graph/Cubes/Text/constants.ts b/src/components/Universe/Graph/Cubes/Text/constants.ts index 10a33e75b..5a21f8bd4 100644 --- a/src/components/Universe/Graph/Cubes/Text/constants.ts +++ b/src/components/Universe/Graph/Cubes/Text/constants.ts @@ -1,7 +1,7 @@ export const fontProps = { font: '/fonts/Inter-Bold.woff', characters: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!', - fontSize: 2, + fontSize: 1, letterSpacing: -0.05, lineHeight: 1, 'material-toneMapped': false, diff --git a/src/components/Universe/Graph/Cubes/Text/index.tsx b/src/components/Universe/Graph/Cubes/Text/index.tsx index 2574a3476..05e70d8b2 100644 --- a/src/components/Universe/Graph/Cubes/Text/index.tsx +++ b/src/components/Universe/Graph/Cubes/Text/index.tsx @@ -1,8 +1,9 @@ -import { Svg, Text } from '@react-three/drei' +import { Billboard, Point, Svg, Text } from '@react-three/drei' import { useFrame } from '@react-three/fiber' import { Select } from '@react-three/postprocessing' +import { throttle } from 'lodash' import { memo, useMemo, useRef } from 'react' -import { Mesh } from 'three' +import { Mesh, MeshStandardMaterial, TorusGeometry, Vector3 } from 'three' import { Icons } from '~/components/Icons' import { useNodeTypes } from '~/stores/useDataStore' import { useGraphStore, useHoveredNode, useSelectedNode, useSelectedNodeRelativeIds } from '~/stores/useGraphStore' @@ -11,7 +12,6 @@ import { NodeExtended } from '~/types' import { colors } from '~/utils/colors' import { removeEmojis } from '~/utils/removeEmojisFromText' import { truncateText } from '~/utils/truncateText' -import { smoothness } from '../Cube/constants' import { fontProps } from './constants' const COLORS_MAP = [ @@ -50,32 +50,27 @@ type Props = { } function splitStringIntoThreeParts(text: string): string { - // Split the string into an array of words - const truncatedText = truncateText(text, 30) const words = truncatedText.split(' ') - // If the word count is 5 or less, return the original text - if (text.split(' ').length <= 5) { + if (words.length <= 5) { return truncatedText } - // Determine the split points const third = Math.ceil(words.length / 3) const twoThirds = third * 2 - // Split the array into three parts const firstPart = words.slice(0, third).join(' ') const secondPart = words.slice(third, twoThirds).join(' ') const thirdPart = words.slice(twoThirds).join(' ') - // Return the three parts as a single string with newline characters in between return `${firstPart}\n${secondPart}\n${thirdPart}` } export const TextNode = memo(({ node, hide }: Props) => { const ref = useRef(null) const svgRef = useRef(null) + const ringRef = useRef(null) const selectedNode = useSelectedNode() const hoveredNode = useHoveredNode() @@ -86,20 +81,24 @@ export const TextNode = memo(({ node, hide }: Props) => { const showSelectionGraph = useGraphStore((s) => s.showSelectionGraph) const { normalizedSchemasByType } = useSchemaStore((s) => s) - const nodeTypes = useNodeTypes() - useFrame(({ camera }) => { - if (ref?.current) { - // Make text face the camera - ref.current.quaternion.copy(camera.quaternion) - } + const checkDistance = throttle(() => { + const nodePosition = new Vector3().setFromMatrixPosition(ref.current!.matrixWorld) + const distance = nodePosition.distanceTo(camera.position) + const isLess = distance < 5000 - if (svgRef?.current) { - // Make text face the camera - svgRef.current.quaternion.copy(camera.quaternion) - } + if (ringRef.current) { + ringRef.current.visible = isLess + } + + // Set visibility based on distance + }, 1500) // Throttle checks to run only every 500ms + + checkDistance() }) + const nodeTypes = useNodeTypes() + const textScale = useMemo(() => { let scale = (node.edge_count || 1) * 20 @@ -111,9 +110,7 @@ export const TextNode = memo(({ node, hide }: Props) => { const nodeScale = scale / Math.sqrt(node.name.length) - scale = Math.max(nodeScale, 20) - - return Math.min(scale, 30) + return Math.min(Math.max(nodeScale, 20), 30) }, [node.edge_count, node.name, isSelected, isRelative, showSelectionGraph]) const fillOpacity = useMemo(() => { @@ -134,36 +131,57 @@ export const TextNode = memo(({ node, hide }: Props) => { const color = primaryColor ?? (COLORS_MAP[nodeTypes.indexOf(node.node_type)] || colors.white) const Icon = primaryIcon ? Icons[primaryIcon] : null - - const iconName = Icon ? primaryIcon : 'AddCircleIcon' - + const iconName = Icon ? primaryIcon : 'NodesIcon' const sanitizedNodeName = removeEmojis(String(node.name)) + const ringGeometry = useMemo(() => new TorusGeometry(30, 4, 16, 100), []) + const ringMaterial = useMemo(() => new MeshStandardMaterial({ color }), [color]) + return ( <> - {!Icon ? ( - - {splitStringIntoThreeParts(sanitizedNodeName)} - - ) : ( - + + {/* Ring geometry */} + + { + svg.traverse((child) => { + if (child instanceof Mesh) { + // Apply dynamic color to meshes + // eslint-disable-next-line no-param-reassign + child.material = new MeshStandardMaterial({ color }) + } + }) + }} + position={[-15, 15, 0]} + scale={2} + src={`svg-icons/${iconName}.svg`} + strokeMaterial={{ color: 'yellow' }} + /> + + + {splitStringIntoThreeParts(sanitizedNodeName)} + - - )} + + + ) }) diff --git a/src/components/Universe/Graph/Cubes/index.tsx b/src/components/Universe/Graph/Cubes/index.tsx index 6e221fd88..4c1b222a9 100644 --- a/src/components/Universe/Graph/Cubes/index.tsx +++ b/src/components/Universe/Graph/Cubes/index.tsx @@ -6,8 +6,6 @@ import { useAppStore } from '~/stores/useAppStore' import { useDataStore } from '~/stores/useDataStore' import { useGraphStore, useSelectedNode, useSelectedNodeRelativeIds } from '~/stores/useGraphStore' import { NodeExtended } from '~/types' -import { BlurryInstances } from './BlurryInstances' -import { Cube } from './Cube' import { RelevanceBadges } from './RelevanceBadges' import { SelectionDataNodes } from './SelectionDataNodes' import { TextNode } from './Text' @@ -97,7 +95,6 @@ export const Cubes = memo(() => { onPointerOut={onPointerOut} onPointerOver={onPointerIn} > - {false && } {data?.nodes.map((node: NodeExtended) => { @@ -105,11 +102,7 @@ export const Cubes = memo(() => { return ( - {node.name ? ( - - ) : ( - - )} + ) })} diff --git a/src/components/Universe/Graph/index.tsx b/src/components/Universe/Graph/index.tsx index d7bd5559b..b489c9178 100644 --- a/src/components/Universe/Graph/index.tsx +++ b/src/components/Universe/Graph/index.tsx @@ -9,7 +9,6 @@ import { Connections } from './Connections' import { Cubes } from './Cubes' import { Earth } from './Earth' import { LoadingNodes } from './LoadingNodes' -import { Particles } from './Particles' import { NodeDetailsPanel } from './UI' export type LinkPosition = { @@ -154,7 +153,6 @@ export const Graph = () => { - {false && } {(isLoadingNew || isFetching) && } {graphStyle !== 'earth' && } diff --git a/src/components/Universe/index.tsx b/src/components/Universe/index.tsx index 0936ff25f..2c4cb84c4 100644 --- a/src/components/Universe/index.tsx +++ b/src/components/Universe/index.tsx @@ -1,12 +1,12 @@ /* eslint-disable no-underscore-dangle */ /* eslint-disable import/no-extraneous-dependencies */ -import { AdaptiveDpr, AdaptiveEvents, Html, Loader, Preload } from '@react-three/drei' +import { AdaptiveDpr, AdaptiveEvents, Html, Loader, PerformanceMonitor, Preload } from '@react-three/drei' import { Canvas, RootState } from '@react-three/fiber' import { Bloom, EffectComposer, Outline, Selection, Vignette } from '@react-three/postprocessing' import { useControls } from 'leva' import { BlendFunction, Resolution } from 'postprocessing' import { Perf } from 'r3f-perf' -import { Suspense, memo, useCallback, useMemo } from 'react' +import { Suspense, memo, useCallback, useMemo, useState } from 'react' import styled from 'styled-components' import { getNodeColorByType } from '~/components/Universe/Graph/constant' import { isDevelopment } from '~/constants' @@ -53,26 +53,28 @@ const Content = () => { - - - - - - - + {false && ( + + + + + + + )} + ƒ @@ -95,6 +97,10 @@ const _Universe = () => { useControlStore((s) => s.setUserMovedCamera), ] + const [dpr, setDpr] = useState(2) + + console.log(dpr) + const isLoading = useDataStore((s) => s.isFetching) const universeQuestionIsOpen = useAppStore((s) => s.universeQuestionIsOpen) @@ -130,8 +136,21 @@ const _Universe = () => { return ( - + {isDevelopment && } + { + console.log(factor) + setDpr(Math.floor(0.5 + 1.5 * factor, 1)) + }} + /> }> From ad077a291083d9df262be20891a9f672fd7ae5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Thu, 24 Oct 2024 12:51:19 +0300 Subject: [PATCH 2/4] feat: added ghost shapes --- .../Graph/Cubes/NodePoints/Point/index.tsx | 41 +++++++++ .../Universe/Graph/Cubes/NodePoints/index.tsx | 71 ++++++++++++++++ .../Universe/Graph/Cubes/Text/index.tsx | 84 +++++++++---------- src/components/Universe/Graph/Cubes/index.tsx | 4 + src/components/Universe/Graph/index.tsx | 9 ++ 5 files changed, 165 insertions(+), 44 deletions(-) create mode 100644 src/components/Universe/Graph/Cubes/NodePoints/Point/index.tsx create mode 100644 src/components/Universe/Graph/Cubes/NodePoints/index.tsx diff --git a/src/components/Universe/Graph/Cubes/NodePoints/Point/index.tsx b/src/components/Universe/Graph/Cubes/NodePoints/Point/index.tsx new file mode 100644 index 000000000..620dfc1d7 --- /dev/null +++ b/src/components/Universe/Graph/Cubes/NodePoints/Point/index.tsx @@ -0,0 +1,41 @@ +import { Instance } from '@react-three/drei' +import { useFrame } from '@react-three/fiber' +import { throttle } from 'lodash' +import { useRef } from 'react' +import { Mesh, MeshStandardMaterial, Vector3 } from 'three' + +type Props = { + color: string +} + +export const Point = ({ color }: Props) => { + const ref = useRef(null) + const instanceRef = useRef(null) + + useFrame(({ camera }) => { + if (!instanceRef.current || !ref.current) { + return + } + + const checkDistance = throttle(() => { + const nodePosition = new Vector3().setFromMatrixPosition(instanceRef.current!.matrixWorld) + const distance = nodePosition.distanceTo(camera.position) + const isLess = distance < 2500 + + if (ref.current) { + ref.current.visible = !isLess + } + + // Set visibility based on distance + }, 1500) // Throttle checks to run only every 500ms + + checkDistance() + }) + + return ( + <> + + + + ) +} diff --git a/src/components/Universe/Graph/Cubes/NodePoints/index.tsx b/src/components/Universe/Graph/Cubes/NodePoints/index.tsx new file mode 100644 index 000000000..21b5066dd --- /dev/null +++ b/src/components/Universe/Graph/Cubes/NodePoints/index.tsx @@ -0,0 +1,71 @@ +import { Instances } from '@react-three/drei' +import { memo } from 'react' +import { useDataStore, useNodeTypes } from '~/stores/useDataStore' +import { useSelectedNode, useSelectedNodeRelativeIds } from '~/stores/useGraphStore' +import { useSchemaStore } from '~/stores/useSchemaStore' +import { NodeExtended } from '~/types' +import { colors } from '~/utils' +import { Point } from './Point' + +const COLORS_MAP = [ + '#fff', + '#9747FF', + '#00887A', + '#0098A6', + '#0288D1', + '#33691E', + '#465A65', + '#512DA7', + '#5C6BC0', + '#5D4038', + '#662C00', + '#689F39', + '#6B1B00', + '#750000', + '#78909C', + '#7E57C2', + '#8C6E63', + '#AA47BC', + '#BF360C', + '#C2175B', + '#EC407A', + '#EF6C00', + '#F5511E', + '#FF9696', + '#FFC064', + '#FFCD29', + '#FFEA60', +] + +// eslint-disable-next-line no-underscore-dangle +const _NodePoints = () => { + const selectedNode = useSelectedNode() + const relativeIds = useSelectedNodeRelativeIds() + const data = useDataStore((s) => s.dataInitial) + const { normalizedSchemasByType } = useSchemaStore((s) => s) + const nodeTypes = useNodeTypes() + + return ( + <> + + + + {data?.nodes.map((node: NodeExtended) => { + const hide = !!selectedNode && (relativeIds.includes(node.ref_id) || selectedNode.ref_id === node.ref_id) + + console.log(hide) + + const primaryColor = normalizedSchemasByType[node.node_type]?.primary_color + const color = primaryColor ?? (COLORS_MAP[nodeTypes.indexOf(node.node_type)] || colors.white) + + return + })} + + + ) +} + +export const NodePoints = memo(_NodePoints) diff --git a/src/components/Universe/Graph/Cubes/Text/index.tsx b/src/components/Universe/Graph/Cubes/Text/index.tsx index 05e70d8b2..f7955e24c 100644 --- a/src/components/Universe/Graph/Cubes/Text/index.tsx +++ b/src/components/Universe/Graph/Cubes/Text/index.tsx @@ -1,6 +1,5 @@ -import { Billboard, Point, Svg, Text } from '@react-three/drei' +import { Billboard, Svg, Text } from '@react-three/drei' import { useFrame } from '@react-three/fiber' -import { Select } from '@react-three/postprocessing' import { throttle } from 'lodash' import { memo, useMemo, useRef } from 'react' import { Mesh, MeshStandardMaterial, TorusGeometry, Vector3 } from 'three' @@ -85,7 +84,7 @@ export const TextNode = memo(({ node, hide }: Props) => { const checkDistance = throttle(() => { const nodePosition = new Vector3().setFromMatrixPosition(ref.current!.matrixWorld) const distance = nodePosition.distanceTo(camera.position) - const isLess = distance < 5000 + const isLess = distance < 2500 if (ringRef.current) { ringRef.current.visible = isLess @@ -139,49 +138,46 @@ export const TextNode = memo(({ node, hide }: Props) => { return ( <> - + {splitStringIntoThreeParts(sanitizedNodeName)} + + + ) }) diff --git a/src/components/Universe/Graph/Cubes/index.tsx b/src/components/Universe/Graph/Cubes/index.tsx index 4c1b222a9..9c37ba074 100644 --- a/src/components/Universe/Graph/Cubes/index.tsx +++ b/src/components/Universe/Graph/Cubes/index.tsx @@ -6,6 +6,7 @@ import { useAppStore } from '~/stores/useAppStore' import { useDataStore } from '~/stores/useDataStore' import { useGraphStore, useSelectedNode, useSelectedNodeRelativeIds } from '~/stores/useGraphStore' import { NodeExtended } from '~/types' +import { NodePoints } from './NodePoints' import { RelevanceBadges } from './RelevanceBadges' import { SelectionDataNodes } from './SelectionDataNodes' import { TextNode } from './Text' @@ -107,6 +108,9 @@ export const Cubes = memo(() => { ) })} + + + {hideUniverse && } diff --git a/src/components/Universe/Graph/index.tsx b/src/components/Universe/Graph/index.tsx index b489c9178..1c29fe7fa 100644 --- a/src/components/Universe/Graph/index.tsx +++ b/src/components/Universe/Graph/index.tsx @@ -85,6 +85,7 @@ export const Graph = () => { if (groupRef.current) { const gr = groupRef.current.getObjectByName('simulation-3d-group__nodes') as Group + 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) => { @@ -95,6 +96,14 @@ export const Graph = () => { } }) + grPoints.children[0].children.forEach((mesh, index) => { + const simulationNode = simulation.nodes()[index] + + if (simulationNode) { + mesh.position.set(simulationNode.x, simulationNode.y, simulationNode.z) + } + }) + grConnections.children.forEach((r, i) => { const link = dataInitial?.links[i] const Line = r as Line2 From 074242df10eab51724faa1068df859a635e0f0b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Thu, 24 Oct 2024 19:35:53 +0300 Subject: [PATCH 3/4] feat: update graph layout --- src/components/App/index.tsx | 2 +- .../Graph/Cubes/NodePoints/Point/index.tsx | 44 ++------ .../Universe/Graph/Cubes/NodePoints/index.tsx | 17 ++- .../Universe/Graph/Cubes/Text/index.tsx | 102 ++++++++---------- src/components/Universe/Graph/Cubes/index.tsx | 25 +++-- src/components/Universe/Graph/index.tsx | 14 +-- src/components/Universe/index.tsx | 26 +---- 7 files changed, 91 insertions(+), 139 deletions(-) diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index 482901655..bf355b12b 100644 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -333,7 +333,7 @@ export const App = () => { -