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>Water Tank Particle Simulation</title>
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
let scene, camera, renderer, particles;
let particleCount = 20000;
let particlePositions = [];
let particleVelocities = [];
let tankWidth = 40, tankHeight = 20, tankDepth = 20;
function init() {
scene = new THREE.Scene(); // シーンの作成
camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); // カメラの作成
renderer = new THREE.WebGLRenderer(); // レンダラーの作成
renderer.setSize(window.innerWidth, window.innerHeight); // レンダラーのサイズ設定
document.body.appendChild(renderer.domElement); // レンダラーをHTMLに追加
let geometry = new THREE.BufferGeometry(); // パーティクルのジオメトリ作成
let positions = new Float32Array(particleCount * 3); // パーティクルの位置配列
let velocities = new Float32Array(particleCount * 3); // パーティクルの速度配列
// パーティクルの初期位置と速度を設定
for (let i = 0; i < particleCount; i++) {
positions[i * 3] = (Math.random() - 0.5) * tankWidth; // X座標
positions[i * 3 + 1] = (Math.random() - 0.5) * tankHeight; // Y座標
positions[i * 3 + 2] = (Math.random() - 0.5) * tankDepth; // Z座標
velocities[i * 3] = (Math.random() - 0.5) * 0.1; // X方向速度
velocities[i * 3 + 1] = (Math.random() - 0.5) * 0.1; // Y方向速度
velocities[i * 3 + 2] = (Math.random() - 0.5) * 0.1; // Z方向速度
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); // 位置属性をジオメトリに追加
let material = new THREE.PointsMaterial({ color: 0x00aaff, size: 0.1 }); // パーティクルのマテリアル設定
particles = new THREE.Points(geometry, material); // パーティクルの作成
scene.add(particles); // シーンにパーティクルを追加
particlePositions = positions; // パーティクルの位置配列を保存
particleVelocities = velocities; // パーティクルの速度配列を保存
// カメラ位置を斜め上からに調整
camera.position.set(30, 20, 30); // カメラを斜め上から見た位置に設定
camera.lookAt(scene.position); // カメラがシーンの中心を向くように設定
animate(); // アニメーションを開始
}
function animate() {
requestAnimationFrame(animate); // 次のフレームのアニメーションをリクエスト
let positions = particles.geometry.attributes.position.array; // パーティクルの位置配列を取得
for (let i = 0; i < particleCount; i++) {
// パーティクルの位置を更新
positions[i * 3] += particleVelocities[i * 3]; // X座標の更新
positions[i * 3 + 1] += particleVelocities[i * 3 + 1]; // Y座標の更新
positions[i * 3 + 2] += particleVelocities[i * 3 + 2]; // Z座標の更新
// パーティクルが水槽の端に達した場合の反射処理
if (positions[i * 3] < -tankWidth / 2 || positions[i * 3] > tankWidth / 2) {
particleVelocities[i * 3] = -particleVelocities[i * 3]; // X方向の速度を反転
}
if (positions[i * 3 + 1] < -tankHeight / 2 || positions[i * 3 + 1] > tankHeight / 2) {
particleVelocities[i * 3 + 1] = -particleVelocities[i * 3 + 1]; // Y方向の速度を反転
}
if (positions[i * 3 + 2] < -tankDepth / 2 || positions[i * 3 + 2] > tankDepth / 2) {
particleVelocities[i * 3 + 2] = -particleVelocities[i * 3 + 2]; // Z方向の速度を反転
}
// 水槽を揺らすシミュレーション(重力のような効果を追加)
particleVelocities[i * 3 + 1] -= 0.002; // Y軸方向に重力を適用
}
particles.geometry.attributes.position.needsUpdate = true; // パーティクルの位置の更新を反映
renderer.render(scene, camera); // シーンをレンダリング
}
// ウィンドウリサイズ時の処理
window.addEventListener('resize', function() {
let width = window.innerWidth;
let height = window.innerHeight;
renderer.setSize(width, height); // レンダラーのサイズをウィンドウに合わせる
camera.aspect = width / height; // カメラのアスペクト比を更新
camera.updateProjectionMatrix(); // カメラのプロジェクションマトリックスを更新
});
init(); // 初期化関数を呼び出す
</script>
</body>
</html>
"""
# HTMLファイルとして保存
html_file = 'water_tank_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.")