Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support generic numeric type for geometries #30

Merged
merged 16 commits into from
Jul 17, 2016
Merged

Conversation

zarch
Copy link
Contributor

@zarch zarch commented May 18, 2016

Hi, here a revised version to add generics to geometries (see previous pull request #25).
I create a separate branch called: generics to make it easier to keep it sync with the official master.
I've modified the code to remove most of the Neg<Output = T> as suggested by @jdroenner , I was not able to remove Copy without compilation errors so it is still there.

As suggested by @jdroenner we can just pass the reference, in this way we can remove most of the Copy, but it required to add some clone() to pass the tests, here you can see the difference:

diff --git a/src/types.rs b/src/types.rs
index 78ba3f4..dae8244 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -4,16 +4,16 @@ use std::ops::Sub;

 use num::Num;

-#[derive(PartialEq, Clone, Copy, Debug)]
+#[derive(PartialEq, Clone, Debug)]
 pub struct Coordinate<T>
-    where T: Num + Copy
+    where T: Num
 {
     pub x: T,
     pub y: T,
 }

-#[derive(PartialEq, Clone, Copy, Debug)]
-pub struct Point<T> (pub Coordinate<T>) where T: Num + Copy;
+#[derive(PartialEq, Clone, Debug)]
+pub struct Point<T> (pub Coordinate<T>) where T: Num;

 impl<T> Point<T>
     where T: Num + Copy
@@ -25,8 +25,8 @@ impl<T> Point<T>
     ///
     /// let p = Point::new(1.234, 2.345);
     ///
-    /// assert_eq!(p.x(), 1.234);
-    /// assert_eq!(p.y(), 2.345);
+    /// assert_eq!(*p.x(), 1.234);
+    /// assert_eq!(*p.y(), 2.345);
     /// ```
     pub fn new(x: T, y: T) -> Point<T> {
         Point(Coordinate {
@@ -42,10 +42,10 @@ impl<T> Point<T>
     ///
     /// let p = Point::new(1.234, 2.345);
     ///
-    /// assert_eq!(p.x(), 1.234);
+    /// assert_eq!(*p.x(), 1.234);
     /// ```
-    pub fn x(&self) -> T {
-        self.0.x
+    pub fn x(&self) -> &T {
+        &self.0.x
     }

     /// Sets the x/horizontal component of the point.
@@ -56,7 +56,7 @@ impl<T> Point<T>
     /// let mut p = Point::new(1.234, 2.345);
     /// p.set_x(9.876);
     ///
-    /// assert_eq!(p.x(), 9.876);
+    /// assert_eq!(*p.x(), 9.876);
     /// ```
     pub fn set_x(&mut self, x: T) -> &mut Point<T> {
         self.0.x = x;
@@ -70,10 +70,10 @@ impl<T> Point<T>
     ///
     /// let p = Point::new(1.234, 2.345);
     ///
-    /// assert_eq!(p.y(), 2.345);
+    /// assert_eq!(*p.y(), 2.345);
     /// ```
-    pub fn y(&self) -> T {
-        self.0.y
+    pub fn y(&self) -> &T {
+        &self.0.y
     }

     /// Sets the y/vertical component of the point.
@@ -84,7 +84,7 @@ impl<T> Point<T>
     /// let mut p = Point::new(1.234, 2.345);
     /// p.set_y(9.876);
     ///
-    /// assert_eq!(p.y(), 9.876);
+    /// assert_eq!(*p.y(), 9.876);
     /// ```
     pub fn set_y(&mut self, y: T) -> &mut Point<T> {
         self.0.y = y;
@@ -98,9 +98,9 @@ impl<T> Point<T>
     ///
     /// let p = Point::new(1.234, 2.345);
     ///
-    /// assert_eq!(p.lng(), 1.234);
+    /// assert_eq!(*p.lng(), 1.234);
     /// ```
-    pub fn lng(&self) -> T {
+    pub fn lng(&self) -> &T {
         self.x()
     }

@@ -112,7 +112,7 @@ impl<T> Point<T>
     /// let mut p = Point::new(1.234, 2.345);
     /// p.set_lng(9.876);
     ///
-    /// assert_eq!(p.lng(), 9.876);
+    /// assert_eq!(*p.lng(), 9.876);
     /// ```
     pub fn set_lng(&mut self, lng: T) -> &mut Point<T> {
         self.set_x(lng)
@@ -125,9 +125,9 @@ impl<T> Point<T>
     ///
     /// let p = Point::new(1.234, 2.345);
     ///
-    /// assert_eq!(p.lat(), 2.345);
+    /// assert_eq!(*p.lat(), 2.345);
     /// ```
-    pub fn lat(&self) -> T {
+    pub fn lat(&self) -> &T {
         self.y()
     }

@@ -139,7 +139,7 @@ impl<T> Point<T>
     /// let mut p = Point::new(1.234, 2.345);
     /// p.set_lat(9.876);
     ///
-    /// assert_eq!(p.lat(), 9.876);
+    /// assert_eq!(*p.lat(), 9.876);
     /// ```
     pub fn set_lat(&mut self, lat: T) -> &mut Point<T> {
         self.set_y(lat)
@@ -157,7 +157,7 @@ impl<T> Point<T>
     /// assert_eq!(dot, 5.25);
     /// ```
     pub fn dot(&self, point: &Point<T>) -> T {
-        self.x() * point.x() + self.y() * point.y()
+        *self.x() * *point.x() + *self.y() * *point.y()
     }
 }

@@ -173,11 +173,11 @@ impl<T> Neg for Point<T>
     ///
     /// let p = -Point::new(-1.25, 2.5);
     ///
-    /// assert_eq!(p.x(), 1.25);
-    /// assert_eq!(p.y(), -2.5);
+    /// assert_eq!(*p.x(), 1.25);
+    /// assert_eq!(*p.y(), -2.5);
     /// ```
     fn neg(self) -> Point<T> {
-        Point::new(-self.x(), -self.y())
+        Point::new(-*self.x(), -*self.y())
     }
 }

@@ -193,11 +193,11 @@ impl<T> Add for Point<T>
     ///
     /// let p = Point::new(1.25, 2.5) + Point::new(1.5, 2.5);
     ///
-    /// assert_eq!(p.x(), 2.75);
-    /// assert_eq!(p.y(), 5.0);
+    /// assert_eq!(*p.x(), 2.75);
+    /// assert_eq!(*p.y(), 5.0);
     /// ```
     fn add(self, rhs: Point<T>) -> Point<T> {
-        Point::new(self.x() + rhs.x(), self.y() + rhs.y())
+        Point::new(*self.x() + *rhs.x(), *self.y() + *rhs.y())
     }
 }

@@ -213,35 +213,35 @@ impl<T> Sub for Point<T>
     ///
     /// let p = Point::new(1.25, 3.0) - Point::new(1.5, 2.5);
     ///
-    /// assert_eq!(p.x(), -0.25);
-    /// assert_eq!(p.y(), 0.5);
+    /// assert_eq!(*p.x(), -0.25);
+    /// assert_eq!(*p.y(), 0.5);
     /// ```
     fn sub(self, rhs: Point<T>) -> Point<T> {
-        Point::new(self.x() - rhs.x(), self.y() - rhs.y())
+        Point::new(*self.x() - *rhs.x(), *self.y() - *rhs.y())
     }
 }

 #[derive(PartialEq, Clone, Debug)]
-pub struct MultiPoint<T>(pub Vec<Point<T>>) where T: Num + Copy;
+pub struct MultiPoint<T>(pub Vec<Point<T>>) where T: Num;

 #[derive(PartialEq, Clone, Debug)]
-pub struct LineString<T>(pub Vec<Point<T>>) where T: Num + Copy;
+pub struct LineString<T>(pub Vec<Point<T>>) where T: Num;

 #[derive(PartialEq, Clone, Debug)]
-pub struct MultiLineString<T>(pub Vec<LineString<T>>) where T: Num + Copy;
+pub struct MultiLineString<T>(pub Vec<LineString<T>>) where T: Num;

 #[derive(PartialEq, Clone, Debug)]
-pub struct Polygon<T>(pub LineString<T>, pub Vec<LineString<T>>) where T: Num + Copy;
+pub struct Polygon<T>(pub LineString<T>, pub Vec<LineString<T>>) where T: Num;

 #[derive(PartialEq, Clone, Debug)]
-pub struct MultiPolygon<T>(pub Vec<Polygon<T>>) where T: Num + Copy;
+pub struct MultiPolygon<T>(pub Vec<Polygon<T>>) where T: Num;

 #[derive(PartialEq, Clone, Debug)]
-pub struct GeometryCollection<T>(pub Vec<Geometry<T>>) where T: Num + Copy;
+pub struct GeometryCollection<T>(pub Vec<Geometry<T>>) where T: Num;

 #[derive(PartialEq, Clone, Debug)]
 pub enum Geometry<T>
-    where T: Num + Copy
+    where T: Num
 {
     Point(Point<T>),
     LineString(LineString<T>),
@@ -263,9 +263,9 @@ mod test {
             y: 116.34
         };

-        let p = Point(c);
+        let p = Point(c.clone());

-        let Point(c2) = p;
+        let Point(c2) = p.clone();
         assert_eq!(c, c2);
         assert_eq!(c.x, c2.x);
         assert_eq!(c.y, c2.y);

Other ideas to improve this? should we pass the Copy or the reference?

@@ -163,13 +169,17 @@ impl Point {
///
/// assert!(dist < 1e-1)
/// ```
pub fn distance_to(&self, point: &Point) -> f64 {
((self.x() - point.x()).powi(2) + (self.y() - point.y()).powi(2)).sqrt()
pub fn distance_to(&self, point: &Point<T>) -> f64 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If instead of using num::Num everywhere, we used num::Float, we could make distance_to generic by utilizing num::Float::sqrt.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also means you can remove the explicit Copy trait bounds specifying everywhere since Float is a subtype of Copy.

@frewsxcv
Copy link
Member

I wouldn't be too worried about the Copy trait bound right now. In the future, we can look into removing it if it's desired, but for now, I think it's okay to have Copy.

@frewsxcv
Copy link
Member

Looks great, awesome work!

In general, I think it'd be great if all the operations could be made generic. Then we could remove the ToPrimitive trait bound as well.

let x1 : f64 = p1.x().to_f64().unwrap();
let x2 : f64 = p2.x().to_f64().unwrap();
let y1 : f64 = p1.y().to_f64().unwrap();
let y2 : f64 = p2.y().to_f64().unwrap();
total_length += segment_len;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you try to make this generic, the type checker might complain. You might need to make this into total_length = total_length + segment_len;

@Kerollmops
Copy link
Contributor

Up ? 😄

@zarch
Copy link
Contributor Author

zarch commented Jul 13, 2016

Hi Kerosene2000,
actually I've started the merging between the master and this branch, but actually I'm new to rust, and I'm stuck to some compile errors that I don't know how to fix. I push the changes on the branch. Perhaps someone can help to fix them.

@Kerollmops
Copy link
Contributor

Kerollmops commented Jul 13, 2016

Here is the pull request I make to @zarch zarch#1. This way we can continue using this thread to speak and don't create another one.

@jdroenner
Copy link
Member

Hi @Kerosene2000 and @zarch,
maybe you could replace lon()/lat() with x()/y() in your pull requests :) Using both of them is a bit confusing.

@Kerollmops
Copy link
Contributor

Done 😄

sum_x += tmp * p.lng();
sum_y += tmp * p.lat();
sum_x = sum_x + tmp * p.lng();
sum_y = sum_y + tmp * p.lat();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You forgot this one ;-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have to look at here 😄
Until @zarch merge with its branch

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, didn't see the pull request on the pull request 👼

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha 😸

@zarch
Copy link
Contributor Author

zarch commented Jul 14, 2016

can you explain ?

Sorry, I didn't notice it, I've just accept your PR...
But it seems fix to me right now, isn't it?

@Kerollmops
Copy link
Contributor

I think so !

@frewsxcv
Copy link
Member

This looks great! Upon a quick glance, it looks like everything has been made generic. Is there anything else to do here before final reviews + merging?

fn centroid(&self) -> Option<Point> {
// TODO: consideration of inner polygons;
fn centroid(&self) -> Option<Point<T>> {
// TODO: consideration of inner polygons;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: extra whitespace in front here

@frewsxcv
Copy link
Member

I just did a review. Besides the commented out code, which can be removed, I don't see any other changes that need to be made. Anyone else have thoughts?

let (dx, dy) = (self.x() - p.x(), self.y() - p.y());
// (dx * dx + dy * dy)**(T::one()/(T::one() + T::one()))
// ((self.x() - p.x()).powi(2) + (self.y() - p.y()).powi(2)).sqrt()
(dx * dx + dy * dy).sqrt() // FIXME is it correct ??? (euclidian dist)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These comments can be removed. The logic seems fine to me.

@frewsxcv
Copy link
Member

Thank you @zarch and @Kerosene2000 for your contributions here! I'm looking forward to these changes! 🎊

I thought about squashing, but:

  1. I don't think the git history is too messy
  2. I want both @zarch and @Kerosene2000 to get credit for their commits
  3. There is some value in seeing the evolution of ideas

I'm going to merge now, but I won't plan on publishing a new version of rust-geo (version 0.2) until Sunday evening Eastern time just to give some time in-case anyone has additional concerns.

@frewsxcv frewsxcv merged commit e23eaa6 into georust:master Jul 17, 2016
@frewsxcv
Copy link
Member

Version 0.2.0 of geo-rust has been released which includes these changes.

@zarch zarch deleted the generics branch July 21, 2016 05:13
nyurik pushed a commit to nyurik/geo that referenced this pull request Mar 19, 2022
Merge georust#30 a=@frewsxcv r=@frewsxcv
________________________________________________________________________

I'm the only contributor, so this should be a straightforward license
change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants