-
Notifications
You must be signed in to change notification settings - Fork 200
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
introduce GeometryCow avoid clone in
Geometry::from(inner.clone())
The primary currency of the geo crate is the Geometry enum: ``` pub enum Geometry<T> where T: CoordNum, { Point(Point<T>), Line(Line<T>), LineString(LineString<T>), Polygon(Polygon<T>), MultiPoint(MultiPoint<T>), MultiLineString(MultiLineString<T>), MultiPolygon(MultiPolygon<T>), GeometryCollection(GeometryCollection<T>), Rect(Rect<T>), Triangle(Triangle<T>), } ``` Most of our traits have impls for the individual Geometry cases: ``` impl<T> Foo for Point<T> { fn foo(&self) -> { // do something } } impl<T> Foo for Line<T> { fn foo(&self) -> { // do something } } impl<T> Foo for LineString<T> { fn foo(&self) -> { // do something } } ... ``` and the impl for `Geometry` itself is a simple dispatch to the inner variants impl. ``` impl <T> Foo for Geometry<T> { fn foo(&self) -> { match self { Geometry::Point(g) => g.foo(), Geometry::Line(g) => g.foo(), Geometry::LineString(g) => g.foo(), ... => etc. } } ``` However, the new `Relate` trait, which utilizes the geometry graph I've been working on, is implemented directly on Geometry. Like this: ``` impl<F: 'static + GeoFloat> Relate<F, Geometry<F>> for Geometry<F> { fn relate(&self, other: &Geometry<F>) -> IntersectionMatrix { let mut relate_computer = relate_computer::RelateComputer::new(self, other); relate_computer.compute_intersection_matrix() } } ``` So, the way to make this work, is to introduce undesirable cloning: ``` fn color(line: &Line, polygon: &Polygon) -> Color { # We want to avoid these clones! let line_geometry = Geometry::Line(line.clone()); let polygon_geometry = Geometry::Polygon(polygon.clone()); if polygon_geometry.relate(line_geometry).is_contains() { Color::Green } else { Color::Red } } ``` In this commit, I've started to address this by introducing a new GeometryCow variant, which is just like Geometry, but with a reference to the inner type. ``` pub enum GeometryCow<'a, T> where T: CoordNum, { Point(&'a Point<T>), Line(&'a Line<T>), LineString(&'a LineString<T>), Polygon(&'a Polygon<T>), MultiPoint(&'a MultiPoint<T>), MultiLineString(&'a MultiLineString<T>), MultiPolygon(&'a MultiPolygon<T>), GeometryCollection(&'a GeometryCollection<T>), Rect(&'a Rect<T>), Triangle(&'a Triangle<T>), } impl<F: 'static + GeoFloat> Relate<F, GeometryCow<F>> for GeometryCow<F> { fn relate(&self, other: &GeometryCow<F>) -> IntersectionMatrix { let mut relate_computer = relate_computer::RelateComputer::new(self, other); relate_computer.compute_intersection_matrix() } } ``` Then the problematic clones go away: ``` fn color(line: &Line, polygon: &Polygon) -> Color { let line_geometry = GeometryCow::Line(line); let polygon_geometry = GeometryCow::Polygon(polygon); if polygon_geometry.relate(line_geometry).is_contains() { Color::Green } else { Color::Red } } ``` So, that works... *but* `relate` in turn relies on a bunch of functionality that's already implemented on `Geometry`. I've started down the road of more or less copy/pasting those impls for `GeometryCow`. It's almost entirely simple dispatch logic - e.g. whereas before we had something like this: ``` impl<T> BoundingRect<T> for Geometry<T> where T: CoordNum, { type Output = Option<Rect<T>>; fn bounding_rect(&self) -> Self::Output { match self { Geometry::Point(g) => Some(g.bounding_rect()), Geometry::Line(g) => Some(g.bounding_rect()), Geometry::LineString(g) => g.bounding_rect(), Geometry::Polygon(g) => g.bounding_rect(), Geometry::MultiPoint(g) => g.bounding_rect(), Geometry::MultiLineString(g) => g.bounding_rect(), Geometry::MultiPolygon(g) => g.bounding_rect(), Geometry::GeometryCollection(g) => g.bounding_rect(), Geometry::Rect(g) => Some(g.bounding_rect()), Geometry::Triangle(g) => Some(g.bounding_rect()), } } } ``` We now also need an almost identical impl like this: ``` impl<T> BoundingRect<T> for GeometryCow<'_, T> where T: CoordNum, { type Output = Option<Rect<T>>; fn bounding_rect(&self) -> Self::Output { match self { GeometryCow::Point(g) => Some(g.bounding_rect()), GeometryCow::Line(g) => Some(g.bounding_rect()), GeometryCow::LineString(g) => g.bounding_rect(), GeometryCow::Polygon(g) => g.bounding_rect(), GeometryCow::MultiPoint(g) => g.bounding_rect(), GeometryCow::MultiLineString(g) => g.bounding_rect(), GeometryCow::MultiPolygon(g) => g.bounding_rect(), GeometryCow::GeometryCollection(g) => g.bounding_rect(), GeometryCow::Rect(g) => Some(g.bounding_rect()), GeometryCow::Triangle(g) => Some(g.bounding_rect()), } } } ``` This is doable... but is there a better way? Some way to avoid all the mostly duplication? Or is there some more conventional way this could be done?
- Loading branch information
1 parent
484f87e
commit c7de1ff
Showing
11 changed files
with
251 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
use crate::{ | ||
CoordNum, Geometry, GeometryCollection, Line, LineString, MultiLineString, MultiPoint, | ||
MultiPolygon, Point, Polygon, Rect, Triangle, | ||
}; | ||
use std::borrow::Cow; | ||
|
||
#[derive(PartialEq, Debug, Hash)] | ||
pub enum GeometryCow<'a, T> | ||
where | ||
T: CoordNum, | ||
{ | ||
Point(Cow<'a, Point<T>>), | ||
Line(Cow<'a, Line<T>>), | ||
LineString(Cow<'a, LineString<T>>), | ||
Polygon(Cow<'a, Polygon<T>>), | ||
MultiPoint(Cow<'a, MultiPoint<T>>), | ||
MultiLineString(Cow<'a, MultiLineString<T>>), | ||
MultiPolygon(Cow<'a, MultiPolygon<T>>), | ||
GeometryCollection(Cow<'a, GeometryCollection<T>>), | ||
Rect(Cow<'a, Rect<T>>), | ||
Triangle(Cow<'a, Triangle<T>>), | ||
} | ||
|
||
impl<'a, T: CoordNum> From<&'a Geometry<T>> for GeometryCow<'a, T> { | ||
fn from(geometry: &'a Geometry<T>) -> Self { | ||
match geometry { | ||
Geometry::Point(g) => GeometryCow::Point(Cow::Borrowed(g)), | ||
Geometry::Line(g) => GeometryCow::Line(Cow::Borrowed(g)), | ||
Geometry::LineString(g) => GeometryCow::LineString(Cow::Borrowed(g)), | ||
Geometry::Polygon(g) => GeometryCow::Polygon(Cow::Borrowed(g)), | ||
Geometry::MultiPoint(g) => GeometryCow::MultiPoint(Cow::Borrowed(g)), | ||
Geometry::MultiLineString(g) => GeometryCow::MultiLineString(Cow::Borrowed(g)), | ||
Geometry::MultiPolygon(g) => GeometryCow::MultiPolygon(Cow::Borrowed(g)), | ||
Geometry::GeometryCollection(g) => GeometryCow::GeometryCollection(Cow::Borrowed(g)), | ||
Geometry::Rect(g) => GeometryCow::Rect(Cow::Borrowed(g)), | ||
Geometry::Triangle(g) => GeometryCow::Triangle(Cow::Borrowed(g)), | ||
} | ||
} | ||
} | ||
|
||
impl<'a, T: CoordNum> From<&'a Point<T>> for GeometryCow<'a, T> { | ||
fn from(point: &'a Point<T>) -> Self { | ||
GeometryCow::Point(Cow::Borrowed(point)) | ||
} | ||
} | ||
|
||
impl<'a, T: CoordNum> From<&'a LineString<T>> for GeometryCow<'a, T> { | ||
fn from(line_string: &'a LineString<T>) -> Self { | ||
GeometryCow::LineString(Cow::Borrowed(line_string)) | ||
} | ||
} | ||
|
||
impl<'a, T: CoordNum> From<&'a Line<T>> for GeometryCow<'a, T> { | ||
fn from(line: &'a Line<T>) -> Self { | ||
GeometryCow::Line(Cow::Borrowed(line)) | ||
} | ||
} | ||
|
||
impl<'a, T: CoordNum> From<&'a Polygon<T>> for GeometryCow<'a, T> { | ||
fn from(polygon: &'a Polygon<T>) -> Self { | ||
GeometryCow::Polygon(Cow::Borrowed(polygon)) | ||
} | ||
} | ||
|
||
impl<'a, T: CoordNum> From<&'a MultiPoint<T>> for GeometryCow<'a, T> { | ||
fn from(multi_point: &'a MultiPoint<T>) -> GeometryCow<'a, T> { | ||
GeometryCow::MultiPoint(Cow::Borrowed(multi_point)) | ||
} | ||
} | ||
|
||
impl<'a, T: CoordNum> From<&'a MultiLineString<T>> for GeometryCow<'a, T> { | ||
fn from(multi_line_string: &'a MultiLineString<T>) -> Self { | ||
GeometryCow::MultiLineString(Cow::Borrowed(multi_line_string)) | ||
} | ||
} | ||
|
||
impl<'a, T: CoordNum> From<&'a MultiPolygon<T>> for GeometryCow<'a, T> { | ||
fn from(multi_polygon: &'a MultiPolygon<T>) -> Self { | ||
GeometryCow::MultiPolygon(Cow::Borrowed(multi_polygon)) | ||
} | ||
} | ||
|
||
impl<'a, T: CoordNum> From<&'a GeometryCollection<T>> for GeometryCow<'a, T> { | ||
fn from(geometry_collection: &'a GeometryCollection<T>) -> Self { | ||
GeometryCow::GeometryCollection(Cow::Borrowed(geometry_collection)) | ||
} | ||
} | ||
|
||
impl<'a, T: CoordNum> From<&'a Rect<T>> for GeometryCow<'a, T> { | ||
fn from(rect: &'a Rect<T>) -> Self { | ||
GeometryCow::Rect(Cow::Borrowed(rect)) | ||
} | ||
} | ||
|
||
impl<'a, T: CoordNum> From<&'a Triangle<T>> for GeometryCow<'a, T> { | ||
fn from(triangle: &'a Triangle<T>) -> Self { | ||
GeometryCow::Triangle(Cow::Borrowed(triangle)) | ||
} | ||
} |
Oops, something went wrong.
c7de1ff
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦
Commit message is out of date - the GeometryCow actually looks like this (with a
Cow
not just a reference):