1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
use crate::{AffineOps, AffineTransform, BoundingRect, Coord, CoordFloat, CoordNum, Rect};
/// An affine transformation which scales a geometry up or down by a factor.
///
/// ## Performance
///
/// If you will be performing multiple transformations, like [`Scale`](crate::Scale),
/// [`Skew`](crate::Skew), [`Translate`](crate::Translate), or [`Rotate`](crate::Rotate), it is more
/// efficient to compose the transformations and apply them as a single operation using the
/// [`AffineOps`](crate::AffineOps) trait.
pub trait Scale<T: CoordNum> {
/// Scale a geometry from it's bounding box center.
///
/// # Examples
///
/// ```
/// use geo::Scale;
/// use geo::{LineString, line_string};
///
/// let ls: LineString = line_string![(x: 0., y: 0.), (x: 10., y: 10.)];
///
/// let scaled = ls.scale(2.);
///
/// assert_eq!(scaled, line_string![
/// (x: -5., y: -5.),
/// (x: 15., y: 15.)
/// ]);
/// ```
#[must_use]
fn scale(&self, scale_factor: T) -> Self;
/// Mutable version of [`scale`](Self::scale)
fn scale_mut(&mut self, scale_factor: T);
/// Scale a geometry from it's bounding box center, using different values for `x_factor` and
/// `y_factor` to distort the geometry's [aspect ratio](https://en.wikipedia.org/wiki/Aspect_ratio).
///
/// # Examples
///
/// ```
/// use geo::Scale;
/// use geo::{LineString, line_string};
///
/// let ls: LineString = line_string![(x: 0., y: 0.), (x: 10., y: 10.)];
///
/// let scaled = ls.scale_xy(2., 4.);
///
/// assert_eq!(scaled, line_string![
/// (x: -5., y: -15.),
/// (x: 15., y: 25.)
/// ]);
/// ```
#[must_use]
fn scale_xy(&self, x_factor: T, y_factor: T) -> Self;
/// Mutable version of [`scale_xy`](Self::scale_xy).
fn scale_xy_mut(&mut self, x_factor: T, y_factor: T);
/// Scale a geometry around a point of `origin`.
///
/// The point of origin is *usually* given as the 2D bounding box centre of the geometry, in
/// which case you can just use [`scale`](Self::scale) or [`scale_xy`](Self::scale_xy), but
/// this method allows you to specify any point.
///
/// # Examples
///
/// ```
/// use geo::Scale;
/// use geo::{LineString, line_string};
///
/// let ls: LineString = line_string![(x: 0., y: 0.), (x: 10., y: 10.)];
///
/// let scaled = ls.scale_xy(2., 4.);
///
/// assert_eq!(scaled, line_string![
/// (x: -5., y: -15.),
/// (x: 15., y: 25.)
/// ]);
/// ```
#[must_use]
fn scale_around_point(&self, x_factor: T, y_factor: T, origin: impl Into<Coord<T>>) -> Self;
/// Mutable version of [`scale_around_point`](Self::scale_around_point).
fn scale_around_point_mut(&mut self, x_factor: T, y_factor: T, origin: impl Into<Coord<T>>);
}
impl<T, IR, G> Scale<T> for G
where
T: CoordFloat,
IR: Into<Option<Rect<T>>>,
G: Clone + AffineOps<T> + BoundingRect<T, Output = IR>,
{
fn scale(&self, scale_factor: T) -> Self {
self.scale_xy(scale_factor, scale_factor)
}
fn scale_mut(&mut self, scale_factor: T) {
self.scale_xy_mut(scale_factor, scale_factor);
}
fn scale_xy(&self, x_factor: T, y_factor: T) -> Self {
let origin = match self.bounding_rect().into() {
Some(rect) => rect.center(),
// Empty geometries have no bounding rect, but in that case
// transforming is a no-op anyway.
None => return self.clone(),
};
self.scale_around_point(x_factor, y_factor, origin)
}
fn scale_xy_mut(&mut self, x_factor: T, y_factor: T) {
let origin = match self.bounding_rect().into() {
Some(rect) => rect.center(),
// Empty geometries have no bounding rect, but in that case
// transforming is a no-op anyway.
None => return,
};
self.scale_around_point_mut(x_factor, y_factor, origin);
}
fn scale_around_point(&self, x_factor: T, y_factor: T, origin: impl Into<Coord<T>>) -> Self {
let affineop = AffineTransform::scale(x_factor, y_factor, origin);
self.affine_transform(&affineop)
}
fn scale_around_point_mut(&mut self, x_factor: T, y_factor: T, origin: impl Into<Coord<T>>) {
let affineop = AffineTransform::scale(x_factor, y_factor, origin);
self.affine_transform_mut(&affineop)
}
}