summaryrefslogtreecommitdiff
path: root/app_expo/components/graph
diff options
context:
space:
mode:
Diffstat (limited to 'app_expo/components/graph')
-rw-r--r--app_expo/components/graph/graph.story.tsx15
-rw-r--r--app_expo/components/graph/graph.tsx529
-rw-r--r--app_expo/components/graph/graphbak.tsx448
-rw-r--r--app_expo/components/graph/graphgood.tsx440
4 files changed, 1432 insertions, 0 deletions
diff --git a/app_expo/components/graph/graph.story.tsx b/app_expo/components/graph/graph.story.tsx
new file mode 100644
index 0000000..3b094d9
--- /dev/null
+++ b/app_expo/components/graph/graph.story.tsx
@@ -0,0 +1,15 @@
+import * as React from 'react'
+import { storiesOf } from '@storybook/react-native'
+import { StoryScreen, Story, UseCase } from '../../../storybook/views'
+import { color } from '../../theme'
+import { Graph } from './graph'
+
+storiesOf('Graph', module)
+ .addDecorator((fn) => <StoryScreen>{fn()}</StoryScreen>)
+ .add('Style Presets', () => (
+ <Story>
+ <UseCase text="Primary" usage="The primary.">
+ <Graph style={{ backgroundColor: color.error }} />
+ </UseCase>
+ </Story>
+ ))
diff --git a/app_expo/components/graph/graph.tsx b/app_expo/components/graph/graph.tsx
new file mode 100644
index 0000000..0c959ec
--- /dev/null
+++ b/app_expo/components/graph/graph.tsx
@@ -0,0 +1,529 @@
+import * as React from 'react'
+import { useState, useEffect, useRef, useMemo, useCallback } from 'react'
+import { StyleProp, TextStyle, View, ViewStyle } from 'react-native'
+import { observer } from 'mobx-react-lite'
+import { color, typography } from '../../theme'
+import { Text } from '../'
+import { flatten } from 'ramda'
+
+//import data from "../../data/miserables.json"
+//import genRandomTree from "../../data/randomdata";
+//import gData from "../../data/rando.json"
+
+import { ForceGraph2D, ForceGraph3D, ForceGraphVR, ForceGraphAR } from 'react-force-graph'
+import * as d3 from 'd3-force-3d'
+//import * as three from "three"
+import SpriteText from 'three-spritetext'
+
+const CONTAINER: ViewStyle = {
+ justifyContent: 'center',
+}
+
+const TEXT: TextStyle = {
+ fontFamily: typography.primary,
+ fontSize: 14,
+ color: color.primary,
+}
+
+export interface GraphProps {
+ style?: StyleProp<ViewStyle>
+ physics
+ gData
+ setPhysics
+ nodeIds: string[]
+ threeDim
+ setThreeDim
+ local
+ setLocal
+}
+
+export const Graph = observer(function Graph(props: GraphProps): JSX.Element {
+ const { style, physics, setPhysics, gData, threeDim, setThreeDim, local, setLocal } = props
+ const styles = flatten([CONTAINER, style])
+
+ const fgRef = useRef()
+
+ const GROUPS: number = 12
+ const NODE_R: number = 8
+ //const gData = genRandomTree(200);
+
+ //const [charge, setCharge] = useState(-30);
+ //const [link, setLink] = useState(-30);
+
+ useEffect(() => {
+ const fg = fgRef.current
+ //fg.d3Force('center').strength(0.05);
+ if (physics.gravityOn) {
+ fg.d3Force('x', d3.forceX().strength(physics.gravity))
+ fg.d3Force('y', d3.forceY().strength(physics.gravity))
+ if (threeDim) {
+ if (physics.galaxy) {
+ fg.d3Force('x', d3.forceX().strength(physics.gravity / 5))
+ fg.d3Force('z', d3.forceZ().strength(physics.gravity / 5))
+ } else {
+ fg.d3Force('x', d3.forceX().strength(physics.gravity))
+ fg.d3Force('z', d3.forceZ().strength(physics.gravity))
+ }
+ } else {
+ fg.d3Force('z', null)
+ }
+ } else {
+ fg.d3Force('x', null)
+ fg.d3Force('y', null)
+ threeDim ? fg.d3Force('z', null) : null
+ }
+ fg.d3Force('link').strength(physics.linkStrength)
+ fg.d3Force('link').iterations(physics.linkIts)
+ physics.collision
+ ? fg.d3Force('collide', d3.forceCollide().radius(20))
+ : fg.d3Force('collide', null)
+ fg.d3Force('charge').strength(physics.charge)
+ })
+
+ // For the expandable version of the graph
+
+ /* const nodesById = useMemo(() => {
+ * const nodesById = Object.fromEntries(gData.nodes.map((node) => [node.index, node]))
+ * console.log(nodesById)
+ * // link parent/children
+ * gData.nodes.forEach((node) => {
+ * typeof physics.rootId === "number"
+ * ? (node.collapsed = node.index !== physics.rootId)
+ * : (node.collapsed = node.id !== physics.rootId)
+ * node.childLinks = []
+ * })
+ * gData.links.forEach((link) => nodesById[link.sourceIndex].childLinks.push(link))
+ * return nodesById
+ * }, [gData])
+ * const getPrunedTree = useCallback(() => {
+ * const visibleNodes = []
+ * const visibleLinks = []
+ * ;(function traverseTree(node = nodesById[physics.rootId]) {
+ * visibleNodes.push(node)
+ * if (node.collapsed) return
+ * visibleLinks.push(...node.childLinks)
+ * node.childLinks
+ * .map((link) =>
+ * typeof link.targetIndex === "object" ? link.targetIndex : nodesById[link.targetIndex],
+ * ) // get child node
+ * .forEach(traverseTree)
+ * })()
+
+ * return { nodes: visibleNodes, links: visibleLinks }
+ * }, [nodesById])
+ * const [prunedTree, setPrunedTree] = useState(getPrunedTree())
+ */
+ const handleNodeClick = useCallback((node) => {
+ node.collapsed = !node.collapsed // toggle collapse state
+ setPrunedTree(getPrunedTree())
+ }, [])
+
+ //highlighting
+ const [highlightNodes, setHighlightNodes] = useState(new Set())
+ const [highlightLinks, setHighlightLinks] = useState(new Set())
+ const [hoverNode, setHoverNode] = useState(null)
+
+ const updateHighlight = () => {
+ setHighlightNodes(highlightNodes)
+ setHighlightLinks(highlightLinks)
+ }
+
+ const handleBackgroundClick = (event) => {
+ highlightNodes.clear()
+ highlightLinks.clear()
+
+ setSelectedNode(null)
+ updateHighlight()
+ }
+
+ const handleNodeHover = (node) => {
+ console.log('hover')
+ if (!selectedNode) {
+ highlightNodes.clear()
+ highlightLinks.clear()
+ if (node) {
+ highlightNodes.add(node)
+ node.neighbors.forEach((neighbor) => highlightNodes.add(neighbor))
+ node.links.forEach((link) => highlightLinks.add(link))
+ }
+
+ setHoverNode(node || null)
+ updateHighlight()
+ }
+ }
+
+ const handleLinkHover = (link) => {
+ highlightNodes.clear()
+ highlightLinks.clear()
+
+ if (link) {
+ highlightLinks.add(link)
+ highlightNodes.add(link.source)
+ highlightNodes.add(link.target)
+ }
+
+ updateHighlight()
+ }
+
+ // Normally the graph doesn't update when you just change the physics parameters
+ // This forces the graph to make a small update when you do
+ useEffect(() => {
+ fgRef.current.d3ReheatSimulation()
+ }, [physics])
+ /* const paintRing = useCallback((node, ctx) => {
+ * // add ring just for highlighted nodes
+ * ctx.beginPath();
+ * ctx.arc(node.x, node.y, NODE_R * 1.4, 0, 2 * Math.PI, false);
+ * ctx.fillStyle = node === hoverNode ? 'red' : 'orange';
+ * ctx.fill();
+ * }, [hoverNode]);
+ */
+
+ /* autoPauseRedraw={false}
+linkWidth={link => highlightLinks.has(link) ? 5 : 1}
+linkDirectionalParticles={4}
+linkDirectionalParticleWidth={link => highlightLinks.has(link) ? 4 : 0}
+nodeCanvasObjectMode={node => highlightNodes.has(node) ? 'before' : undefined}
+nodeCanvasObject={paintRing}
+onNodeHover={handleNodeHover}
+onLinkHover={handleLinkHover}
+ nodeRelSize={NODE_R} */
+
+ //nodeColor={(node) =>
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ //}
+
+ const [selectedNode, setSelectedNode] = useState({})
+
+ //shitty handler to check for doubleClicks
+ const [doubleClick, setDoubleClick] = useState(0)
+ const [localGraphData, setLocalGraphData] = useState({
+ nodes: [],
+ links: [],
+ })
+
+ useEffect(() => {
+ localGraphData.nodes.length && !local && setLocal(true)
+ }, [localGraphData])
+
+ const getLocalGraphData = (node) => {
+ console.log(localGraphData)
+ localGraphData.nodes.length ? setLocalGraphData({ nodes: [], links: [] }) : null
+ let g = localGraphData
+ console.log(g.nodes)
+ if (!node.local) {
+ g = { nodes: [], links: [] }
+ console.log('length is 0')
+ node.local = true //keep track of these boys
+ g.nodes.push(node) //only add the clicked node if its the first
+ }
+ node.links.length &&
+ node.links.forEach((neighborLink) => {
+ if (!neighborLink.local) {
+ console.log('0')
+ neighborLink.local = true
+ g.links.push(neighborLink)
+ console.log(neighborLink)
+ const targetNode = gData.nodes[neighborLink.targetIndex]
+ const sourceNode = gData.nodes[neighborLink.sourceIndex]
+ if (targetNode.id !== sourceNode.id) {
+ if (targetNode.id === node.id) {
+ console.log('1. I am the target, the source is ')
+ console.log(sourceNode)
+ if (!sourceNode.local) {
+ console.log('2. The source is not local')
+ sourceNode.local = true
+ g.nodes.push(sourceNode)
+ } else {
+ console.log('2.5 The source is already local')
+ }
+ } else {
+ console.log('3. I am the source')
+ if (!targetNode.local) {
+ console.log('4. The target is not local.')
+ targetNode.local = true
+ g.nodes.push(targetNode)
+ } else {
+ console.log('The target is already local')
+ }
+ }
+ }
+ }
+ })
+ setLocalGraphData(g)
+ }
+
+ const selectClick = (node, event) => {
+ window.open('org-protocol://roam-node?node=' + node.id, '_self')
+ highlightNodes.clear()
+ highlightLinks.clear()
+ console.log(localGraphData)
+ if (event.timeStamp - doubleClick < 400) {
+ getLocalGraphData(node)
+ }
+ if (node) {
+ highlightNodes.add(node)
+ node.neighbors.forEach((neighbor) => highlightNodes.add(neighbor))
+ node.links.forEach((link) => highlightLinks.add(link))
+ }
+
+ setSelectedNode(node || null)
+ updateHighlight()
+ setDoubleClick(event.timeStamp)
+ }
+
+ useEffect(() => {
+ if (local && selectedNode) {
+ getLocalGraphData(selectedNode)
+ }
+ }, [local])
+ return (
+ <View style={style}>
+ {!threeDim ? (
+ <ForceGraph2D
+ ref={fgRef}
+ //autoPauseRedraw={false}
+ //graphData={gData}
+ graphData={local ? localGraphData : gData}
+ //nodeAutoColorBy={physics.colorful ? (node)=>node.index%GROUPS : undefined}
+ nodeColor={
+ !physics.colorful
+ ? (node) => {
+ if (highlightNodes.size === 0) {
+ return 'rgb(100, 100, 100, 1)'
+ } else {
+ return highlightNodes.has(node) ? '#a991f1' : 'rgb(50, 50, 50, 0.5)'
+ }
+ }
+ : (node) => {
+ if (node.neighbors.length === 1 || node.neighbors.length === 2) {
+ return [
+ '#ff665c',
+ '#e69055',
+ '#7bc275',
+ '#4db5bd',
+ '#FCCE7B',
+ '#51afef',
+ '#1f5582',
+ '#C57BDB',
+ '#a991f1',
+ '#5cEfFF',
+ '#6A8FBF',
+ ][node.neighbors[0].index % 11]
+ } else {
+ return [
+ '#ff665c',
+ '#e69055',
+ '#7bc275',
+ '#4db5bd',
+ '#FCCE7B',
+ '#51afef',
+ '#1f5582',
+ '#C57BDB',
+ '#a991f1',
+ '#5cEfFF',
+ '#6A8FBF',
+ ][node.index % 11]
+ }
+ }
+ }
+ //linkAutoColorBy={physics.colorful ? ((d) => gData.nodes[d.sourceIndex].id % GROUPS) : undefined}
+ linkColor={
+ !physics.colorful
+ ? (link) => {
+ if (highlightLinks.size === 0) {
+ return 'rgb(50, 50, 50, 0.8)'
+ } else {
+ return highlightLinks.has(link) ? '#a991f1' : 'rgb(50, 50, 50, 0.2)'
+ }
+ }
+ : (link) =>
+ [
+ '#ff665c',
+ '#e69055',
+ '#7bc275',
+ '#4db5bd',
+ '#FCCE7B',
+ '#51afef',
+ '#1f5582',
+ '#C57BDB',
+ '#a991f1',
+ '#5cEfFF',
+ '#6A8FBF',
+ ][gData.nodes[link.sourceIndex].index % 11]
+ }
+ linkDirectionalParticles={physics.particles}
+ onNodeClick={selectClick}
+ nodeLabel={(node) => node.title}
+ linkWidth={(link) =>
+ highlightLinks.has(link) ? 3 * physics.linkWidth : physics.linkWidth
+ }
+ linkOpacity={physics.linkOpacity}
+ nodeRelSize={physics.nodeRel}
+ nodeVal={(node) => {
+ return highlightNodes.has(node) ? node.neighbors.length + 5 : node.neighbors.length + 3
+ }}
+ linkDirectionalParticleWidth={physics.particleWidth}
+ nodeCanvasObject={(node, ctx, globalScale) => {
+ if (physics.labels) {
+ if (globalScale > physics.labelScale || highlightNodes.has(node)) {
+ const label = node.title.substring(0, Math.min(node.title.length, 30))
+ const fontSize = 12 / globalScale
+ ctx.font = `${fontSize}px Sans-Serif`
+ const textWidth = ctx.measureText(label).width
+ const bckgDimensions = [textWidth * 1.1, fontSize].map((n) => n + fontSize * 0.5) // some padding
+ const fadeFactor = Math.min(
+ (3 * (globalScale - physics.labelScale)) / physics.labelScale,
+ 1,
+ )
+
+ ctx.fillStyle =
+ 'rgba(20, 20, 20, ' +
+ (highlightNodes.size === 0
+ ? 0.5 * fadeFactor
+ : highlightNodes.has(node)
+ ? 0.5
+ : 0.15 * fadeFactor) +
+ ')'
+ ctx.fillRect(
+ node.x - bckgDimensions[0] / 2,
+ node.y - bckgDimensions[1] / 2,
+ ...bckgDimensions,
+ )
+
+ ctx.textAlign = 'center'
+ ctx.textBaseline = 'middle'
+ ctx.fillStyle =
+ 'rgb(255, 255, 255, ' +
+ (highlightNodes.size === 0
+ ? fadeFactor
+ : highlightNodes.has(node)
+ ? 1
+ : 0.3 * fadeFactor) +
+ ')'
+ ctx.fillText(label, node.x, node.y)
+
+ node.__bckgDimensions = bckgDimensions // to re-use in nodePointerAreaPaint
+ }
+ }
+ }}
+ nodeCanvasObjectMode={() => 'after'}
+ onNodeHover={physics.hover ? handleNodeHover : null}
+ //onLinkHover={physics.hover ? handleLinkHover : null}
+ d3AlphaDecay={physics.alphaDecay}
+ d3AlphaMin={physics.alphaTarget}
+ d3VelocityDecay={physics.velocityDecay}
+ onBackgroundClick={handleBackgroundClick}
+ backgroundColor={'#242730'}
+ />
+ ) : (
+ <ForceGraph3D
+ ref={fgRef}
+ graphData={!local ? gData : localGraphData}
+ //graphData={gData}
+ nodeColor={
+ !physics.colorful
+ ? (node) => {
+ if (highlightNodes.size === 0) {
+ return 'rgb(100, 100, 100, 1)'
+ } else {
+ return highlightNodes.has(node) ? 'purple' : 'rgb(50, 50, 50, 0.5)'
+ }
+ }
+ : (node) => {
+ if (node.neighbors.length === 1 || node.neighbors.length === 2) {
+ return [
+ '#ff665c',
+ '#e69055',
+ '#7bc275',
+ '#4db5bd',
+ '#FCCE7B',
+ '#51afef',
+ '#1f5582',
+ '#C57BDB',
+ '#a991f1',
+ '#5cEfFF',
+ '#6A8FBF',
+ ][node.neighbors[0].index % 11]
+ } else {
+ return [
+ '#ff665c',
+ '#e69055',
+ '#7bc275',
+ '#4db5bd',
+ '#FCCE7B',
+ '#51afef',
+ '#1f5582',
+ '#C57BDB',
+ '#a991f1',
+ '#5cEfFF',
+ '#6A8FBF',
+ ][node.index % 11]
+ }
+ }
+ }
+ //linkAutoColorBy={physics.colorful ? ((d) => gData.nodes[d.sourceIndex].id % GROUPS) : undefined}
+ linkColor={
+ !physics.colorful
+ ? (link) => {
+ if (highlightLinks.size === 0) {
+ return 'rgb(50, 50, 50, 0.8)'
+ } else {
+ return highlightLinks.has(link) ? 'purple' : 'rgb(50, 50, 50, 0.2)'
+ }
+ }
+ : (link) =>
+ [
+ '#ff665c',
+ '#e69055',
+ '#7bc275',
+ '#4db5bd',
+ '#FCCE7B',
+ '#51afef',
+ '#1f5582',
+ '#C57BDB',
+ '#a991f1',
+ '#5cEfFF',
+ '#6A8FBF',
+ ][gData.nodes[link.sourceIndex].index % 11]
+ }
+ linkDirectionalParticles={physics.particles}
+ nodeLabel={(node) => node.title}
+ linkWidth={(link) =>
+ highlightLinks.has(link) ? 3 * physics.linkWidth : physics.linkWidth
+ }
+ linkOpacity={physics.linkOpacity}
+ nodeRelSize={physics.nodeRel}
+ nodeVal={(node) =>
+ highlightNodes.has(node) ? node.neighbors.length * 3 : node.neighbors.length * 2
+ }
+ linkDirectionalParticleWidth={physics.particleWidth}
+ onNodeHover={physics.hover ? handleNodeHover : null}
+ d3AlphaDecay={physics.alphaDecay}
+ d3AlphaMin={physics.alphaTarget}
+ d3VelocityDecay={physics.velocityDecay}
+ nodeThreeObject={
+ !physics.labels
+ ? undefined
+ : (node) => {
+ if (highlightNodes.has(node)) {
+ console.log(node.title)
+ const sprite = new SpriteText(node.title.substring(0, 30))
+ console.log('didnt crash here 2')
+ sprite.color = '#ffffff'
+ sprite.textHeight = 8
+ return sprite
+ } else {
+ return undefined
+ }
+ }
+ }
+ nodeThreeObjectExtend={true}
+ onNodeClick={selectClick}
+ onBackgroundClick={handleBackgroundClick}
+ backgroundColor={'#242730'}
+ />
+ )}
+ </View>
+ )
+})
diff --git a/app_expo/components/graph/graphbak.tsx b/app_expo/components/graph/graphbak.tsx
new file mode 100644
index 0000000..5291678
--- /dev/null
+++ b/app_expo/components/graph/graphbak.tsx
@@ -0,0 +1,448 @@
+
+import * as React from "react"
+import { useState, useEffect, useRef, useMemo, useCallback } from "react"
+import { StyleProp, TextStyle, View, ViewStyle } from "react-native"
+import { observer } from "mobx-react-lite"
+import { color, typography } from "../../theme"
+import { Text } from "../"
+import { flatten } from "ramda"
+
+//import data from "../../data/miserables.json"
+//import genRandomTree from "../../data/randomdata";
+//import rando from "../../data/rando.json"
+
+import { ForceGraph2D, ForceGraph3D, ForceGraphVR, ForceGraphAR } from "react-force-graph"
+import * as d3 from "d3-force-3d"
+import * as three from "three"
+import SpriteText from "three-spritetext"
+
+const CONTAINER: ViewStyle = {
+ justifyContent: "center",
+}
+
+const TEXT: TextStyle = {
+ fontFamily: typography.primary,
+ fontSize: 14,
+ color: color.primary,
+}
+
+export interface GraphProps {
+ /**
+ * An optional style override useful for padding & margin.
+ */
+ style?: StyleProp<ViewStyle>
+ physics
+ gData
+ setPhysics
+ nodeIds: string[]
+}
+
+/**
+ * Describe your component here
+ */
+export const Graph = observer(function Graph(props: GraphProps): JSX.Element {
+ const { style, physics, setPhysics, gData, nodeIds } = props
+ const styles = flatten([CONTAINER, style])
+
+ const fgRef = useRef()
+
+ const GROUPS: number = 12
+ const NODE_R: number = 8
+ //const gData = genRandomTree(200);
+
+ //const [charge, setCharge] = useState(-30);
+ //const [link, setLink] = useState(-30);
+
+ useEffect(() => {
+ const fg = fgRef.current
+ //fg.d3Force('center').strength(0.05);
+ if (physics.gravityOn) {
+ fg.d3Force("x", d3.forceX().strength(physics.gravity))
+ fg.d3Force("y", d3.forceY().strength(physics.gravity))
+ if (physics.threedim) {
+ if (physics.galaxy) {
+ fg.d3Force("y", d3.forceY().strength(physics.gravity / 5))
+ fg.d3Force("z", d3.forceZ().strength(physics.gravity / 5))
+ } else {
+ fg.d3Force("y", d3.forceY().strength(physics.gravity))
+ fg.d3Force("z", d3.forceZ().strength(physics.gravity))
+ }
+ } else {
+ fg.d3Force("z", null)
+ }
+ } else {
+ fg.d3Force("x", null)
+ fg.d3Force("y", null)
+ physics.threedim ? fg.d3Force("z", null) : null
+ }
+ fg.d3Force("link").strength(physics.linkStrength)
+ fg.d3Force("link").iterations(physics.linkIts)
+ physics.collision
+ ? fg.d3Force("collide", d3.forceCollide().radius(20))
+ : fg.d3Force("collide", null)
+ fg.d3Force("charge").strength(physics.charge)
+ })
+
+ // For the expandable version of the graph
+
+ const nodesById = useMemo(() => {
+ const nodesById = Object.fromEntries(gData.nodes.map((node) => [node.index, node]))
+ console.log(nodesById)
+ // link parent/children
+ gData.nodes.forEach((node) => {
+ typeof physics.rootId === "number"
+ ? (node.collapsed = node.index !== physics.rootId)
+ : (node.collapsed = node.id !== physics.rootId)
+ node.childLinks = []
+ })
+ gData.links.forEach((link) => nodesById[link.sourceIndex].childLinks.push(link))
+ return nodesById
+ }, [gData])
+
+ const getPrunedTree = useCallback(() => {
+ const visibleNodes = []
+ const visibleLinks = []
+ ;(function traverseTree(node = nodesById[physics.rootId]) {
+ visibleNodes.push(node)
+ if (node.collapsed) return
+ visibleLinks.push(...node.childLinks)
+ node.childLinks
+ .map((link) =>
+ typeof link.targetIndex === "object" ? link.targetIndex : nodesById[link.targetIndex],
+ ) // get child node
+ .forEach(traverseTree)
+ })()
+
+ return { nodes: visibleNodes, links: visibleLinks }
+ }, [nodesById])
+
+ const [prunedTree, setPrunedTree] = useState(getPrunedTree())
+
+ const handleNodeClick = useCallback((node) => {
+ node.collapsed = !node.collapsed // toggle collapse state
+ setPrunedTree(getPrunedTree())
+ }, [])
+
+ //highlighting
+ const [highlightNodes, setHighlightNodes] = useState(new Set())
+ const [highlightLinks, setHighlightLinks] = useState(new Set())
+ const [hoverNode, setHoverNode] = useState(null)
+
+ const updateHighlight = () => {
+ setHighlightNodes(highlightNodes)
+ setHighlightLinks(highlightLinks)
+ }
+
+ const handleBackgroundClick = (event) => {
+ highlightNodes.clear()
+ highlightLinks.clear()
+
+ setSelectedNode(null)
+ updateHighlight()
+ }
+
+ const handleNodeHover = (node) => {
+ console.log("hover")
+ if (!selectedNode) {
+ highlightNodes.clear()
+ highlightLinks.clear()
+ if (node) {
+ highlightNodes.add(node)
+ node.neighbors.forEach((neighbor) => highlightNodes.add(neighbor))
+ node.links.forEach((link) => highlightLinks.add(link))
+ }
+
+ setHoverNode(node || null)
+ updateHighlight()
+ }
+ }
+
+ const handleLinkHover = (link) => {
+ highlightNodes.clear()
+ highlightLinks.clear()
+
+ if (link) {
+ highlightLinks.add(link)
+ highlightNodes.add(link.source)
+ highlightNodes.add(link.target)
+ }
+
+ updateHighlight()
+ }
+
+ // Normally the graph doesn't update when you just change the physics parameters
+ // This forces the graph to make a small update when you do
+ useEffect(() => {
+ fgRef.current.d3ReheatSimulation()
+ }, [physics])
+ /* const paintRing = useCallback((node, ctx) => {
+ * // add ring just for highlighted nodes
+ * ctx.beginPath();
+ * ctx.arc(node.x, node.y, NODE_R * 1.4, 0, 2 * Math.PI, false);
+ * ctx.fillStyle = node === hoverNode ? 'red' : 'orange';
+ * ctx.fill();
+ * }, [hoverNode]);
+ */
+
+ /* autoPauseRedraw={false}
+linkWidth={link => highlightLinks.has(link) ? 5 : 1}
+linkDirectionalParticles={4}
+linkDirectionalParticleWidth={link => highlightLinks.has(link) ? 4 : 0}
+nodeCanvasObjectMode={node => highlightNodes.has(node) ? 'before' : undefined}
+nodeCanvasObject={paintRing}
+onNodeHover={handleNodeHover}
+onLinkHover={handleLinkHover}
+ nodeRelSize={NODE_R} */
+
+ //nodeColor={(node) =>
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ //}
+
+ const [selectedNode, setSelectedNode] = useState({})
+
+ //shitty handler to check for doubleClicks
+ const [doubleClick, setDoubleClick] = useState(0)
+ const [localGraphData, setLocalGraphData] = useState({ nodes: [], links: [] })
+
+ useEffect(() => {
+ !physics.local && setPhysics({ ...physics, local: true })
+ }, [localGraphData])
+
+ const getLocalGraphData = (node) {
+ console.log(localGraphData)
+ localGraphData.nodes.length ? setLocalGraphData({nodes: [], links: []}) : null;
+ let g = localGraphData
+ console.log(g.nodes)
+ if (g.nodes.length === 0) {
+ console.log("length is 0")
+ node.local = true //keep track of these boys
+ g.nodes.push(node) //only add the clicked node if its the first
+ }
+ node.links.forEach((neighborLink) => {
+ if (!neighborLink.local) {
+ console.log("0")
+ neighborLink.local = true
+ g.links.push(neighborLink)
+ const targetNode = gData.nodes[neighborLink.targetIndex]
+ const sourceNode = gData.nodes[neighborLink.sourceIndex]
+ if (targetNode.id === node.id) {
+ console.log("1. I am the target, the source is ")
+ console.log(sourceNode)
+ if (!sourceNode.local) {
+ console.log("2. The source is not local")
+ sourceNode.local = true
+ g.nodes.push(sourceNode)
+ } else {
+ console.log("2.5 The source is already local")
+ }
+ } else {
+ console.log("3. I am the source")
+ if (!targetNode.local) {
+ console.log("4. The target is not local.")
+ targetNode.local = true
+ g.nodes.push(targetNode)
+ } else {
+ console.log("The target is already local")
+ }
+ }
+ }
+ })
+ setLocalGraphData(g)
+ };
+
+ const selectClick = (node, event) => {
+ highlightNodes.clear()
+ highlightLinks.clear()
+ console.log(localGraphData)
+ if (event.timeStamp - doubleClick < 400) {
+ getLocalGraphData(node);
+ }
+ if (node) {
+ highlightNodes.add(node)
+ node.neighbors.forEach((neighbor) => highlightNodes.add(neighbor))
+ node.links.forEach((link) => highlightLinks.add(link))
+ }
+
+ setSelectedNode(node || null)
+ updateHighlight()
+ setDoubleClick(event.timeStamp)
+ }
+
+ return (
+ <View>
+ {!physics.threedim ? (
+ <ForceGraph2D
+ ref={fgRef}
+ autoPauseRedraw={false}
+ //graphData={gData}
+ graphData={physics.local ? localGraphData : (physics.collapse ? prunedTree : gData)}
+ nodeAutoColorBy={physics.colorful ? "id" : undefined}
+ nodeColor={
+ !physics.colorful
+ ? (node) => {
+ if (highlightNodes.size === 0) {
+ return "rgb(100, 100, 100, 1)"
+ } else {
+ return highlightNodes.has(node) ? "purple" : "rgb(50, 50, 50, 0.5)"
+ }
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ : undefined
+ }
+ linkAutoColorBy={physics.colorful ? "target" : undefined}
+ //linkAutoColorBy={(d) => gData.nodes[d.source].id % GROUPS}
+ linkColor={
+ !physics.colorful
+ ? (link) => {
+ if (highlightLinks.size === 0) {
+ return "rgb(50, 50, 50, 0.8)"
+ } else {
+ return highlightLinks.has(link) ? "purple" : "rgb(50, 50, 50, 0.2)"
+ }
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ : undefined
+ //highlightLinks.has(link) ? "purple" : "grey"
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ linkDirectionalParticles={physics.particles}
+ onNodeClick={!physics.collapse ? selectClick : handleNodeClick}
+ nodeLabel={(node) => node.title}
+ //nodeVal ={(node)=> node.childLinks.length * 0.5 + 1}
+ //d3VelocityDecay={visco}
+ linkWidth={(link) =>
+ highlightLinks.has(link) ? 3 * physics.linkWidth : physics.linkWidth
+ }
+ linkOpacity={physics.linkOpacity}
+ nodeRelSize={physics.nodeRel}
+ nodeVal={(node) =>
+ highlightNodes.has(node) ? node.neighbors.length + 5 : node.neighbors.length + 3
+ }
+ linkDirectionalParticleWidth={physics.particleWidth}
+ nodeCanvasObject={(node, ctx, globalScale) => {
+ if (physics.labels) {
+ if (globalScale > physics.labelScale || highlightNodes.has(node)) {
+ const label = node.title.substring(0, Math.min(node.title.length, 30))
+ const fontSize = 12 / globalScale
+ ctx.font = `${fontSize}px Sans-Serif`
+ const textWidth = ctx.measureText(label).width
+ const bckgDimensions = [textWidth * 1.1, fontSize].map((n) => n + fontSize * 0.5) // some padding
+ const fadeFactor = Math.min(
+ (3 * (globalScale - physics.labelScale)) / physics.labelScale,
+ 1,
+ )
+
+ ctx.fillStyle =
+ "rgba(20, 20, 20, " +
+ (highlightNodes.size === 0
+ ? 0.5 * fadeFactor
+ : highlightNodes.has(node)
+ ? 0.5
+ : 0.15 * fadeFactor) +
+ ")"
+ ctx.fillRect(
+ node.x - bckgDimensions[0] / 2,
+ node.y - bckgDimensions[1] / 2,
+ ...bckgDimensions,
+ )
+
+ ctx.textAlign = "center"
+ ctx.textBaseline = "middle"
+ ctx.fillStyle =
+ "rgb(255, 255, 255, " +
+ (highlightNodes.size === 0
+ ? fadeFactor
+ : highlightNodes.has(node)
+ ? 1
+ : 0.3 * fadeFactor) +
+ ")"
+ ctx.fillText(label, node.x, node.y)
+
+ node.__bckgDimensions = bckgDimensions // to re-use in nodePointerAreaPaint
+ }
+ }
+ }}
+ nodeCanvasObjectMode={() => "after"}
+ onNodeHover={physics.hover ? handleNodeHover : null}
+ //onLinkHover={physics.hover ? handleLinkHover : null}
+ d3AlphaDecay={physics.alphaDecay}
+ d3AlphaMin={physics.alphaTarget}
+ d3VelocityDecay={physics.velocityDecay}
+ onBackgroundClick={handleBackgroundClick}
+ />
+ ) : (
+ <ForceGraph3D
+ ref={fgRef}
+ autoPauseRedraw={false}
+ graphData={gData}
+ //graphData={physics.collapse ? prunedTree : gData}
+ nodeAutoColorBy={physics.colorful ? "id" : undefined}
+ nodeColor={
+ !physics.colorful
+ ? (node) => {
+ if (highlightNodes.size === 0) {
+ return "rgb(100, 100, 100, 1)"
+ } else {
+ return highlightNodes.has(node) ? "purple" : "rgb(50, 50, 50, 0.5)"
+ }
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ : undefined
+ }
+ linkAutoColorBy={physics.colorful ? "target" : undefined}
+ //linkAutoColorBy={(d) => gData.nodes[d.source].id % GROUPS}
+ linkColor={
+ !physics.colorful
+ ? (link) => {
+ if (highlightLinks.size === 0) {
+ return "rgb(50, 50, 50, 0.8)"
+ } else {
+ return highlightLinks.has(link) ? "purple" : "rgb(50, 50, 50, 0.2)"
+ }
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ : undefined
+ //highlightLinks.has(link) ? "purple" : "grey"
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ linkDirectionalParticles={physics.particles}
+ //onNodeClick={!physics.collapse ? null : handleNodeClick}
+ nodeLabel={(node) => node.title}
+ //nodeVal ={(node)=> node.childLinks.length * 0.5 + 1}
+ //d3VelocityDecay={visco}
+ linkWidth={(link) =>
+ highlightLinks.has(link) ? 3 * physics.linkWidth : physics.linkWidth
+ }
+ linkOpacity={physics.linkOpacity}
+ nodeRelSize={physics.nodeRel}
+ nodeVal={(node) =>
+ highlightNodes.has(node) ? node.neighbors.length + 5 : node.neighbors.length + 3
+ }
+ linkDirectionalParticleWidth={physics.particleWidth}
+ onNodeHover={physics.hover ? handleNodeHover : null}
+ //onLinkHover={physics.hover ? handleLinkHover : null}
+ d3AlphaDecay={physics.alphaDecay}
+ d3AlphaMin={physics.alphaTarget}
+ d3VelocityDecay={physics.velocityDecay}
+ nodeThreeObject={
+ !physics.labels
+ ? undefined
+ : (node) => {
+ if (highlightNodes.has(node)) {
+ console.log(node.title)
+ const sprite = new SpriteText(node.title.substring(0, 30))
+ console.log("didnt crash here 2")
+ sprite.color = "#ffffff"
+ sprite.textHeight = 8
+ return sprite
+ } else {
+ return undefined
+ }
+ }
+ }
+ nodeThreeObjectExtend={true}
+ />
+ )}
+ </View>
+ )
+})
diff --git a/app_expo/components/graph/graphgood.tsx b/app_expo/components/graph/graphgood.tsx
new file mode 100644
index 0000000..4d70e25
--- /dev/null
+++ b/app_expo/components/graph/graphgood.tsx
@@ -0,0 +1,440 @@
+import * as React from 'react'
+import { useState, useEffect, useRef, useMemo, useCallback } from 'react'
+import { StyleProp, TextStyle, View, ViewStyle } from 'react-native'
+import { observer } from 'mobx-react-lite'
+import { color, typography } from '../../theme'
+import { Text } from '../'
+import { flatten } from 'ramda'
+
+//import data from "../../data/miserables.json"
+//import genRandomTree from "../../data/randomdata";
+//import rando from "../../data/rando.json"
+
+import { ForceGraph2D, ForceGraph3D, ForceGraphVR, ForceGraphAR } from 'react-force-graph'
+import * as d3 from 'd3-force-3d'
+import * as three from 'three'
+import SpriteText from 'three-spritetext'
+
+const CONTAINER: ViewStyle = {
+ justifyContent: 'center',
+}
+
+const TEXT: TextStyle = {
+ fontFamily: typography.primary,
+ fontSize: 14,
+ color: color.primary,
+}
+
+export interface GraphProps {
+ /**
+ * An optional style override useful for padding & margin.
+ */
+ style?: StyleProp<ViewStyle>
+ physics
+ setPhysics
+ gData
+ nodeIds: string[]
+}
+
+/**
+ * Describe your component here
+ */
+export const Graph = observer(function Graph(props: GraphProps): JSX.Element {
+ const { style, physics, setPhysics, gData, nodeIds } = props
+ const styles = flatten([CONTAINER, style])
+
+ const fgRef = useRef()
+
+ const GROUPS: number = 12
+ const NODE_R: number = 8
+ //const gData = genRandomTree(200);
+
+ //const [charge, setCharge] = useState(-30);
+ //const [link, setLink] = useState(-30);
+
+ useEffect(() => {
+ const fg = fgRef.current
+ //fg.d3Force('center').strength(0.05);
+ if (physics.gravityOn) {
+ fg.d3Force('x', d3.forceX().strength(physics.gravity))
+ fg.d3Force('y', d3.forceY().strength(physics.gravity))
+ if (physics.threedim) {
+ if (physics.galaxy) {
+ fg.d3Force('y', d3.forceY().strength(physics.gravity / 5))
+ fg.d3Force('z', d3.forceZ().strength(physics.gravity / 5))
+ } else {
+ fg.d3Force('y', d3.forceY().strength(physics.gravity))
+ fg.d3Force('z', d3.forceZ().strength(physics.gravity))
+ }
+ } else {
+ fg.d3Force('z', null)
+ }
+ } else {
+ fg.d3Force('x', null)
+ fg.d3Force('y', null)
+ physics.threedim ? fg.d3Force('z', null) : null
+ }
+ fg.d3Force('link').strength(physics.linkStrength)
+ fg.d3Force('link').iterations(physics.linkIts)
+ physics.collision
+ ? fg.d3Force('collide', d3.forceCollide().radius(20))
+ : fg.d3Force('collide', null)
+ fg.d3Force('charge').strength(physics.charge)
+ })
+
+ // For the expandable version of the graph
+
+ const nodesById = useMemo(() => {
+ const nodesById = Object.fromEntries(gData.nodes.map((node) => [node.index, node]))
+ console.log(nodesById)
+ // link parent/children
+ gData.nodes.forEach((node) => {
+ typeof physics.rootId === 'number'
+ ? (node.collapsed = node.index !== physics.rootId)
+ : (node.collapsed = node.id !== physics.rootId)
+ node.childLinks = []
+ })
+ gData.links.forEach((link) => nodesById[link.sourceIndex].childLinks.push(link))
+ return nodesById
+ }, [gData])
+ const getPrunedTree = useCallback(() => {
+ const visibleNodes = []
+ const visibleLinks = []
+ ;(function traverseTree(node = nodesById[physics.rootId]) {
+ visibleNodes.push(node)
+ if (node.collapsed) return
+ visibleLinks.push(...node.childLinks)
+ node.childLinks
+ .map((link) =>
+ typeof link.targetIndex === 'object' ? link.targetIndex : nodesById[link.targetIndex],
+ ) // get child node
+ .forEach(traverseTree)
+ })()
+
+ return { nodes: visibleNodes, links: visibleLinks }
+ }, [nodesById])
+
+ const [prunedTree, setPrunedTree] = useState(getPrunedTree())
+
+ const handleNodeClick = useCallback((node) => {
+ node.collapsed = !node.collapsed // toggle collapse state
+ setPrunedTree(getPrunedTree())
+ }, [])
+
+ //highlighting
+ const [highlightNodes, setHighlightNodes] = useState(new Set())
+ const [highlightLinks, setHighlightLinks] = useState(new Set())
+ const [hoverNode, setHoverNode] = useState(null)
+
+ const updateHighlight = () => {
+ setHighlightNodes(highlightNodes)
+ setHighlightLinks(highlightLinks)
+ }
+
+ const handleBackgroundClick = (event) => {
+ highlightNodes.clear()
+ highlightLinks.clear()
+
+ setSelectedNode(null)
+ updateHighlight()
+ }
+
+ const handleNodeHover = (node) => {
+ console.log('hover')
+ if (!selectedNode) {
+ highlightNodes.clear()
+ highlightLinks.clear()
+ if (node) {
+ highlightNodes.add(node)
+ node.neighbors.forEach((neighbor) => highlightNodes.add(neighbor))
+ node.links.forEach((link) => highlightLinks.add(link))
+ }
+
+ setHoverNode(node || null)
+ updateHighlight()
+ }
+ }
+
+ const handleLinkHover = (link) => {
+ highlightNodes.clear()
+ highlightLinks.clear()
+
+ if (link) {
+ highlightLinks.add(link)
+ highlightNodes.add(link.source)
+ highlightNodes.add(link.target)
+ }
+
+ updateHighlight()
+ }
+
+ // Normally the graph doesn't update when you just change the physics parameters
+ // This forces the graph to make a small update when you do
+ useEffect(() => {
+ fgRef.current.d3ReheatSimulation()
+ }, [physics])
+ /* const paintRing = useCallback((node, ctx) => {
+ * // add ring just for highlighted nodes
+ * ctx.beginPath();
+ * ctx.arc(node.x, node.y, NODE_R * 1.4, 0, 2 * Math.PI, false);
+ * ctx.fillStyle = node === hoverNode ? 'red' : 'orange';
+ * ctx.fill();
+ * }, [hoverNode]);
+ */
+
+ /* autoPauseRedraw={false}
+linkWidth={link => highlightLinks.has(link) ? 5 : 1}
+linkDirectionalParticles={4}
+linkDirectionalParticleWidth={link => highlightLinks.has(link) ? 4 : 0}
+nodeCanvasObjectMode={node => highlightNodes.has(node) ? 'before' : undefined}
+nodeCanvasObject={paintRing}
+onNodeHover={handleNodeHover}
+onLinkHover={handleLinkHover}
+ nodeRelSize={NODE_R} */
+
+ //nodeColor={(node) =>
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ //}
+
+ const [selectedNode, setSelectedNode] = useState({})
+
+ //shitty handler to check for doubleClicks
+ const [doubleClick, setDoubleClick] = useState(0)
+ const [localGraphData, setLocalGraphData] = useState({
+ nodes: [],
+ links: [],
+ })
+
+ const updateLocalGraph = (node) => {
+ console.log(localGraphData)
+ // localGraphData.nodes.length ? setLocalGraphData({ nodes: [], links: [] }) : null
+ let g = localGraphData
+ if (!node.local) {
+ node.local = true
+ g.nodes.push(node)
+ }
+ node.neighbors.forEach((neighbor) => {
+ if (neighbor !== node || !neighbor.local) {
+ const newNode: boolean = g.nodes.every((existingNode) => {
+ if (existingNode === neighbor) {
+ return false
+ } else {
+ return true
+ }
+ })
+ if (newNode) {
+ neighbor.local = true
+ g.nodes.push(neighbor)
+ }
+ }
+ })
+
+ node.links.forEach((neighborLink) => {
+ const newLink: boolean = g.links.every((existingLink) => {
+ if (existingLink === neighborLink) {
+ return false
+ } else {
+ return true
+ }
+ })
+ newLink && g.links.push(neighborLink)
+ })
+ setLocalGraphData(g)
+ setPhysics({ ...physics, local: true })
+ }
+
+ const selectClick = (node, event) => {
+ highlightNodes.clear()
+ highlightLinks.clear()
+ console.log(localGraphData)
+ if (event.timeStamp - doubleClick < 400) {
+ updateLocalGraph(node)
+ }
+ if (node) {
+ highlightNodes.add(node)
+ node.neighbors.forEach((neighbor) => highlightNodes.add(neighbor))
+ node.links.forEach((link) => highlightLinks.add(link))
+ }
+ setSelectedNode(node || null)
+ updateHighlight()
+ setDoubleClick(event.timeStamp)
+ }
+
+ return (
+ <View>
+ {!physics.threedim ? (
+ <ForceGraph2D
+ ref={fgRef}
+ autoPauseRedraw={false}
+ //graphData={gData}
+ graphData={physics.local ? localGraphData : physics.collapse ? prunedTree : gData}
+ nodeAutoColorBy={physics.colorful ? 'id' : undefined}
+ nodeColor={
+ !physics.colorful
+ ? (node) => {
+ if (highlightNodes.size === 0) {
+ return 'rgb(100, 100, 100, 1)'
+ } else {
+ return highlightNodes.has(node) ? 'purple' : 'rgb(50, 50, 50, 0.5)'
+ }
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ : undefined
+ }
+ linkAutoColorBy={physics.colorful ? 'target' : undefined}
+ //linkAutoColorBy={(d) => gData.nodes[d.source].id % GROUPS}
+ linkColor={
+ !physics.colorful
+ ? (link) => {
+ if (highlightLinks.size === 0) {
+ return 'rgb(50, 50, 50, 0.8)'
+ } else {
+ return highlightLinks.has(link) ? 'purple' : 'rgb(50, 50, 50, 0.2)'
+ }
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ : undefined
+ //highlightLinks.has(link) ? "purple" : "grey"
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ linkDirectionalParticles={physics.particles}
+ onNodeClick={!physics.collapse ? selectClick : handleNodeClick}
+ nodeLabel={(node) => node.title}
+ //nodeVal ={(node)=> node.childLinks.length * 0.5 + 1}
+ //d3VelocityDecay={visco}
+ linkWidth={(link) =>
+ highlightLinks.has(link) ? 3 * physics.linkWidth : physics.linkWidth
+ }
+ linkOpacity={physics.linkOpacity}
+ nodeRelSize={physics.nodeRel}
+ nodeVal={(node) =>
+ highlightNodes.has(node) ? node.neighbors.length + 5 : node.neighbors.length + 3
+ }
+ linkDirectionalParticleWidth={physics.particleWidth}
+ nodeCanvasObject={(node, ctx, globalScale) => {
+ if (physics.labels) {
+ if (globalScale > physics.labelScale || highlightNodes.has(node)) {
+ const label = node.title.substring(0, Math.min(node.title.length, 30))
+ const fontSize = 12 / globalScale
+ ctx.font = `${fontSize}px Sans-Serif`
+ const textWidth = ctx.measureText(label).width
+ const bckgDimensions = [textWidth * 1.1, fontSize].map((n) => n + fontSize * 0.5) // some padding
+ const fadeFactor = Math.min(
+ (3 * (globalScale - physics.labelScale)) / physics.labelScale,
+ 1,
+ )
+
+ ctx.fillStyle =
+ 'rgba(20, 20, 20, ' +
+ (highlightNodes.size === 0
+ ? 0.5 * fadeFactor
+ : highlightNodes.has(node)
+ ? 0.5
+ : 0.15 * fadeFactor) +
+ ')'
+ ctx.fillRect(
+ node.x - bckgDimensions[0] / 2,
+ node.y - bckgDimensions[1] / 2,
+ ...bckgDimensions,
+ )
+
+ ctx.textAlign = 'center'
+ ctx.textBaseline = 'middle'
+ ctx.fillStyle =
+ 'rgb(255, 255, 255, ' +
+ (highlightNodes.size === 0
+ ? fadeFactor
+ : highlightNodes.has(node)
+ ? 1
+ : 0.3 * fadeFactor) +
+ ')'
+ ctx.fillText(label, node.x, node.y)
+
+ node.__bckgDimensions = bckgDimensions // to re-use in nodePointerAreaPaint
+ }
+ }
+ }}
+ nodeCanvasObjectMode={() => 'after'}
+ onNodeHover={physics.hover ? handleNodeHover : null}
+ //onLinkHover={physics.hover ? handleLinkHover : null}
+ d3AlphaDecay={physics.alphaDecay}
+ d3AlphaMin={physics.alphaTarget}
+ d3VelocityDecay={physics.velocityDecay}
+ onBackgroundClick={handleBackgroundClick}
+ />
+ ) : (
+ <ForceGraph3D
+ ref={fgRef}
+ autoPauseRedraw={false}
+ graphData={gData}
+ //graphData={physics.collapse ? prunedTree : gData}
+ nodeAutoColorBy={physics.colorful ? 'id' : undefined}
+ nodeColor={
+ !physics.colorful
+ ? (node) => {
+ if (highlightNodes.size === 0) {
+ return 'rgb(100, 100, 100, 1)'
+ } else {
+ return highlightNodes.has(node) ? 'purple' : 'rgb(50, 50, 50, 0.5)'
+ }
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ : undefined
+ }
+ linkAutoColorBy={physics.colorful ? 'target' : undefined}
+ //linkAutoColorBy={(d) => gData.nodes[d.source].id % GROUPS}
+ linkColor={
+ !physics.colorful
+ ? (link) => {
+ if (highlightLinks.size === 0) {
+ return 'rgb(50, 50, 50, 0.8)'
+ } else {
+ return highlightLinks.has(link) ? 'purple' : 'rgb(50, 50, 50, 0.2)'
+ }
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ : undefined
+ //highlightLinks.has(link) ? "purple" : "grey"
+ // !node.childLinks.length ? "green" : node.collapsed ? "red" : "yellow"
+ }
+ linkDirectionalParticles={physics.particles}
+ //onNodeClick={!physics.collapse ? null : handleNodeClick}
+ nodeLabel={(node) => node.title}
+ //nodeVal ={(node)=> node.childLinks.length * 0.5 + 1}
+ //d3VelocityDecay={visco}
+ linkWidth={(link) =>
+ highlightLinks.has(link) ? 3 * physics.linkWidth : physics.linkWidth
+ }
+ linkOpacity={physics.linkOpacity}
+ nodeRelSize={physics.nodeRel}
+ nodeVal={(node) =>
+ highlightNodes.has(node) ? node.neighbors.length + 5 : node.neighbors.length + 3
+ }
+ linkDirectionalParticleWidth={physics.particleWidth}
+ onNodeHover={physics.hover ? handleNodeHover : null}
+ //onLinkHover={physics.hover ? handleLinkHover : null}
+ d3AlphaDecay={physics.alphaDecay}
+ d3AlphaMin={physics.alphaTarget}
+ d3VelocityDecay={physics.velocityDecay}
+ nodeThreeObject={
+ !physics.labels
+ ? undefined
+ : (node) => {
+ if (highlightNodes.has(node)) {
+ console.log(node.title)
+ const sprite = new SpriteText(node.title.substring(0, 30))
+ console.log('didnt crash here 2')
+ sprite.color = '#ffffff'
+ sprite.textHeight = 8
+ return sprite
+ } else {
+ return undefined
+ }
+ }
+ }
+ nodeThreeObjectExtend={true}
+ />
+ )}
+ </View>
+ )
+})