ローレンツ・アトラクターを3Dでプロットし、その空間内をカメラが飛び回る視点でアニメーションを行ってます。
import os
import webbrowser
from http.server import SimpleHTTPRequestHandler, HTTPServer
import threading
# HTMLコードを生成
html_content = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lorenz Attractor in 3D</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// シーン、カメラ、レンダラーの設定
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// ローレンツアトラクターのパラメータと初期条件
const sigma = 10, rho = 28, beta = 8 / 3;
const dt = 0.01; // 時間刻み
let x = 0.1, y = 0.1, z = 0.1; // 初期条件
const points = []; // 点の配列
// ローレンツアトラクターの点を生成
for (let i = 0; i < 10000; i++) {
const dx = sigma * (y - x) * dt;
const dy = (x * (rho - z) - y) * dt;
const dz = (x * y - beta * z) * dt;
x += dx;
y += dy;
z += dz;
points.push(new THREE.Vector3(x, y, z)); // 点を追加
}
// ジオメトリとマテリアルを作成し、点群を作成
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.PointsMaterial({ color: 0xff0000, size: 0.1 });
const lorenzAttractor = new THREE.Points(geometry, material);
scene.add(lorenzAttractor); // シーンに追加
// カメラの初期位置とアニメーションの設定
camera.position.set(0, 0, 50); // カメラの初期位置
const cameraSpeed = 0.02; // カメラの移動速度
// アニメーションループ
function animate() {
requestAnimationFrame(animate);
// ローレンツアトラクターの経路に沿ってカメラを移動
const pointIndex = Math.floor((performance.now() * cameraSpeed) % points.length);
const point = points[pointIndex];
camera.position.x = point.x; // カメラのX座標を更新
camera.position.y = point.y; // カメラのY座標を更新
camera.position.z = point.z + 10; // カメラのZ座標を更新(オフセットを追加)
// カメラが常にアトラクターの中心を見るように設定
camera.lookAt(new THREE.Vector3(0, 0, 0));
renderer.render(scene, camera); // シーンをレンダリング
}
animate(); // アニメーションを開始
// ウィンドウリサイズ時の処理
window.addEventListener('resize', function () {
renderer.setSize(window.innerWidth, window.innerHeight); // レンダラーのサイズを更新
camera.aspect = window.innerWidth / window.innerHeight; // カメラのアスペクト比を更新
camera.updateProjectionMatrix(); // カメラの投影行列を更新
});
</script>
</body>
</html>
"""
# HTMLファイルとして保存
html_file = 'galaxy_simulation.html'
with open(html_file, 'w') as file:
file.write(html_content)
# サーバーをバックグラウンドで起動
def run_server():
server_address = ('', 8000)
httpd = HTTPServer(server_address, SimpleHTTPRequestHandler)
httpd.serve_forever()
# サーバーを別スレッドで実行
thread = threading.Thread(target=run_server)
thread.daemon = True
thread.start()
# デフォルトのブラウザでHTMLファイルを開く
webbrowser.open(f'http://localhost:8000/{html_file}')
# サーバー停止まで待機
try:
thread.join()
except KeyboardInterrupt:
print("Server stopped.")
各部分の説明
シーン、カメラ、レンダラーの設定: THREE.Scene()でシーンを作成し、THREE.PerspectiveCamera()で視点となるカメラを設定します。THREE.WebGLRenderer()を使用して、描画を行うレンダラーを設定し、renderer.domElementをHTMLに追加しています。
ローレンツアトラクターの点の生成: ローレンツ方程式のパラメータを設定し、時間刻みdtに従って次々と新しい点を計算し、点の配列pointsに追加しています。
ジオメトリとマテリアルの作成: ローレンツアトラクターの点をTHREE.BufferGeometry()とTHREE.PointsMaterial()で設定し、シーンに追加します。
アニメーションループ: requestAnimationFrame()を用いて、カメラがローレンツアトラクターに沿って動き回るようにアニメーションを行います。カメラは各フレームで現在のアトラクターの点を基準に移動し、常にアトラクターの中心を見続けます。
ウィンドウリサイズ時の処理: ウィンドウのサイズが変更された際に、レンダラーとカメラのアスペクト比を更新し、適切に描画が行われるようにします。
このコードで、ローレンツアトラクターの空間内をカメラが動きながら観察するアニメーションが実現できます。