243 lines
7.2 KiB
Python
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() |