polygon-art/src/bin/asteroids.rs

151 lines
5.4 KiB
Rust

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<f64> = 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<f64> = 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<f64> = 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<f64> {
let mut asteroid_points: Vec<Point<f64>> = 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(),
)
}