diff --git a/simulation/public/texture/moon_texture.jpg b/simulation/public/texture/moon_texture.jpg new file mode 100644 index 0000000..81e639c --- /dev/null +++ b/simulation/public/texture/moon_texture.jpg Binary files differ diff --git a/simulation/src/App.css b/simulation/src/App.css new file mode 100644 index 0000000..22f82b6 --- /dev/null +++ b/simulation/src/App.css @@ -0,0 +1,4 @@ +body { + margin: 0; + overflow: hidden; +} \ No newline at end of file diff --git a/simulation/src/App.jsx b/simulation/src/App.jsx index 1123bb2..5aff3e2 100644 --- a/simulation/src/App.jsx +++ b/simulation/src/App.jsx @@ -1,195 +1,23 @@ -import React, { useRef, useState, useEffect } from "react"; -import { Canvas, useFrame, useThree } from "@react-three/fiber"; -import { TextureLoader } from "three/src/loaders/TextureLoader"; -import { useLoader } from "@react-three/fiber"; -import * as THREE from "three"; - -function Sun() { - const texture = useLoader(TextureLoader, "/texture/sun_texture.jpg"); - const ref = useRef(); - - useFrame(() => { - ref.current.rotation.y += 0.005; - }); - - return ( - - - - - ); -} - -function Earth({ G, resetTrigger }) { - const texture = useLoader(TextureLoader, "/texture/earth_texture.jpg"); - const ref = useRef(); - - const initialPos = [10, 0, 0]; - const initialVel = [0, 0, 10]; - - const pos = useRef([...initialPos]); - const vel = useRef([...initialVel]); - - const trailPoints = useRef([]); - const trailRef = useRef(); - - useEffect(() => { - // 位置・速度・軌跡をリセット - pos.current = [...initialPos]; - vel.current = [...initialVel]; - trailPoints.current = []; - - if (ref.current) { - ref.current.position.set(...initialPos); - ref.current.rotation.set(0, 0, 0); - } - if (trailRef.current) { - trailRef.current.geometry.setDrawRange(0, 0); - } - }, [G, resetTrigger]); - - const M = 1000; - const dt = 0.01; // タイムスケールなし固定 - - useFrame(() => { - const rVec = [-pos.current[0], -pos.current[1], -pos.current[2]]; - const dist = Math.sqrt( - rVec[0] ** 2 + rVec[1] ** 2 + rVec[2] ** 2 - ); - - const acc = [ - (G * M * rVec[0]) / dist ** 3, - (G * M * rVec[1]) / dist ** 3, - (G * M * rVec[2]) / dist ** 3, - ]; - - vel.current[0] += acc[0] * dt; - vel.current[1] += acc[1] * dt; - vel.current[2] += acc[2] * dt; - - pos.current[0] += vel.current[0] * dt; - pos.current[1] += vel.current[1] * dt; - pos.current[2] += vel.current[2] * dt; - - if (ref.current) { - ref.current.position.set(...pos.current); - ref.current.rotation.y += 0.01; - } - - trailPoints.current.push(new THREE.Vector3(...pos.current)); - if (trailPoints.current.length > 200) trailPoints.current.shift(); - - if (trailRef.current) { - const positions = new Float32Array(trailPoints.current.length * 3); - trailPoints.current.forEach((v, i) => { - positions[i * 3] = v.x; - positions[i * 3 + 1] = v.y; - positions[i * 3 + 2] = v.z; - }); - trailRef.current.geometry.setAttribute( - "position", - new THREE.BufferAttribute(positions, 3) - ); - trailRef.current.geometry.setDrawRange(0, trailPoints.current.length); - trailRef.current.geometry.attributes.position.needsUpdate = true; - } - }); - - return ( - <> - - - - - - - - - - - ); -} - -function CameraController() { - const { camera } = useThree(); - - useEffect(() => { - camera.position.set(18, 8, 8); - camera.lookAt(0, 0, 0); - }, [camera]); - - return null; -} +import React, { useState } from "react"; +import { Canvas } from "@react-three/fiber"; +import CameraController from "./components/CameraController"; +import Sun from "./components/Sun"; +import Earth from "./components/Earth"; +import UI from "./components/UI"; +import "./App.css"; export default function App() { const [G, setG] = useState(1.0); const [resetKey, setResetKey] = useState(0); const handleReset = () => { - setG(1.0); // Gを1に戻す - setResetKey((k) => k + 1); // リセットトリガー更新 + setG(1.0); + setResetKey((k) => k + 1); }; return ( <> -
- - -
-
- - -
- + { + camera.position.set(18, 8, 8); // 全体を俯瞰する位置 + camera.lookAt(0, 0, 0); + }, [camera]); + + return null; +} diff --git a/simulation/src/components/Earth.jsx b/simulation/src/components/Earth.jsx new file mode 100644 index 0000000..ae6d1e6 --- /dev/null +++ b/simulation/src/components/Earth.jsx @@ -0,0 +1,95 @@ +import React, { useRef, useEffect } from "react"; +import { useLoader, useFrame } from "@react-three/fiber"; +import { TextureLoader } from "three"; +import * as THREE from "three"; + +export default function Earth({ G, resetTrigger }) { + const texture = useLoader(TextureLoader, "/texture/earth_texture.jpg"); + const ref = useRef(); + + const initialPos = [10, 0, 0]; + const initialVel = [0, 0, 10]; + + const pos = useRef([...initialPos]); + const vel = useRef([...initialVel]); + + const trailPoints = useRef([]); + const trailRef = useRef(); + + useEffect(() => { + pos.current = [...initialPos]; + vel.current = [...initialVel]; + trailPoints.current = []; + + if (ref.current) { + ref.current.position.set(...initialPos); + ref.current.rotation.set(0, 0, 0); + } + if (trailRef.current) { + trailRef.current.geometry.setDrawRange(0, 0); + } + }, [G, resetTrigger]); + + const M = 1000; + const dt = 0.01; + + useFrame(() => { + const rVec = [-pos.current[0], -pos.current[1], -pos.current[2]]; + const dist = Math.sqrt(rVec[0] ** 2 + rVec[1] ** 2 + rVec[2] ** 2); + + const acc = [ + (G * M * rVec[0]) / dist ** 3, + (G * M * rVec[1]) / dist ** 3, + (G * M * rVec[2]) / dist ** 3, + ]; + + vel.current[0] += acc[0] * dt; + vel.current[1] += acc[1] * dt; + vel.current[2] += acc[2] * dt; + + pos.current[0] += vel.current[0] * dt; + pos.current[1] += vel.current[1] * dt; + pos.current[2] += vel.current[2] * dt; + + if (ref.current) { + ref.current.position.set(...pos.current); + ref.current.rotation.y += 0.01; + } + + trailPoints.current.push(new THREE.Vector3(...pos.current)); + if (trailPoints.current.length > 200) trailPoints.current.shift(); + + if (trailRef.current) { + const positions = new Float32Array(trailPoints.current.length * 3); + trailPoints.current.forEach((v, i) => { + positions[i * 3] = v.x; + positions[i * 3 + 1] = v.y; + positions[i * 3 + 2] = v.z; + }); + trailRef.current.geometry.setAttribute( + "position", + new THREE.BufferAttribute(positions, 3) + ); + trailRef.current.geometry.setDrawRange(0, trailPoints.current.length); + trailRef.current.geometry.attributes.position.needsUpdate = true; + } + }); + + return ( + <> + + + + + + + + + + + ); +} diff --git a/simulation/src/components/Sun.jsx b/simulation/src/components/Sun.jsx new file mode 100644 index 0000000..fd4dc39 --- /dev/null +++ b/simulation/src/components/Sun.jsx @@ -0,0 +1,23 @@ +import React, { useRef } from "react"; +import { useLoader, useFrame } from "@react-three/fiber"; +import { TextureLoader } from "three"; + +export default function Sun() { + const texture = useLoader(TextureLoader, "/texture/sun_texture.jpg"); + const ref = useRef(); + + useFrame(() => { + ref.current.rotation.y += 0.005; + }); + + return ( + + + + + ); +} diff --git a/simulation/src/components/UI.css b/simulation/src/components/UI.css new file mode 100644 index 0000000..f807282 --- /dev/null +++ b/simulation/src/components/UI.css @@ -0,0 +1,29 @@ +.ui-panel { + position: absolute; + top: 10px; + left: 10px; + z-index: 1; + background: rgba(0, 0, 0, 0.6); + color: white; + padding: 15px; + border-radius: 8px; + width: 260px; + font-family: Arial, sans-serif; + user-select: none; +} + +.ui-panel input[type="range"] { + width: 100%; +} + +.ui-panel button { + width: 100%; + padding: 8px 0; + border-radius: 5px; + border: none; + background-color: #ff6600; + color: white; + font-weight: bold; + cursor: pointer; + font-size: 16px; +} diff --git a/simulation/src/components/UI.jsx b/simulation/src/components/UI.jsx new file mode 100644 index 0000000..3bc7670 --- /dev/null +++ b/simulation/src/components/UI.jsx @@ -0,0 +1,26 @@ +import React from "react"; +import "./UI.css"; + +export default function UI({ G, setG, handleReset }) { + return ( +
+ + +
+
+ + +
+ ); +}