use geo::algorithm::intersects::Intersects; use geo::algorithm::rotate::Rotate; use geo::prelude::Translate; use geo::rotate::RotatePoint; use geo::LineString; use geo::{polygon, Coordinate, MultiPolygon, Point, Polygon}; use geo_clipper::Clipper; use polygon_art::{load_multipolygon_from_svg_include, Context, MergeExt, ScaleExt}; use polygon_art::{AsteroidFont, FontDirection, TranslateExt}; use rand::prelude::StdRng; use rand::Rng; /// A more or less complete example that illustrates various technics fn main() { let mut context = Context::create(); let mut rng = context.gen_rng(); let width = context.width; let height = context.height; let diagonal = f64::sqrt(width * width + height * height); let frame_offset = diagonal * 0.01; let max_asteroid = (diagonal / 80.) as usize; let min_asteroid_size = diagonal / 50.; let max_asteroid_size = diagonal / 12.; let ship_length = diagonal / 23.; let ship_orbit = 3.0; let center_point = MultiPolygon(vec![create_asteroid(&mut rng, 8, 2)]) .scale_to_width(ship_orbit * ship_length) .translate(width / 2., height / 2.); // create asteroid let mut asteroids: MultiPolygon = MultiPolygon(Vec::new()); for _ in 0..(max_asteroid * 2) { let without_orbit = rng.gen_range(min_asteroid_size..max_asteroid_size) as f64; let with_orbit = without_orbit * 1.32; let x = rng.gen_range(0..(context.width as i32)) as f64; let y = rng.gen_range(0..(context.height as i32)) as f64; let asteroid = MultiPolygon(vec![create_asteroid(&mut rng, 20, 15)]) .scale_to_width(with_orbit) .translate(x, y); if (!center_point.intersects(&asteroid)) && !asteroids.intersects(&asteroid) { // asteroids.push(asteroid.scale_to_width(without_orbit)); asteroids.merge_destructive(&mut asteroid.scale_to_width(without_orbit)) } if asteroids.0.len() > max_asteroid { break; } } // load ships let rotation = rng.gen_range(0..360); let ship_yard = [ include_str!("../../pool/asteroids/ship1.svg"), include_str!("../../pool/asteroids/ship2.svg"), include_str!("../../pool/asteroids/ship3.svg"), ]; let chosen_ship = rng.gen_range(0..3); let raw_ship = load_multipolygon_from_svg_include(ship_yard[chosen_ship]) .unwrap() // rotate to scale ship with width .rotate(90.); let ship = raw_ship .scale_to_width(ship_length) .translate_center() .rotate(rotation as f64); context.save(); context.translate_center(); context.draw_multipolygon(&ship); context.restore(); // draw score board let font = AsteroidFont::new(); let random_seed_string = context.seed.to_string(); let font_size = 20.; let (x_font_size, y_font_size) = font.get_text_scale_factor(font_size); let score_board_letters = random_seed_string.len(); let window = context.frame(frame_offset, frame_offset, frame_offset, frame_offset); let window_height = frame_offset + y_font_size; let window_width = frame_offset + score_board_letters as f64 * x_font_size; let score_board: Polygon = polygon![(x:0., y:0.), (x: window_width, y:0.), (x: window_width, y: window_height), (x: 0., y: window_height)]; let score_board_lines = font.get_text(random_seed_string, font_size, FontDirection::LeftRight); context.save(); context.translate(frame_offset, frame_offset); context.draw_multiline_string(&score_board_lines); context.restore(); // draw life board let life = rng.gen_range(2..7) as f64; let life_board: Polygon = polygon![ (x:width, y:0.), (x: width - frame_offset - life * x_font_size - 3., y:0.), (x: width - frame_offset - life * x_font_size - 3., y:frame_offset + y_font_size + 3.), (x: width , y:frame_offset + y_font_size + 3.) ]; let life_ship = raw_ship .scale_to_width(font_size) .rotate(-90.) .translate_center_y() .translate_top(); for l in 1..(1 + life as i32) { context.save(); context.translate( width - frame_offset - (x_font_size * l as f64) as f64, frame_offset, ); context.draw_multipolygon(&life_ship); context.restore(); } // draw window let window = window.difference(&score_board, 100.); let window = window.difference(&life_board, 100.); context.draw_multipolygon(&window); // draw asteroids context.draw_multipolygon(&asteroids.intersection(&window, 100.)); // render image context.render(); } fn create_asteroid(rng: &mut StdRng, subdivisions: i32, radius_randomization: i32) -> Polygon { let mut asteroid_points: Vec> = Vec::new(); for _ in 0..subdivisions { let change = rng.gen_range(0..(radius_randomization * 2)); asteroid_points.push(Point(Coordinate { x: 0.0, y: 100.0 + (change - radius_randomization) as f64, })); for point in asteroid_points.iter_mut() { let (x, y) = point .rotate_around_point( 360. / subdivisions as f64, Point(Coordinate { x: 0., y: 0. }), ) .x_y(); point.set_x(x); point.set_y(y); } } Polygon::new( LineString(asteroid_points.iter().map(|point| point.0).collect()), Vec::new(), ) }