From 418078bf680860b265c01c8c2231d2347439cb72 Mon Sep 17 00:00:00 2001 From: Fabian Zills <46721498+PythonFZ@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:23:00 +0200 Subject: [PATCH] Improved Plotting (#614) * new plotting draft * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * duplicate button, snap to grid * working version of multiple plots * show plots * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * support upload * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix analysis methods * update analysis tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * support selected frames * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * sort, unique and log selected ids * update progress bar viewer * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * improve UI looks * use set * reset selection * fix bookmarks * improve plottly hover experience * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * allow for default plot size * react state bugfix * show SOME plot when running * add `ShowPlotsWindow` * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add missing tooltip * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * bugfix and UI improvement * add controls crosshair * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * split progress bar into multiple pars * remove iteration over all positions * performance improvements * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * make handle draggable * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add plots window button * remove showplots python code * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * limit max and minimum frame for dragging * improve progress bar style * add allowDrag button * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * use `bg-secondary` instead of `bg-dark` * toggle frame * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * full plots support * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- README.md | 2 +- app/src/App.css | 59 ++ app/src/App.tsx | 105 ++- app/src/components/headbar.tsx | 43 +- app/src/components/particles.tsx | 35 +- app/src/components/plotting.tsx | 265 ++++++++ app/src/components/progressbar.tsx | 407 ++++++++--- app/src/components/sidebar.tsx | 178 +---- app/src/components/utils.tsx | 6 + poetry.lock | 1001 +++++++++++++++------------- pyproject.toml | 2 +- tests/test_analysis.py | 16 +- zndraw/analyse/__init__.py | 8 +- zndraw/base.py | 6 +- zndraw/cli.py | 8 +- zndraw/scene.py | 5 + zndraw/server/events.py | 43 +- zndraw/tasks/__init__.py | 18 +- zndraw/upload.py | 8 +- zndraw/utils.py | 22 + zndraw/zndraw.py | 11 +- 21 files changed, 1472 insertions(+), 776 deletions(-) create mode 100644 app/src/components/plotting.tsx diff --git a/README.md b/README.md index a10e59880..2fd9d076c 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ You can modify various aspects of the visualization: - `vis.points` - `vis.selection` - `vis.step` -- `vis.figure` +- `vis.figures` - `vis.bookmarks` - `vis.geometries` diff --git a/app/src/App.css b/app/src/App.css index 420d56f9f..1ebb886e4 100644 --- a/app/src/App.css +++ b/app/src/App.css @@ -81,3 +81,62 @@ height: 100%; border: none; } +:root { + --handle-size: 12px; /* Base size for the handle */ + --handle-color: rgb(48, 75, 183); /* Color of the handle */ +} + +.square { + width: var(--handle-size); + height: var(--handle-size); + background-color: var(--handle-color); /* Color of the square */ + border-left: calc(var(--handle-size) / 2) solid transparent; + border-right: calc(var(--handle-size) / 2) solid transparent; +} + +.triangle { + width: 0; + height: 0; + border-left: calc(var(--handle-size) / 2) solid transparent; + border-right: calc(var(--handle-size) / 2) solid transparent; + border-top: calc(var(--handle-size) / 2) solid var(--handle-color); /* Color of the triangle */ +} + +.handle { + position: absolute; + top: -15px; /* Adjust this value based on your design */ + transform: translateX(-50%); + display: flex; + align-items: center; + flex-direction: column; + z-index: 2; /* Ensure it is above other elements */ +} + +.progress-bar-v-line { + position: absolute; + top: 0; + left: 0; + transform: translateX(-50%); + width: 2px; /* Thickness of the line */ + height: 100%; /* Full height of the column */ + background-color: var(--handle-color); /* Color of the line */ + z-index: 1; /* Ensure it is above tiles but below the bookmark */ +} + +.progress-bar-tick-line { + position: absolute; + top: 0; + left: 0; + transform: translateX(-50%) translateY(-100%); + width: 1px; /* Thickness of the line */ + height: 14%; /* Size of the tick */ + z-index: 1; /* Ensure it is visible */ +} + +.progress-bar-bookmark { + position: absolute; + top: 0; + left: 0; + /* z-index: 1; */ + transform: translateX(-50%); +} diff --git a/app/src/App.tsx b/app/src/App.tsx index d0cc2545f..a2fab4485 100644 --- a/app/src/App.tsx +++ b/app/src/App.tsx @@ -20,13 +20,15 @@ import { import { Frames, Frame, Player } from "./components/particles"; import { Geometries } from "./components/geometries"; import "./App.css"; +import { Plotting } from "./components/plotting"; -import { Canvas, useThree } from "@react-three/fiber"; +import { Canvas, useThree, useFrame } from "@react-three/fiber"; import { OrbitControls, PerspectiveCamera, TrackballControls, TransformControls, + Box, } from "@react-three/drei"; import { Button, InputGroup, Form } from "react-bootstrap"; import * as THREE from "three"; @@ -35,6 +37,53 @@ import ControlsBuilder from "./components/transforms"; import { ParticleInfoOverlay, SceneInfoOverlay } from "./components/overlays"; import VectorField from "./components/vectorfield"; import { useColorMode } from "./components/utils"; +import { IndicesState } from "./components/utils"; + +const MoveCameraTarget = ({ + controlsRef, + colorMode, +}: { + controlsRef: any; + colorMode: string; +}) => { + const controlsCrosshairRef = useRef(null); + const shortDimension = 0.05; + const longDimension = 0.5; + + // Update the controlsCrosshair position to match the orbit controls target + useFrame(() => { + if (controlsCrosshairRef.current && controlsRef.current) { + const crosshair = controlsCrosshairRef.current; + const target = controlsRef.current.target; + crosshair.position.copy(target); + } + }); + + return ( + + {/* X axis box */} + + + + + {/* Y axis box */} + + + + + {/* Z axis box */} + + + + + ); +}; export default function App() { // const [isConnected, setIsConnected] = useState(socket.connected); @@ -60,6 +109,10 @@ export default function App() { const [length, setLength] = useState(0); // updated via sockets const [step, setStep] = useState(0); + const [selectedFrames, setSelectedFrames] = useState({ + active: true, + indices: new Set(), + }); const [selectedIds, setSelectedIds] = useState>(new Set()); const [bookmarks, setBookmarks] = useState({}); // {name: [step, ...] const [points, setPoints] = useState([]); @@ -114,6 +167,7 @@ export default function App() { const [cursorPosition, setCursorPosition] = useState({ x: 0, y: 0 }); const [lineLength, setLineLength] = useState(0); const [showParticleInfo, setShowParticleInfo] = useState(false); + const [addPlotsWindow, setAddPlotsWindow] = useState(0); // external useEffects, should be disabled when // the input is received via sockets @@ -393,8 +447,18 @@ export default function App() { setStep(nextBookmark); } } else { - // Move to the next step, or wrap around to the start - setStep((prevStep) => (prevStep + 1 < length ? prevStep + 1 : 0)); + if (selectedFrames.indices.size > 0 && selectedFrames.active) { + const nextFrame = Array.from(selectedFrames.indices).find( + (frame) => frame > step, + ); + if (nextFrame) { + setStep(nextFrame); + } else { + setStep(Math.min(...selectedFrames.indices)); + } + } else { + setStep((prevStep) => (prevStep + 1 < length ? prevStep + 1 : 0)); + } } } else if (event.key === "ArrowLeft") { setPlaying(false); @@ -410,9 +474,22 @@ export default function App() { } } else { // Move to the previous step, or wrap around to the end - setStep((prevStep) => - prevStep - 1 >= 0 ? prevStep - 1 : length - 1, - ); + // check if selectedFrames length is greater than 0, then only jump + // between selectedFrames + if (selectedFrames.indices.size > 0 && selectedFrames.active) { + const previousFrame = Array.from(selectedFrames.indices) + .reverse() + .find((frame) => frame < step); + if (previousFrame) { + setStep(previousFrame); + } else { + setStep(Math.max(...selectedFrames.indices)); + } + } else { + setStep((prevStep) => + prevStep - 1 >= 0 ? prevStep - 1 : length - 1, + ); + } } } else if (event.key == "ArrowUp") { // jump 10 percent, or to the end @@ -425,6 +502,7 @@ export default function App() { const newStep = Math.max(step - Math.floor(length / 10), 0); setStep(newStep); } else if (event.key == " ") { + // backspace updateLength(); setPlaying((prev) => !prev); if (step == length - 1) { @@ -656,6 +734,12 @@ export default function App() { makeDefault /> )} + {roomConfig["scene"].crosshair && ( + + )} + {showParticleInfo && ( <> diff --git a/app/src/components/headbar.tsx b/app/src/components/headbar.tsx index 46aed4048..61839cf5f 100644 --- a/app/src/components/headbar.tsx +++ b/app/src/components/headbar.tsx @@ -41,7 +41,7 @@ import { FaPencil, } from "react-icons/fa6"; import { TbPlugConnected } from "react-icons/tb"; -import { MdExitToApp } from "react-icons/md"; +import { MdAddChart, MdExitToApp } from "react-icons/md"; function getServerUrl() { const { protocol, host } = window.location; @@ -347,6 +347,22 @@ const FileUpload = forwardRef((props, ref) => { ); }); +interface HeadBarProps { + room: string; + colorMode: string; + handleColorMode: any; + setIsDrawing: any; + setGeometries: any; + setPoints: any; + isDrawing: boolean; + tutorialURL: string; + showSiMGen: boolean; + modifierQueue: number; + isAuthenticated: boolean; + roomLock: boolean; + setAddPlotsWindow: any; +} + const HeadBar = ({ room, colorMode, @@ -360,20 +376,8 @@ const HeadBar = ({ modifierQueue, isAuthenticated, roomLock, -}: { - room: string; - colorMode: string; - handleColorMode: any; - setIsDrawing: any; - setGeometries: any; - setPoints: any; - isDrawing: boolean; - tutorialURL: string; - showSiMGen: boolean; - modifierQueue: number; - isAuthenticated: boolean; - roomLock: boolean; -}) => { + setAddPlotsWindow, +}: HeadBarProps) => { const [helpModalShow, setHelpModalShow] = useState(false); const [connectModalShow, setConnectModalShow] = useState(false); const [refreshModalShow, setRefreshModalShow] = useState(false); @@ -513,6 +517,15 @@ const HeadBar = ({ + + + + + + + +