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>Cloud and Rain 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 maxHeight = 20; // 雲の最大高度
let gravity = -9.8; // 重力加速度
let riseSpeed = 0.02; // 上昇速度
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) * 20; // X座標
positions[i * 3 + 1] = Math.random() * maxHeight / 2; // Y座標 (地面から始まる)
positions[i * 3 + 2] = (Math.random() - 0.5) * 20; // Z座標
velocities[i * 3] = 0; // X方向速度
velocities[i * 3 + 1] = riseSpeed; // Y方向速度(初めは上昇)
velocities[i * 3 + 2] = 0; // 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, 30, 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 + 1] += particleVelocities[i * 3 + 1]; // Y座標の更新(上下動)
// 高度が一定以上なら、パーティクルを落下させる
if (positions[i * 3 + 1] > maxHeight) {
particleVelocities[i * 3 + 1] = gravity; // 重力で落下させる
}
// 地面に達したら、また上昇を開始する
if (positions[i * 3 + 1] < 0) {
positions[i * 3 + 1] = 0;
particleVelocities[i * 3 + 1] = riseSpeed; // 上昇を再開
}
}
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 = 'cloud_rain_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.")