From 14498d7753d0b39ba167d6e51a1f1c05e686d603 Mon Sep 17 00:00:00 2001
From: "Thomas F. K. Jorna" <thomasfkjorna@gmail.com>
Date: Thu, 22 Jul 2021 14:03:11 +0200
Subject: very timely commit of new tweak interface

---
 pages/index.tsx | 452 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 448 insertions(+), 4 deletions(-)

(limited to 'pages/index.tsx')

diff --git a/pages/index.tsx b/pages/index.tsx
index fb15ddc..dcdb225 100644
--- a/pages/index.tsx
+++ b/pages/index.tsx
@@ -9,6 +9,42 @@ import { GraphData, NodeObject } from 'force-graph'
 
 import { useWindowSize } from '@react-hook/window-size'
 
+import {
+  Accordion,
+  AccordionButton,
+  AccordionItem,
+  AccordionIcon,
+  AccordionPanel,
+  Text,
+  Heading,
+  VStack,
+  StackDivider,
+  Button,
+  CloseButton,
+  Slider,
+  SliderThumb,
+  SliderTrack,
+  SliderFilledTrack,
+  Switch,
+  FormControl,
+  FormLabel,
+  Box,
+  Container,
+  Icon,
+  IconButton,
+  Tooltip,
+  Menu,
+  MenuList,
+  MenuButton,
+  MenuItem,
+  MenuGroup,
+  MenuDivider,
+  MenuOptionGroup,
+  MenuItemOption,
+} from '@chakra-ui/react'
+
+import { InfoOutlineIcon, RepeatClockIcon, ChevronDownIcon } from '@chakra-ui/icons'
+
 // react-force-graph fails on import when server-rendered
 // https://github.com/vasturiano/react-force-graph/issues/155
 const ForceGraph2D = (
@@ -19,6 +55,7 @@ export type NodeById = { [nodeId: string]: OrgRoamNode | undefined }
 export type LinksByNodeId = { [nodeId: string]: OrgRoamLink[] | undefined }
 
 const initialPhysics = {
+  enabled: true,
   charge: -350,
   collision: true,
   linkStrength: 0.1,
@@ -30,15 +67,18 @@ const initialPhysics = {
   nodeRel: 4,
   labels: true,
   labelScale: 1.5,
-  alphaDecay: 0.16,
+  alphaDecay: 0.02,
   alphaTarget: 0,
+  alphaMin: 0,
   velocityDecay: 0.25,
   gravity: 0.5,
   gravityOn: true,
-  hover: true,
   colorful: true,
   galaxy: true,
-  rootId: 0,
+  ticks: 1,
+  hover: 'highlight',
+  click: 'select',
+  doubleClick: 'local',
 }
 
 const initialTheme = {
@@ -127,6 +167,14 @@ export function GraphPage() {
 
   return (
     <div>
+      <Tweaks
+        {...{
+          physics,
+          setPhysics,
+          threeDim,
+          local,
+        }}
+      />
       <Graph
         nodeById={nodeByIdRef.current!}
         linksByNodeId={linksByNodeIdRef.current!}
@@ -141,6 +189,402 @@ export function GraphPage() {
   )
 }
 
+export interface TweakProps {
+  physics: typeof initialPhysics
+  setPhysics: any
+  threeDim: boolean
+  local: boolean
+}
+
+export const Tweaks = function (props: TweakProps) {
+  const { physics, setPhysics, threeDim, local } = props
+
+  interface InfoTooltipProps {
+    infoText: string
+  }
+  const InfoTooltip = (props: InfoTooltipProps) => {
+    const { infoText } = props
+    return (
+      <Box paddingLeft="1">
+        <Tooltip label={infoText} placement="top" color="gray.100" bg="gray.800" hasArrow>
+          <InfoOutlineIcon />
+        </Tooltip>
+      </Box>
+    )
+  }
+
+  interface SliderWithInfo {
+    min: number
+    max: number
+    step: number
+    value: number
+    onChange: (arg0: number) => void
+    label: string
+    infoText: string
+  }
+
+  const SliderWithInfo = (props: SliderWithLabel) => {
+    const { min, max, step, value, onChange, label, infoText } = props
+    return (
+      <Box>
+        <Box display="flex" alignItems="center">
+          <Text>{label}</Text>
+          <InfoTooltip infoText={infoText} />
+        </Box>
+        <Slider value={value} onChange={onChange} min={min} max={max} step={step}>
+          <SliderTrack>
+            <SliderFilledTrack />
+          </SliderTrack>
+          <Tooltip label={value} color="gray.100" bg="gray.800">
+            <SliderThumb />
+          </Tooltip>
+        </Slider>
+      </Box>
+    )
+  }
+
+  interface SliderWithoutInfo {
+    min: number
+    max: number
+    step: number
+    val: number
+    change: (arg0: number) => void
+    label: string
+    physics: typeof initialPhysics
+    setPhysics: any
+  }
+
+  const SliderWithoutInfo = (props: SliderWithoutInfo) => {
+    const { min, max, step, physics, setPhysics, label } = props
+    return (
+      <Box>
+        <Box display="flex">
+          <Text>{label}</Text>
+        </Box>
+        <Slider
+          value={physics.gravity}
+          onChange={(value) => setPhysics({ ...physics, gravity: value })}
+          min={min}
+          max={max}
+          step={step}
+          focusThumbOnChange={false}
+        >
+          <SliderTrack>
+            <SliderFilledTrack />
+          </SliderTrack>
+          <SliderThumb />
+        </Slider>
+      </Box>
+    )
+  }
+
+  return (
+    <Container zIndex="overlay" position="absolute" bg="white" w="xs">
+      <Box display="flex" justifyContent="flex-end">
+        <Tooltip label="Reset settings to defaults">
+          <IconButton
+            aria-label="Reset Defaults"
+            icon={<RepeatClockIcon />}
+            onClick={() => setPhysics(initialPhysics)}
+          />
+        </Tooltip>
+        <CloseButton />
+      </Box>
+      <Accordion allowMultiple defaultIndex={[0]} allowToggle>
+        <AccordionItem>
+          <AccordionButton display="flex" justifyContent="space-between">
+            <Box display="flex">
+              <AccordionIcon />
+              <Text>Physics</Text>
+            </Box>
+            <Switch
+              id="physicsOn"
+              onChange={() => setPhysics({ ...physics, enabled: !physics.enabled })}
+              isChecked={physics.enabled}
+            />
+          </AccordionButton>
+          <AccordionPanel>
+            <VStack
+              spacing={2}
+              justifyContent="flex-start"
+              divider={<StackDivider borderColor="gray.200" />}
+              align="stretch"
+            >
+              <Box>
+                <Box display="flex" justifyContent="space-between">
+                  <Text>Gravity</Text>
+                  <Switch
+                    id="physicsOn"
+                    onChange={() => setPhysics({ ...physics, gravityOn: !physics.gravityOn })}
+                    isChecked={physics.gravityOn}
+                  />
+                </Box>
+                {physics.gravityOn && (
+                  <Box>
+                    <Text>Strength</Text>
+                    <Slider
+                      value={physics.gravity}
+                      onChange={(value) => setPhysics({ ...physics, gravity: value })}
+                      min={0}
+                      max={1}
+                      step={0.01}
+                    >
+                      <SliderTrack>
+                        <SliderFilledTrack />
+                      </SliderTrack>
+                      <Tooltip label={physics.gravity}>
+                        <SliderThumb />
+                      </Tooltip>
+                    </Slider>
+                  </Box>
+                )}
+              </Box>
+              <SliderWithoutInfo
+                label="Test"
+                min={0}
+                max={1}
+                step={0.01}
+                physics={physics}
+                setPhysics={setPhysics}
+              />
+              <Box>
+                <Text>Repulsive Force</Text>
+                <Slider
+                  value={-physics.charge}
+                  onChange={(value) => setPhysics({ ...physics, charge: -value })}
+                  min={0}
+                  max={1000}
+                >
+                  <SliderTrack>
+                    <SliderFilledTrack />
+                  </SliderTrack>
+                  <Tooltip label={physics.charge}>
+                    <SliderThumb />
+                  </Tooltip>
+                </Slider>
+              </Box>
+              <Box>
+                <Box display="flex" justifyContent="space-between">
+                  <Box display="flex" alignItems="center">
+                    <Text>Collision</Text>
+                    <InfoTooltip infoText="This is rather slow, disable if experiencing performance issues" />
+                  </Box>
+                  <Switch
+                    id="physicsCollision"
+                    onChange={() => setPhysics({ ...physics, collision: !physics.collision })}
+                    isChecked={physics.collision}
+                  />
+                </Box>
+                {physics.collision && (
+                  <Box>
+                    <Text>Strength</Text>
+                    <Slider
+                      value={physics.collisionStrength}
+                      onChange={(value) => setPhysics({ ...physics, collisionStrength: value })}
+                      min={0}
+                      max={2}
+                    >
+                      <SliderTrack>
+                        <SliderFilledTrack />
+                      </SliderTrack>
+                      <Tooltip label={physics.collisionStrength}>
+                        <SliderThumb />
+                      </Tooltip>
+                    </Slider>
+                  </Box>
+                )}
+              </Box>
+              <Box>
+                <Text>Link Strength</Text>
+                <Slider
+                  value={physics.linkStrength}
+                  onChange={(value) => setPhysics({ ...physics, linkStrength: value })}
+                  min={0}
+                  max={2}
+                  step={0.01}
+                >
+                  <SliderTrack>
+                    <SliderFilledTrack />
+                  </SliderTrack>
+                  <Tooltip label={physics.linkStrength}>
+                    <SliderThumb />
+                  </Tooltip>
+                </Slider>
+              </Box>
+              <StackDivider bg="grey.200" />
+              <Box>
+                <Box display="flex" alignItems="center">
+                  <Text>Link Iterations</Text>
+                  <InfoTooltip infoText="How many links down the line the physics of a single node effects (Slow)" />
+                </Box>
+                <Slider
+                  value={physics.linkIts}
+                  onChange={(value) => setPhysics({ ...physics, linkIts: value })}
+                  min={0}
+                  max={6}
+                  step={1}
+                >
+                  <SliderTrack>
+                    <SliderFilledTrack />
+                  </SliderTrack>
+                  <Tooltip label={physics.linkIts}>
+                    <SliderThumb />
+                  </Tooltip>
+                </Slider>
+              </Box>
+              <Box>
+                <Text>Viscosity</Text>
+                <Slider
+                  value={physics.velocityDecay}
+                  onChange={(value) => setPhysics({ ...physics, velocityDecay: value })}
+                  min={0}
+                  max={1}
+                  step={0.01}
+                >
+                  <SliderTrack>
+                    <SliderFilledTrack />
+                  </SliderTrack>
+                  <Tooltip label={physics.velocityDecay}>
+                    <SliderThumb />
+                  </Tooltip>
+                </Slider>
+              </Box>
+            </VStack>
+            <Box>
+              <Accordion allowToggle>
+                <AccordionItem>
+                  <AccordionButton>
+                    <Text>Advanced</Text>
+                    <AccordionIcon />
+                  </AccordionButton>
+                  <AccordionPanel>
+                    <VStack
+                      spacing={2}
+                      justifyContent="flex-start"
+                      divider={<StackDivider borderColor="gray.200" />}
+                      align="stretch"
+                    >
+                      <Box>
+                        <Text>Simulation Ticks</Text>
+                        <Slider value={0} min={0} max={1} step={0.01}>
+                          <SliderTrack>
+                            <SliderFilledTrack />
+                          </SliderTrack>
+                          <Tooltip label={physics.ticks}>
+                            <SliderThumb />
+                          </Tooltip>
+                        </Slider>
+                      </Box>
+                      <Box>
+                        <Text>Alpha Decay</Text>
+                        <Slider
+                          value={physics.alphaDecay}
+                          onChange={(value) => setPhysics({ ...physics, alphaDecay: value })}
+                          min={0}
+                          max={1}
+                          step={0.01}
+                        >
+                          <SliderTrack>
+                            <SliderFilledTrack />
+                          </SliderTrack>
+                          <Tooltip label={physics.alphaDecay}>
+                            <SliderThumb />
+                          </Tooltip>
+                        </Slider>
+                      </Box>
+                      <Text>Alpha Minimum</Text>
+                      <Slider
+                        value={physics.alphaMin}
+                        onChange={(value) => setPhysics({ ...physics, alphaMin: value })}
+                        min={0.01}
+                        max={1}
+                        step={0.01}
+                      >
+                        <SliderTrack>
+                          <SliderFilledTrack />
+                        </SliderTrack>
+                        <Tooltip label={physics.alphaMin}>
+                          <SliderThumb />
+                        </Tooltip>
+                      </Slider>
+                    </VStack>
+                  </AccordionPanel>
+                </AccordionItem>
+              </Accordion>
+            </Box>
+            {/* </VStack> */}
+          </AccordionPanel>
+        </AccordionItem>
+        <AccordionItem>
+          <AccordionButton>
+            <AccordionIcon />
+            Visual
+          </AccordionButton>
+          <AccordionPanel>
+            <VStack
+              spacing={2}
+              justifyContent="flex-start"
+              divider={<StackDivider borderColor="gray.200" />}
+              align="stretch"
+            >
+              <Text>Ayyyy</Text>
+            </VStack>
+          </AccordionPanel>
+        </AccordionItem>
+        <AccordionItem>
+          <AccordionButton>
+            <AccordionIcon />
+            Behavior
+          </AccordionButton>
+          <AccordionPanel>
+            <VStack
+              spacing={2}
+              justifyContent="flex-start"
+              divider={<StackDivider borderColor="gray.200" />}
+              align="stretch"
+            >
+              <Box display="flex" justifyContent="space-between" alignItems="center">
+                <Text>Hover Higlight</Text>
+                <Menu>
+                  <MenuButton as={Button} rightIcon={<ChevronDownIcon />}>
+                    {physics.hover}
+                  </MenuButton>
+                  <MenuList>
+                    <MenuItem>Off</MenuItem>
+                    <MenuItem>On</MenuItem>
+                  </MenuList>
+                </Menu>
+              </Box>
+              <Box display="flex" justifyContent="space-between">
+                <Text>Click</Text>
+              </Box>
+              <Box display="flex" justifyContent="space-between">
+                <Text>Double-click</Text>
+              </Box>
+            </VStack>
+          </AccordionPanel>
+        </AccordionItem>
+        <AccordionItem>
+          <AccordionButton>
+            <AccordionIcon />
+            Visual
+          </AccordionButton>
+          <AccordionPanel>
+            <VStack
+              spacing={2}
+              justifyContent="flex-start"
+              divider={<StackDivider borderColor="gray.200" />}
+              align="stretch"
+            >
+              <Text>Ayyyy</Text>
+            </VStack>
+          </AccordionPanel>
+        </AccordionItem>
+      </Accordion>
+    </Container>
+  )
+}
+
 export interface GraphProps {
   nodeById: NodeById
   linksByNodeId: LinksByNodeId
@@ -360,7 +804,7 @@ export const Graph = function (props: GraphProps) {
         }}
         nodeCanvasObjectMode={() => 'after'}
         d3AlphaDecay={physics.alphaDecay}
-        d3AlphaMin={physics.alphaTarget}
+        d3AlphaMin={physics.alphaMin}
         d3VelocityDecay={physics.velocityDecay}
         backgroundColor={'#242730'}
         onNodeClick={selectClick}
-- 
cgit v1.2.3

