From d59cf6acfcbb1f86d92fa2500ca303a14810e660 Mon Sep 17 00:00:00 2001 From: Pipe-Runner Date: Sun, 26 Mar 2023 22:40:30 +0200 Subject: [PATCH] Added animation loop for ship --- src/components/EditorPanel/EditorPanel.js | 64 +++++++++++++++-------- src/components/Input/Input.js | 11 ++++ src/components/Input/index.js | 1 + src/objects/Cameras/Cameras.js | 54 +++++++++++++++++++ src/objects/Cameras/index.js | 1 + src/objects/Lights/Lights.js | 40 ++------------ src/objects/Ship/Ship.js | 39 ++++++++++++-- src/python/simulator.py | 19 ++++--- src/screens/Home/Home.js | 18 +++---- src/store/index.js | 8 +++ 10 files changed, 173 insertions(+), 82 deletions(-) create mode 100644 src/components/Input/Input.js create mode 100644 src/components/Input/index.js create mode 100644 src/objects/Cameras/Cameras.js create mode 100644 src/objects/Cameras/index.js diff --git a/src/components/EditorPanel/EditorPanel.js b/src/components/EditorPanel/EditorPanel.js index 95669a4..3d224cf 100644 --- a/src/components/EditorPanel/EditorPanel.js +++ b/src/components/EditorPanel/EditorPanel.js @@ -1,5 +1,5 @@ import { motion, AnimatePresence } from "framer-motion"; -import React, { useContext } from "react"; +import React, { useContext, useState } from "react"; import useStore from "../../store"; import { HiOutlineMenu as OpenIcon } from "react-icons/hi"; import PrimaryHeader from "./components/PrimaryHeader"; @@ -7,6 +7,7 @@ import { PyodideContext } from "../../providers/Pyodide"; import script from "../../python/simulator.py"; import { extractScriptText } from "../../utils/script-text"; import SimulationControls from "./components/SimulationControls"; +import Input from "../Input/Input"; const menuVariants = { open: { @@ -20,20 +21,33 @@ const menuVariants = { function EditorPanel() { const isMenuOpen = useStore((state) => state.isMenuOpen); const setIsMenuOpen = useStore((state) => state.setIsMenuOpen); + const setSimulationData = useStore((state) => state.setSimulationData); + const setIndexSkip = useStore((state) => state.setIndexSkip); + const animating = useStore((state) => state.animating); + const setAnimating = useStore((state) => state.setAnimating); + + const [isComputing, setIsComputing] = useState(false); const { pyodide, isPyodideLoading } = useContext(PyodideContext); + const [force, setForce] = useState({ + x: 2, + y: -2, + }); + const computeSimulation = async () => { + setIsComputing(true); + if (!isPyodideLoading) { - window.simulator_input = { - x: 0, - y: 0, - }; + window.simulator_input = { force }; const code = await extractScriptText(script); await pyodide.runPythonAsync(code); - console.log(pyodide.globals.get("output").toJs()); + setSimulationData(pyodide.globals.get("state_time").toJs()); + setIndexSkip(pyodide.globals.get("index_skip")); + + setIsComputing(false); } }; @@ -69,29 +83,33 @@ function EditorPanel() {
-
+
Simulation Controls
-
- {/* {heatData.length > 0 ? ( - - ) : ( -
- {simulationState === "play" - ? "Collecting data..." - : "Start computation to collect data"} -
- )} */} +
+ setForce({ x: value, ...force })} + /> + + setForce({ y: value, ...force })} + />
+ +
+ +
); diff --git a/src/components/Input/Input.js b/src/components/Input/Input.js new file mode 100644 index 0000000..4e120ae --- /dev/null +++ b/src/components/Input/Input.js @@ -0,0 +1,11 @@ +export default function Input({ value, onChange, placeholder }) { + return ( + { + onChange(event.target.value); + }} + > + ); +} diff --git a/src/components/Input/index.js b/src/components/Input/index.js new file mode 100644 index 0000000..a50d7d1 --- /dev/null +++ b/src/components/Input/index.js @@ -0,0 +1 @@ +export { default } from "./Input"; diff --git a/src/objects/Cameras/Cameras.js b/src/objects/Cameras/Cameras.js new file mode 100644 index 0000000..3d36b91 --- /dev/null +++ b/src/objects/Cameras/Cameras.js @@ -0,0 +1,54 @@ +import { OrthographicCamera, PerspectiveCamera, useHelper } from "@react-three/drei"; +import React, { useEffect, useRef } from "react"; +import { CameraHelper } from "three"; + +var w = window.innerWidth; +var h = window.innerHeight; +var viewSize = 350; +var aspectRatio = w / h; + +const _viewport = { + left: (-aspectRatio * viewSize) / 2, + right: (aspectRatio * viewSize) / 2, + top: viewSize / 2, + bottom: -viewSize / 2, +}; + +console.log(_viewport); + +export default function Cameras() { + const topDownCameraRef = useRef(null); + + // useHelper(topDownCameraRef && topDownCameraRef, CameraHelper); + + useEffect(() => { + if (topDownCameraRef.current != null) { + topDownCameraRef.current.lookAt(0, 0, 0); + } + }, []); + + return ( + <> + + + + ); +} diff --git a/src/objects/Cameras/index.js b/src/objects/Cameras/index.js new file mode 100644 index 0000000..2a77d1b --- /dev/null +++ b/src/objects/Cameras/index.js @@ -0,0 +1 @@ +export {default} from './Cameras'; \ No newline at end of file diff --git a/src/objects/Lights/Lights.js b/src/objects/Lights/Lights.js index 4765da6..55e1c3a 100644 --- a/src/objects/Lights/Lights.js +++ b/src/objects/Lights/Lights.js @@ -1,35 +1,16 @@ import { useHelper } from "@react-three/drei"; import { useControls } from "leva"; import React, { useMemo, useRef } from "react"; -import { CameraHelper, DirectionalLightHelper, Object3D } from "three"; +import { DirectionalLightHelper, Object3D } from "three"; function Lights() { const directionalLightRef = useRef(null); - const shadowCameraRef = useRef(null); - const { - position, - cameraFrustumTop, - cameraFrustumBottom, - cameraFrustumLeft, - cameraFrustumRight, - shouldShowHelper, - cameraNear, - cameraFar, - } = useControls("Sun", { - position: { value: [24, 24, 24] }, - cameraFrustumTop: { value: 40 }, - cameraFrustumBottom: { value: -40 }, - cameraFrustumLeft: { value: 40 }, - cameraFrustumRight: { value: -40 }, - cameraNear: { value: 10 }, - cameraFar: { value: 74 }, - shadowBias: { value: -0.001, max: 0, min: -0.1 }, + const { position, shouldShowHelper } = useControls("Sun", { shouldShowHelper: { value: false, label: "Debug" }, }); useHelper(shouldShowHelper && directionalLightRef, DirectionalLightHelper); - useHelper(shouldShowHelper && shadowCameraRef, CameraHelper); const target = useMemo(() => { const target = new Object3D(); @@ -46,22 +27,7 @@ function Lights() { color="white" position={position} target={target} - castShadow - shadow-bias={-0.004} - shadow-mapSize-height={1024} - shadow-mapSize-width={1024} - > - - + /> ); } diff --git a/src/objects/Ship/Ship.js b/src/objects/Ship/Ship.js index 22ab86d..ab20563 100644 --- a/src/objects/Ship/Ship.js +++ b/src/objects/Ship/Ship.js @@ -1,8 +1,39 @@ import shipModel from "../../assets/ship.gltf"; -import { useLoader } from '@react-three/fiber' -import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader' +import { useFrame, useLoader } from "@react-three/fiber"; +import { useRef } from "react"; +import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"; +import useStore from "../../store"; export default function Ship() { - const gltf = useLoader(GLTFLoader, shipModel) - return ; + const shipRef = useRef(); + const dataCounterIndex = useRef(0); + const gltf = useLoader(GLTFLoader, shipModel); + + const simulationData = useStore((state) => state.simulationData); + const indexSkip = useStore((state) => state.indexSkip); + const animating = useStore((state) => state.animating); + const setAnimating = useStore((state) => state.setAnimating); + + useFrame(() => { + if (animating && simulationData && indexSkip) { + const idx = indexSkip * dataCounterIndex.current; + + if (idx < simulationData.length) { + shipRef.current.position.x = simulationData[idx + 0] * 50; + shipRef.current.position.z = simulationData[idx + 1] * 50; + shipRef.current.rotation.y = simulationData[idx + 2]; + + console.log("shipRef.current.position.x", shipRef.current.position.x); + console.log("shipRef.current.position.z", shipRef.current.position.z); + console.log("shipRef.current.rotation.y", shipRef.current.rotation.y); + + dataCounterIndex.current += 1; + } else { + setAnimating(false); + dataCounterIndex.current = 0; + } + } + }); + + return ; } diff --git a/src/python/simulator.py b/src/python/simulator.py index be3e7a6..14c21f8 100644 --- a/src/python/simulator.py +++ b/src/python/simulator.py @@ -2,8 +2,8 @@ from numpy.linalg import inv from js import window -x_1 = window.simulator_input.x -x_2 = window.simulator_input.y +force_x = window.simulator_input.force.x +force_y = window.simulator_input.force.y def rk4_integrator(x_0, t_0, t_n, dt, dx, u): x = x_0 @@ -113,15 +113,22 @@ def dx(X, t = None, U = np.array([0, 0])): F_v = 1 ''' X_0 = np.array([0, 0, 0, 0, 0, 0]) -U = [1, -1] +U = [force_x, -force_y] t_0 = 0 T = 20 -dt = 0.1 +dt = 0.06 state_time = [] state_integrator = rk4_integrator(X_0, t_0, T, dt, dx, np.array(U)) for x, t in state_integrator: - state_time.append({"Position (X)": x[0], "Position (Y)": x[1], "Position (Sai)": x[2], "Vel (u)": x[3], "Vel (v)": x[4], "yaw rate (r)": x[5], "Time": t}) + # state_time.append({"Position (X)": x[0], "Position (Y)": x[1], "Position (Sai)": x[2], "Vel (u)": x[3], "Vel (v)": x[4], "yaw rate (r)": x[5], "Time": t}) + # state_time.append({"Position (X)": x[0], "Position (Y)": x[1], "Position (Sai)": x[2], "Time": t}) + state_time.extend([ + x[0], + x[1], + x[2], + t + ]) -output = [] \ No newline at end of file +index_skip = 4 \ No newline at end of file diff --git a/src/screens/Home/Home.js b/src/screens/Home/Home.js index 0a8ec4e..2d8c1c4 100644 --- a/src/screens/Home/Home.js +++ b/src/screens/Home/Home.js @@ -1,18 +1,17 @@ -import React, { useContext, useRef } from "react"; +import React, { useContext } from "react"; import { Canvas } from "@react-three/fiber"; import Gizmo from "../../objects/Gizmo"; import Controls from "../../objects/Controls"; import EditorPanel from "../../components/EditorPanel"; -import { PerspectiveCamera } from "@react-three/drei"; import Scene from "../../objects/Scene"; import { Leva } from "leva"; import { PyodideContext } from "../../providers/Pyodide"; +import Lights from "../../objects/Lights"; +import Cameras from "../../objects/Cameras"; function Home() { const { isPyodideLoading } = useContext(PyodideContext); - const defaultCameraRef = useRef(null); - return ( <> {isPyodideLoading && ( @@ -25,14 +24,9 @@ function Home() { className="relative w-full h-full overflow-hidden" > - + + + {/* Utils */} diff --git a/src/store/index.js b/src/store/index.js index 4de1db2..41d5672 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -8,6 +8,14 @@ const useStore = create((set) => ({ : set((_) => ({ isMenuOpen: isOpen, })), + isComputing: false, + setIsComputing: (isComputing) => set({ isComputing }), + simulationData: null, + setSimulationData: (simulationData) => set({ simulationData }), + indexSkip: null, + setIndexSkip: (indexSkip) => set({ indexSkip }), + animating: false, + setAnimating: (animating) => set({ animating }), })); export default useStore;