From f008f92bfca73e68d20337e8d6b281d5bf82fadd Mon Sep 17 00:00:00 2001 From: Ingolf Wagner Date: Tue, 13 Apr 2021 20:24:04 +0200 Subject: [PATCH] feat: add merge_ext.rs and scale_ext.rs --- examples/svg.rs | 3 +- src/bin/asteroids.rs | 30 ++++++------- src/extensions.rs | 3 +- src/extensions/merge_ext.rs | 35 +++++++++++++++ src/extensions/multipolygon_ext.rs | 51 ---------------------- src/extensions/scale_ext.rs | 68 ++++++++++++++++++++++++++++++ src/extensions/translate_ext.rs | 28 ++++++------ src/lib.rs | 3 +- 8 files changed, 134 insertions(+), 87 deletions(-) create mode 100644 src/extensions/merge_ext.rs delete mode 100644 src/extensions/multipolygon_ext.rs create mode 100644 src/extensions/scale_ext.rs diff --git a/examples/svg.rs b/examples/svg.rs index 8b0eef9..07f1f71 100644 --- a/examples/svg.rs +++ b/examples/svg.rs @@ -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() { diff --git a/src/bin/asteroids.rs b/src/bin/asteroids.rs index a302754..ff754e1 100644 --- a/src/bin/asteroids.rs +++ b/src/bin/asteroids.rs @@ -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> = Vec::new(); + 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; @@ -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(); } diff --git a/src/extensions.rs b/src/extensions.rs index 76f3e22..f2166c7 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -1,2 +1,3 @@ -pub(crate) mod multipolygon_ext; +pub(crate) mod merge_ext; +pub(crate) mod scale_ext; pub(crate) mod translate_ext; diff --git a/src/extensions/merge_ext.rs b/src/extensions/merge_ext.rs new file mode 100644 index 0000000..8aab680 --- /dev/null +++ b/src/extensions/merge_ext.rs @@ -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 { + 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 { + 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 { + 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); + } +} diff --git a/src/extensions/multipolygon_ext.rs b/src/extensions/multipolygon_ext.rs deleted file mode 100644 index 8b79290..0000000 --- a/src/extensions/multipolygon_ext.rs +++ /dev/null @@ -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 { - 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> = Vec::new(); - for polygon in zeroed_multi_polygons.iter() { - // map interiors - let mut interiors: Vec> = Vec::new(); - for interior_line_string in polygon.interiors().to_vec() { - let mut new_line_string: Vec> = 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> = 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) - } -} diff --git a/src/extensions/scale_ext.rs b/src/extensions/scale_ext.rs new file mode 100644 index 0000000..dfd159f --- /dev/null +++ b/src/extensions/scale_ext.rs @@ -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 { + fn map_coordinates(&self, func: impl Fn(&(T, T)) -> (T, T) + Copy) -> Self; +} + +impl ExplicitMapCoords for LineString { + fn map_coordinates(&self, func: impl Fn(&(T, T)) -> (T, T) + Copy) -> Self { + self.map_coords(func) + } +} + +impl ExplicitMapCoords for MultiLineString { + fn map_coordinates(&self, func: impl Fn(&(T, T)) -> (T, T) + Copy) -> Self { + self.map_coords(func) + } +} + +impl ExplicitMapCoords for MultiPolygon { + fn map_coordinates(&self, func: impl Fn(&(T, T)) -> (T, T) + Copy) -> Self { + self.map_coords(func) + } +} + +impl ExplicitMapCoords for Polygon { + 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 ScaleExt for V +where + V: ExplicitBoundingRect + ExplicitMapCoords + Translate, +{ + 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) + } +} diff --git a/src/extensions/translate_ext.rs b/src/extensions/translate_ext.rs index 1dee99f..8d43461 100644 --- a/src/extensions/translate_ext.rs +++ b/src/extensions/translate_ext.rs @@ -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 where @@ -48,6 +34,20 @@ impl ExplicitBoundingRect for MultiLineString { } } +/// 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 TranslateExt for B where B: ExplicitBoundingRect + Translate, diff --git a/src/lib.rs b/src/lib.rs index 18a3ad2..6923347 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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;