Skip to content

Commit

Permalink
Added animation loop for ship
Browse files Browse the repository at this point in the history
  • Loading branch information
Pipe-Runner committed Mar 26, 2023
1 parent aebe59d commit d59cf6a
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 82 deletions.
64 changes: 41 additions & 23 deletions src/components/EditorPanel/EditorPanel.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
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";
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: {
Expand All @@ -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);
}
};

Expand Down Expand Up @@ -69,29 +83,33 @@ function EditorPanel() {
<div className="h-[1px] w-auto mx-2 bg-gray-300" />

<div className="flex flex-col flex-1 p-2 space-y-2 overflow-auto">
<div className="flex flex-col p-2 space-y-2 border border-gray-400 border-solid rounded-md min-h-[60%]">
<div className="flex flex-col p-2 space-y-2 border border-gray-400 border-solid rounded-md">
<div className="flex items-center justify-center w-full py-1 rounded-sm bg-violet-300">
Simulation Controls
</div>
<div className="flex items-center justify-center flex-1">
{/* {heatData.length > 0 ? (
<Heatmap
xField="x"
yField="z"
colorField="value"
data={heatData}
{...heatConfig}
/>
) : (
<div>
{simulationState === "play"
? "Collecting data..."
: "Start computation to collect data"}
</div>
)} */}
<div className="">
<Input
value={force.x}
onChange={(value) => setForce({ x: value, ...force })}
/>

<Input
value={force.y}
onChange={(value) => setForce({ y: value, ...force })}
/>
</div>
<SimulationControls computeSimulation={computeSimulation} />
</div>

<div>
<button
onClick={() => {
setAnimating(true);
}}
>
{animating ? "Stop" : "Play"}
</button>
</div>
</div>
</motion.div>
);
Expand Down
11 changes: 11 additions & 0 deletions src/components/Input/Input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function Input({ value, onChange, placeholder }) {
return (
<input
type="number"
value={value}
onChange={(event) => {
onChange(event.target.value);
}}
></input>
);
}
1 change: 1 addition & 0 deletions src/components/Input/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./Input";
54 changes: 54 additions & 0 deletions src/objects/Cameras/Cameras.js
Original file line number Diff line number Diff line change
@@ -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 (
<>
<PerspectiveCamera
// ref={defaultCameraRef}
position={[45, 26, 45]}
fov={35}
far={500}
near={1}
makeDefault={true}
/>
<OrthographicCamera
left={_viewport.left}
right={_viewport.right}
top={_viewport.top}
bottom={_viewport.bottom}
zoom={2.5}
position={[0, 8, 0]}
far={9}
near={1}
ref={topDownCameraRef}
// makeDefault={true}
/>
</>
);
}
1 change: 1 addition & 0 deletions src/objects/Cameras/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {default} from './Cameras';
40 changes: 3 additions & 37 deletions src/objects/Lights/Lights.js
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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}
>
<orthographicCamera
top={cameraFrustumTop}
left={cameraFrustumLeft}
bottom={cameraFrustumBottom}
right={cameraFrustumRight}
ref={shadowCameraRef}
near={cameraNear}
far={cameraFar}
attach="shadow-camera"
/>
</directionalLight>
/>
</>
);
}
Expand Down
39 changes: 35 additions & 4 deletions src/objects/Ship/Ship.js
Original file line number Diff line number Diff line change
@@ -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 <primitive scale={0.1} object={gltf.scene} />;
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 <primitive ref={shipRef} scale={0.1} object={gltf.scene} />;
}
19 changes: 13 additions & 6 deletions src/python/simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = []
index_skip = 4
18 changes: 6 additions & 12 deletions src/screens/Home/Home.js
Original file line number Diff line number Diff line change
@@ -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 && (
Expand All @@ -25,14 +24,9 @@ function Home() {
className="relative w-full h-full overflow-hidden"
>
<Canvas shadows>
<PerspectiveCamera
ref={defaultCameraRef}
position={[45, 26, 45]}
fov={35}
far={500}
near={1}
makeDefault
/>

<Cameras />
<Lights />
<Scene />

{/* Utils */}
Expand Down
8 changes: 8 additions & 0 deletions src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

0 comments on commit d59cf6a

Please sign in to comment.