world_generator_tool/planet_visualization.py
2025-03-03 19:46:14 +01:00

243 lines
7.2 KiB
Python

import numpy as np
import xarray as xr
import random
import webview
import os
import json
import pathlib
import time
# Function to generate Fibonacci sphere points
def fibonacci_sphere(n_points, randomize=False):
"""
Parameters:
n_points (int): Number of points to generate
randomize (bool): Add some randomization to the points
Returns:
ndarray: Array of [x, y, z] coordinates of points on a unit sphere
"""
points = []
phi = np.pi * (3. - np.sqrt(5.)) # golden angle in radians
for i in range(n_points):
y = 1 - (i / float(n_points - 1)) * 2 # y goes from 1 to -1
radius = np.sqrt(1 - y * y) # radius at y
theta = phi * i # golden angle increment
if randomize:
# Add slight randomization to the angle
theta = theta + random.uniform(-0.1, 0.1)
x = np.cos(theta) * radius
z = np.sin(theta) * radius
points.append([x, y, z])
return np.array(points)
# Function to convert cartesian to spherical coordinates
def cartesian_to_spherical(points):
"""
Convert cartesian (x, y, z) coordinates to spherical (r, theta, phi) coordinates.
Parameters:
points (ndarray): Array of [x, y, z] coordinates
Returns:
ndarray: Array of [r, theta, phi] coordinates
"""
x, y, z = points[:, 0], points[:, 1], points[:, 2]
r = np.sqrt(x**2 + y**2 + z**2)
theta = np.arccos(z / r) # polar angle (0 to pi)
phi = np.arctan2(y, x) # azimuthal angle (0 to 2pi)
return np.column_stack((r, theta, phi))
# Function to generate random colors for points
def generate_point_colors(n_points, color_scheme='random'):
"""
Generate colors for each point.
Parameters:
n_points (int): Number of points
color_scheme (str): Type of color scheme to use
Returns:
ndarray: Array of [r, g, b] values (0-1 scale)
"""
colors = []
if color_scheme == 'random':
for _ in range(n_points):
colors.append([random.random(), random.random(), random.random()])
elif color_scheme == 'terrain':
# Generate terrain-like colors
for _ in range(n_points):
elev = random.random() # simulated elevation
if elev < 0.25:
# Deep water (dark blue)
colors.append([0.0, 0.0, 0.5 + 0.5 * random.random()])
elif elev < 0.4:
# Shallow water (light blue)
colors.append([0.0, 0.3 + 0.3 * random.random(), 0.8])
elif elev < 0.5:
# Sand (tan)
colors.append([0.76, 0.7, 0.5])
elif elev < 0.7:
# Grass/forest (green)
colors.append([0.0, 0.5 + 0.3 * random.random(), 0.0])
elif elev < 0.9:
# Mountain (gray/brown)
g = 0.3 + 0.2 * random.random()
colors.append([g, g, g])
else:
# Snow (white)
colors.append([0.9, 0.9, 0.9])
return np.array(colors)
# Generate point data and store in xarray dataset
def create_planet_data(n_points=10000, color_scheme='terrain'):
"""
Create planet data with Fibonacci sphere distribution and colors.
Parameters:
n_points (int): Number of points to generate
color_scheme (str): Type of color scheme to use
Returns:
xarray.Dataset: Dataset containing planet data
"""
# Generate Fibonacci sphere points
cartesian_points = fibonacci_sphere(n_points)
# Add some small delays for larger point sets to make progress bar meaningful
if n_points > 5000:
time.sleep(0.2) # Simulate computation time
# Convert to spherical coordinates
spherical_points = cartesian_to_spherical(cartesian_points)
if n_points > 5000:
time.sleep(0.2) # Simulate computation time
# Generate colors
colors = generate_point_colors(n_points, color_scheme)
if n_points > 5000:
time.sleep(0.2) # Simulate computation time
# Create xarray dataset
ds = xr.Dataset(
data_vars={
'x': ('point_idx', cartesian_points[:, 0]),
'y': ('point_idx', cartesian_points[:, 1]),
'z': ('point_idx', cartesian_points[:, 2]),
'r': ('point_idx', spherical_points[:, 0]),
'theta': ('point_idx', spherical_points[:, 1]),
'phi': ('point_idx', spherical_points[:, 2]),
'color_r': ('point_idx', colors[:, 0]),
'color_g': ('point_idx', colors[:, 1]),
'color_b': ('point_idx', colors[:, 2]),
},
coords={
'point_idx': np.arange(n_points),
},
attrs={
'description': 'Planet data with Fibonacci sphere distribution',
'n_points': n_points,
'color_scheme': color_scheme,
}
)
return ds
# Function to prepare data for visualization
def prepare_visualization_data(ds):
"""
Prepare the xarray dataset for visualization in the web view.
Parameters:
ds (xarray.Dataset): Dataset containing planet data
Returns:
dict: Dictionary with data for visualization
"""
# Extract data for visualization
point_data = []
for i in range(len(ds.point_idx)):
point_data.append({
'x': float(ds.x[i].values),
'y': float(ds.y[i].values),
'z': float(ds.z[i].values),
'theta': float(ds.theta[i].values),
'phi': float(ds.phi[i].values),
'color': [
float(ds.color_r[i].values),
float(ds.color_g[i].values),
float(ds.color_b[i].values)
]
})
return {'points': point_data}
# API exposed to the browser
class Api:
def __init__(self, ds):
self.ds = ds
def regenerate_planet(self, n_points, color_scheme):
"""
Regenerate planet data with new parameters.
Parameters:
n_points (int): Number of points
color_scheme (str): Color scheme to use
Returns:
dict: Updated visualization data
"""
self.ds = create_planet_data(n_points, color_scheme)
return prepare_visualization_data(self.ds)
def main():
# Create planet data
n_points = 5000 # default number of points
color_scheme = 'terrain' # default color scheme
ds = create_planet_data(n_points, color_scheme)
# Get the directory of the current script
script_dir = pathlib.Path(__file__).parent.absolute()
# Prepare data for visualization
data = prepare_visualization_data(ds)
# Create API instance
api = Api(ds)
# Function to initialize data after window loads
def on_loaded():
window.evaluate_js(f'setPlanetData({json.dumps(data)})')
# Create webview window
window = webview.create_window(
'Planet Visualization',
url=os.path.join(script_dir, 'index.html'),
js_api=api,
width=1200,
height=800
)
# Set up the loaded event handler
window.events.loaded += on_loaded
# Start webview
webview.start()
if __name__ == "__main__":
main()