Something went wrong. Try again.
google balls
Something went wrong. Try again.
9.8 kB · 308 lines
Rust
at master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308use macroquad::prelude::*;use std::time::{Duration, Instant};
#[derive(Clone, Debug)]pub struct Vector3 { pub x: f32, pub y: f32, pub z: f32,}
impl Vector3 { pub fn new(x: f32, y: f32, z: f32) -> Self { Self { x, y, z } }
pub fn set(&mut self, x: f32, y: f32, z: f32) { self.x = x; self.y = y; self.z = z; }}
pub struct Point { pub colour: Color, pub cur_pos: Vector3, pub friction: f32, pub original_pos: Vector3, pub radius: f32, pub size: f32, pub spring_strength: f32, pub target_pos: Vector3, pub velocity: Vector3,}
impl Point { pub fn new(x: f32, y: f32, z: f32, size: f32, color_hex: &str) -> Self { let color = Self::hex_to_color(color_hex); Self { colour: color, cur_pos: Vector3::new(x, y, z), friction: 0.8, original_pos: Vector3::new(x, y, z), radius: size, size, spring_strength: 0.1, target_pos: Vector3::new(x, y, z), velocity: Vector3::new(0.0, 0.0, 0.0), } }
pub fn hex_to_color(hex: &str) -> Color { let hex = hex.trim_start_matches('#'); if hex.len() != 6 { return WHITE; }
let r = u8::from_str_radix(&hex[0..2], 16).unwrap_or(255) as f32 / 255.0; let g = u8::from_str_radix(&hex[2..4], 16).unwrap_or(255) as f32 / 255.0; let b = u8::from_str_radix(&hex[4..6], 16).unwrap_or(255) as f32 / 255.0;
Color::new(r, g, b, 1.0) }
pub fn update(&mut self) { // X axis physics let dx = self.target_pos.x - self.cur_pos.x; let ax = dx * self.spring_strength; self.velocity.x += ax; self.velocity.x *= self.friction; self.cur_pos.x += self.velocity.x;
// Y axis physics let dy = self.target_pos.y - self.cur_pos.y; let ay = dy * self.spring_strength; self.velocity.y += ay; self.velocity.y *= self.friction; self.cur_pos.y += self.velocity.y;
// Z axis physics (for size scaling) let dox = self.original_pos.x - self.cur_pos.x; let doy = self.original_pos.y - self.cur_pos.y; let dd = (dox * dox) + (doy * doy); let d = dd.sqrt();
self.target_pos.z = d / 100.0 + 1.0; let dz = self.target_pos.z - self.cur_pos.z; let az = dz * self.spring_strength; self.velocity.z += az; self.velocity.z *= self.friction; self.cur_pos.z += self.velocity.z;
self.radius = self.size * self.cur_pos.z; if self.radius < 1.0 { self.radius = 1.0; } }
pub fn draw(&self) { // Draw filled circle only - removed the white border draw_circle(self.cur_pos.x, self.cur_pos.y, self.radius, self.colour); }}
pub struct PointCollection { pub mouse_pos: Vector3, pub points: Vec<Point>,}
impl PointCollection { pub fn new() -> Self { Self { mouse_pos: Vector3::new(0.0, 0.0, 0.0), points: Vec::new(), } }
pub fn update(&mut self) { for point in &mut self.points { let dx = self.mouse_pos.x - point.cur_pos.x; let dy = self.mouse_pos.y - point.cur_pos.y; let dd = (dx * dx) + (dy * dy); let d = dd.sqrt();
if d < 150.0 { point.target_pos.x = point.cur_pos.x - dx; point.target_pos.y = point.cur_pos.y - dy; } else { point.target_pos.x = point.original_pos.x; point.target_pos.y = point.original_pos.y; }
point.update(); } }
pub fn draw(&self) { for point in &self.points { point.draw(); } }
pub fn recenter_points(&mut self, canvas_width: f32, canvas_height: f32) { let offset_x = canvas_width / 2.0 - 180.0; let offset_y = canvas_height / 2.0 - 65.0;
for point in &mut self.points { // Calculate relative position from the old center let rel_x = point.original_pos.x - offset_x; let rel_y = point.original_pos.y - offset_y;
// Update to new center point.original_pos.x = offset_x + rel_x; point.original_pos.y = offset_y + rel_y;
point.cur_pos.x = point.original_pos.x; point.cur_pos.y = point.original_pos.y; } }}
pub fn create_google_balls(canvas_width: f32, canvas_height: f32) -> PointCollection { let mut collection = PointCollection::new();
// Original Google logo ball data let ball_data = vec![ (202.0, 78.0, 0.0, 9.0, "#ed9d33"), (348.0, 83.0, 0.0, 9.0, "#d44d61"), (256.0, 69.0, 0.0, 9.0, "#4f7af2"), (214.0, 59.0, 0.0, 9.0, "#ef9a1e"), (265.0, 36.0, 0.0, 9.0, "#4976f3"), (300.0, 78.0, 0.0, 9.0, "#269230"), (294.0, 59.0, 0.0, 9.0, "#1f9e2c"), (45.0, 88.0, 0.0, 9.0, "#1c48dd"), (268.0, 52.0, 0.0, 9.0, "#2a56ea"), (73.0, 83.0, 0.0, 9.0, "#3355d8"), (294.0, 6.0, 0.0, 9.0, "#36b641"), (235.0, 62.0, 0.0, 9.0, "#2e5def"), (353.0, 42.0, 0.0, 8.0, "#d53747"), (336.0, 52.0, 0.0, 8.0, "#eb676f"), (208.0, 41.0, 0.0, 8.0, "#f9b125"), (321.0, 70.0, 0.0, 8.0, "#de3646"), (8.0, 60.0, 0.0, 8.0, "#2a59f0"), (180.0, 81.0, 0.0, 8.0, "#eb9c31"), (146.0, 65.0, 0.0, 8.0, "#c41731"), (145.0, 49.0, 0.0, 8.0, "#d82038"), (246.0, 34.0, 0.0, 8.0, "#5f8af8"), (169.0, 69.0, 0.0, 8.0, "#efa11e"), (273.0, 99.0, 0.0, 8.0, "#2e55e2"), (248.0, 120.0, 0.0, 8.0, "#4167e4"), (294.0, 41.0, 0.0, 8.0, "#0b991a"), (267.0, 114.0, 0.0, 8.0, "#4869e3"), (78.0, 67.0, 0.0, 8.0, "#3059e3"), (294.0, 23.0, 0.0, 8.0, "#10a11d"), (117.0, 83.0, 0.0, 8.0, "#cf4055"), (137.0, 80.0, 0.0, 8.0, "#cd4359"), (14.0, 71.0, 0.0, 8.0, "#2855ea"), (331.0, 80.0, 0.0, 8.0, "#ca273c"), (25.0, 82.0, 0.0, 8.0, "#2650e1"), (233.0, 46.0, 0.0, 8.0, "#4a7bf9"), (73.0, 13.0, 0.0, 8.0, "#3d65e7"), (327.0, 35.0, 0.0, 6.0, "#f47875"), (319.0, 46.0, 0.0, 6.0, "#f36764"), (256.0, 81.0, 0.0, 6.0, "#1d4eeb"), (244.0, 88.0, 0.0, 6.0, "#698bf1"), (194.0, 32.0, 0.0, 6.0, "#fac652"), (97.0, 56.0, 0.0, 6.0, "#ee5257"), (105.0, 75.0, 0.0, 6.0, "#cf2a3f"), (42.0, 4.0, 0.0, 6.0, "#5681f5"), (10.0, 27.0, 0.0, 6.0, "#4577f6"), (166.0, 55.0, 0.0, 6.0, "#f7b326"), (266.0, 88.0, 0.0, 6.0, "#2b58e8"), (178.0, 34.0, 0.0, 6.0, "#facb5e"), (100.0, 65.0, 0.0, 6.0, "#e02e3d"), (343.0, 32.0, 0.0, 6.0, "#f16d6f"), (59.0, 5.0, 0.0, 6.0, "#507bf2"), (27.0, 9.0, 0.0, 6.0, "#5683f7"), (233.0, 116.0, 0.0, 6.0, "#3158e2"), (123.0, 32.0, 0.0, 6.0, "#f0696c"), (6.0, 38.0, 0.0, 6.0, "#3769f6"), (63.0, 62.0, 0.0, 6.0, "#6084ef"), (6.0, 49.0, 0.0, 6.0, "#2a5cf4"), (108.0, 36.0, 0.0, 6.0, "#f4716e"), (169.0, 43.0, 0.0, 6.0, "#f8c247"), (137.0, 37.0, 0.0, 6.0, "#e74653"), (318.0, 58.0, 0.0, 6.0, "#ec4147"), (226.0, 100.0, 0.0, 5.0, "#4876f1"), (101.0, 46.0, 0.0, 5.0, "#ef5c5c"), (226.0, 108.0, 0.0, 5.0, "#2552ea"), (17.0, 17.0, 0.0, 5.0, "#4779f7"), (232.0, 93.0, 0.0, 5.0, "#4b78f1"), ];
for (x, y, z, size, color) in ball_data { let mut point = Point::new( (canvas_width / 2.0 - 180.0) + x, (canvas_height / 2.0 - 65.0) + y, z, size, color, );
point.original_pos.x = (canvas_width / 2.0 - 180.0) + x; point.original_pos.y = (canvas_height / 2.0 - 65.0) + y;
collection.points.push(point); }
collection}
// Main game loop that can be called from different contextspub async fn run_game() { let mut point_collection = create_google_balls(screen_width(), screen_height()); let mut last_screen_size = (screen_width(), screen_height());
// Frame rate control for 33 FPS let target_fps = 33.0; let frame_duration = Duration::from_secs_f64(1.0 / target_fps); let mut last_frame_time = Instant::now();
loop { // Handle window resize let current_screen_size = (screen_width(), screen_height()); if current_screen_size != last_screen_size { point_collection.recenter_points(screen_width(), screen_height()); last_screen_size = current_screen_size; }
// Update mouse position let (mouse_x, mouse_y) = mouse_position(); point_collection.mouse_pos.set(mouse_x, mouse_y, 0.0);
// Clear screen clear_background(WHITE);
// Update and draw points point_collection.update(); point_collection.draw();
// Frame rate control let elapsed = last_frame_time.elapsed(); if elapsed < frame_duration { let sleep_time = frame_duration - elapsed; std::thread::sleep(sleep_time); } last_frame_time = Instant::now();
next_frame().await }}
// WASM entry point#[cfg(target_arch = "wasm32")]use wasm_bindgen::prelude::*;
#[cfg(target_arch = "wasm32")]#[wasm_bindgen(start)]pub fn wasm_main() { // For WASM, we need to start the game loop wasm_bindgen_futures::spawn_local(run_game());}
// Alternative entry point for mobile platforms#[cfg(any(target_os = "android", target_os = "ios"))]pub fn mobile_main() { // For mobile platforms, this would be called from platform-specific wrapper code futures::executor::block_on(run_game());}