feat: add merge_ext.rs and scale_ext.rs

develop
Ingolf Wagner 2021-04-13 20:24:04 +02:00
parent 1574ca725f
commit f008f92bfc
Signed by: palo
GPG Key ID: 76BF5F1928B9618B
8 changed files with 134 additions and 87 deletions

View File

@ -3,10 +3,9 @@
use geo::rotate::RotatePoint;
use geo::translate::Translate;
use geo::{Coordinate, Point};
use polygon_art::load_multipolygon_from_svg;
use polygon_art::Context;
use polygon_art::MultiPolygonExt;
use polygon_art::TranslateExt;
use polygon_art::{load_multipolygon_from_svg, ScaleExt};
use rand::Rng;
fn main() {

View File

@ -5,8 +5,8 @@ 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};
use polygon_art::{AsteroidFont, FontDirection, MultiPolygonExt, TranslateExt};
use polygon_art::{load_multipolygon_from_svg_include, Context, MergeExt, ScaleExt};
use polygon_art::{AsteroidFont, FontDirection, TranslateExt};
use rand::prelude::StdRng;
use rand::Rng;
@ -19,12 +19,10 @@ fn main() {
let height = context.height;
let diagonal = f64::sqrt(width * width + height * height);
// todo : it does not make to much sense to define all parameters like this
// todo : it makes more sense to create a static (diagonal / width /height ratio ) setup and scale it up
let frame_offset = diagonal * 0.01;
let max_asteroid = (diagonal / 40.) as usize;
let max_asteroid = (diagonal / 80.) as usize;
let min_asteroid_size = diagonal / 40.;
let min_asteroid_size = diagonal / 50.;
let max_asteroid_size = diagonal / 12.;
let ship_length = diagonal / 23.;
let ship_orbit = 3.0;
@ -34,7 +32,7 @@ fn main() {
.translate(width / 2., height / 2.);
// create asteroid
let mut asteroids: Vec<MultiPolygon<f64>> = Vec::new();
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;
@ -44,15 +42,11 @@ fn main() {
.scale_to_width(with_orbit)
.translate(x, y);
if (!center_point.intersects(&asteroid))
&& asteroids
.iter()
.find(|&m| *&m.intersects(&asteroid))
.is_none()
{
asteroids.push(asteroid.scale_to_width(without_orbit));
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.len() > max_asteroid {
if asteroids.0.len() > max_asteroid {
break;
}
}
@ -123,10 +117,10 @@ fn main() {
let window = window.difference(&life_board, 100.);
context.draw_multipolygon(&window);
for asteroid in asteroids.iter() {
context.draw_multipolygon(&asteroid.intersection(&window, 100.));
}
// draw asteroids
context.draw_multipolygon(&asteroids.intersection(&window, 100.));
// render image
context.render();
}

View File

@ -1,2 +1,3 @@
pub(crate) mod multipolygon_ext;
pub(crate) mod merge_ext;
pub(crate) mod scale_ext;
pub(crate) mod translate_ext;

View File

@ -0,0 +1,35 @@
use geo::{LineString, MultiLineString, MultiPolygon};
pub trait MergeExt {
/// merge Container object
fn merge(&mut self, other: &Self);
/// merge container object, empty the other object
fn merge_destructive(&mut self, other: &mut Self);
}
impl MergeExt for LineString<f64> {
fn merge(&mut self, other: &Self) {
self.0.append(&mut other.0.clone());
}
fn merge_destructive(&mut self, other: &mut Self) {
self.0.append(&mut other.0);
}
}
impl MergeExt for MultiLineString<f64> {
fn merge(&mut self, other: &Self) {
self.0.append(&mut other.0.clone());
}
fn merge_destructive(&mut self, other: &mut Self) {
self.0.append(&mut other.0);
}
}
impl MergeExt for MultiPolygon<f64> {
fn merge(&mut self, other: &Self) {
self.0.append(&mut other.0.clone());
}
fn merge_destructive(&mut self, other: &mut Self) {
self.0.append(&mut other.0);
}
}

View File

@ -1,51 +0,0 @@
use geo::algorithm::bounding_rect::BoundingRect;
use geo::algorithm::translate::Translate;
use geo::Polygon;
use geo_types::{Coordinate, LineString, MultiPolygon};
pub trait MultiPolygonExt {
fn scale_to_width(&self, size: f64) -> Self;
fn scale(&self, factor: f64) -> Self;
}
impl MultiPolygonExt for MultiPolygon<f64> {
fn scale_to_width(&self, size: f64) -> Self {
let option = self.bounding_rect().unwrap();
let diff = f64::abs(option.min().x - option.max().x);
let scale = size / diff;
self.scale(scale)
}
fn scale(&self, factor: f64) -> Self {
let bounding_rectangle = self.bounding_rect().unwrap();
let zeroed_multi_polygons =
self.translate(-bounding_rectangle.min().x, -bounding_rectangle.min().y);
let mut new_polygons: Vec<Polygon<f64>> = Vec::new();
for polygon in zeroed_multi_polygons.iter() {
// map interiors
let mut interiors: Vec<LineString<f64>> = Vec::new();
for interior_line_string in polygon.interiors().to_vec() {
let mut new_line_string: Vec<Coordinate<f64>> = Vec::new();
for point in interior_line_string.into_points() {
new_line_string.push(Coordinate {
x: point.x() * factor,
y: point.y() * factor,
});
}
interiors.push(LineString::from(new_line_string));
}
// map exterior
let mut new_line_string: Vec<Coordinate<f64>> = Vec::new();
for point in polygon.exterior().clone().into_points() {
new_line_string.push(Coordinate {
x: point.x() * factor,
y: point.y() * factor,
});
}
let exterior = LineString::from(new_line_string);
new_polygons.push(Polygon::new(exterior, interiors));
}
MultiPolygon::from(new_polygons)
.translate(bounding_rectangle.min().x, bounding_rectangle.min().y)
}
}

View File

@ -0,0 +1,68 @@
use crate::extensions::translate_ext::ExplicitBoundingRect;
use geo::algorithm::map_coords::MapCoords;
use geo::algorithm::translate::Translate;
use geo::{CoordinateType, LineString, MultiLineString, MultiPolygon, Polygon};
pub trait ExplicitMapCoords<T> {
fn map_coordinates(&self, func: impl Fn(&(T, T)) -> (T, T) + Copy) -> Self;
}
impl<T: CoordinateType> ExplicitMapCoords<T> for LineString<T> {
fn map_coordinates(&self, func: impl Fn(&(T, T)) -> (T, T) + Copy) -> Self {
self.map_coords(func)
}
}
impl<T: CoordinateType> ExplicitMapCoords<T> for MultiLineString<T> {
fn map_coordinates(&self, func: impl Fn(&(T, T)) -> (T, T) + Copy) -> Self {
self.map_coords(func)
}
}
impl<T: CoordinateType> ExplicitMapCoords<T> for MultiPolygon<T> {
fn map_coordinates(&self, func: impl Fn(&(T, T)) -> (T, T) + Copy) -> Self {
self.map_coords(func)
}
}
impl<T: CoordinateType> ExplicitMapCoords<T> for Polygon<T> {
fn map_coordinates(&self, func: impl Fn(&(T, T)) -> (T, T) + Copy) -> Self {
self.map_coords(func)
}
}
pub trait ScaleExt {
/// scale object
fn scale(&self, scale_x: f64, scale_y: f64) -> Self;
/// scale object to given width (in 1:1 ratio)
fn scale_to_width(&self, size: f64) -> Self;
/// scale object to given height (in 1:1 ratio)
fn scale_to_height(&self, size: f64) -> Self;
}
impl<V> ScaleExt for V
where
V: ExplicitBoundingRect<f64> + ExplicitMapCoords<f64> + Translate<f64>,
{
fn scale(&self, scale_x: f64, scale_y: f64) -> Self {
let bounding_rectangle = self.bounding().unwrap();
let minimum_coordinates = bounding_rectangle.min();
self.translate(-minimum_coordinates.x, -minimum_coordinates.y)
.map_coordinates(|&(x, y)| (x * scale_x, y * scale_y))
.translate(minimum_coordinates.x, minimum_coordinates.y)
}
fn scale_to_width(&self, size: f64) -> Self {
let option = self.bounding().unwrap();
let diff = f64::abs(option.min().x - option.max().x);
let scale = size / diff;
self.scale(scale, scale)
}
fn scale_to_height(&self, size: f64) -> Self {
let option = self.bounding().unwrap();
let diff = f64::abs(option.min().y - option.max().y);
let scale = size / diff;
self.scale(scale, scale)
}
}

View File

@ -2,20 +2,6 @@ use geo::algorithm::bounding_rect::BoundingRect;
use geo::algorithm::translate::Translate;
use geo::{CoordinateType, LineString, MultiLineString, MultiPolygon, Polygon, Rect};
/// Translate Extensions
pub trait TranslateExt {
/// translate the object to it's center
fn translate_center(&self) -> Self;
/// translate the object to it's center on the x aches
fn translate_center_x(&self) -> Self;
/// translate the object to it's center on the y aches
fn translate_center_y(&self) -> Self;
/// translate the object, so it touches the y aches
fn translate_left(&self) -> Self;
/// translate the object, so it touches the x aches
fn translate_top(&self) -> Self;
}
/// wrapper for BoundingRect because of the Output type
pub trait ExplicitBoundingRect<T>
where
@ -48,6 +34,20 @@ impl<T: CoordinateType> ExplicitBoundingRect<T> for MultiLineString<T> {
}
}
/// Translate Extensions
pub trait TranslateExt {
/// translate the object to it's center
fn translate_center(&self) -> Self;
/// translate the object to it's center on the x aches
fn translate_center_x(&self) -> Self;
/// translate the object to it's center on the y aches
fn translate_center_y(&self) -> Self;
/// translate the object, so it touches the y aches
fn translate_left(&self) -> Self;
/// translate the object, so it touches the x aches
fn translate_top(&self) -> Self;
}
impl<B> TranslateExt for B
where
B: ExplicitBoundingRect<f64> + Translate<f64>,

View File

@ -2,7 +2,8 @@ mod context;
pub use crate::context::Context;
mod extensions;
pub use crate::extensions::multipolygon_ext::MultiPolygonExt;
pub use crate::extensions::merge_ext::MergeExt;
pub use crate::extensions::scale_ext::ScaleExt;
pub use crate::extensions::translate_ext::TranslateExt;
mod svg;