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()