From 2415dfd07d341c676a1739c6f4407769a613d6a1 Mon Sep 17 00:00:00 2001 From: Stani Date: Thu, 2 Jul 2015 17:02:59 +0200 Subject: [PATCH 1/4] add useful methods to points and rectangles with 100% test coverage --- .gitignore | 4 + geom.go | 361 ++++++++++++++++++++++++++++++++++++++++++++++++++- geom_test.go | 333 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 694 insertions(+), 4 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c9ff03 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +\#* +*.test +coverage.* +private diff --git a/geom.go b/geom.go index a8c08a0..fdc72ee 100644 --- a/geom.go +++ b/geom.go @@ -26,6 +26,7 @@ package polyclip import ( + "fmt" "math" ) @@ -43,11 +44,173 @@ func (p Point) Length() float64 { return math.Sqrt(p.X*p.X + p.Y*p.Y) } +// String returns a string representation of p like "(3,4)". +func (p Point) String() string { + return fmt.Sprintf("(%.2f,%.2f)", p.X, p.Y) + //return "(" + strconv.FormatFloat(p.X, 'f', 2, 64) + "," + strconv.FormatFloat(p.Y, 'f', 2, 64) + ")" +} + +// Add returns the vector p+q. +func (p Point) Add(q Point) Point { + return Point{p.X + q.X, p.Y + q.Y} +} + +// Sub returns the vector p-q. +func (p Point) Sub(q Point) Point { + return Point{p.X - q.X, p.Y - q.Y} +} + +// Mul returns the vector p*k. +func (p Point) Mul(k float64) Point { + return Point{p.X * k, p.Y * k} +} + +// Div returns the vector p/k. +func (p Point) Div(k float64) Point { + return Point{p.X / k, p.Y / k} +} + +// In reports whether p is in r. +func (p Point) In(r Rectangle) bool { + return r.Min.X <= p.X && p.X < r.Max.X && + r.Min.Y <= p.Y && p.Y < r.Max.Y +} + +// Angle returns the angle in radians from the origin to p and q +func (p Point) Angle(q Point) float64 { + angle := math.Acos(p.Dot(q) / (p.Norm() * q.Norm())) + if p.Cross(q) < 0 { + angle *= -1 + } + return angle +} + +// Angle3 returns the angle in radians from p to q and r +func (p Point) Angle3(q, r Point) float64 { + return q.Sub(p).Angle(r.Sub(p)) +} + +// Append appends a point to seperate x, y float slices +func (p Point) Append(x, y []float64) ([]float64, []float64) { + return append(x, p.X), append(y, p.Y) +} + +// Cross product of p and q +func (p Point) Cross(q Point) float64 { + return p.X*q.Y - p.Y*q.X +} + +// Dist returns distance to another point +func (p Point) Dist(q Point) float64 { + dx := p.X - q.X + dy := p.Y - q.Y + return math.Sqrt(dx*dx + dy*dy) +} + +// Dist2 returns squared distance to another point +func (p Point) Dist2(q Point) float64 { + dx := p.X - q.X + dy := p.Y - q.Y + return dx*dx + dy*dy +} + +// Dot returns the dot product of this vector with another. There are multiple ways +// to describe this value. One is the multiplication of their lengths and cos(theta) where +// theta is the angle between the vectors: v1.v2 = |v1||v2|cos(theta). +// +// The other (and what is actually done) is the sum of the element-wise multiplication of all +// elements. So for instance, two Vec3s would yield v1.x * v2.x + v1.y * v2.y + v1.z * v2.z. +// +// This means that the dot product of a vector and itself is the square of its Len (within +// the bounds of floating points error). +// +// The dot product is roughly a measure of how closely two vectors are to pointing in the same +// direction. If both vectors are normalized, the value will be -1 for opposite pointing, +// one for same pointing, and 0 for perpendicular vectors. +func (p Point) Dot(q Point) float64 { + return p.X*q.X + p.Y*q.Y +} + +// MaxRadius returns the maximum distance to the four corners of a rectangle +func (p Point) MaxRadius(r Rectangle) float64 { + d := math.Max( + p.Dist2(r.Min), // upper left + p.Dist2(r.Max)) // down right + d = math.Max(d, p.Dist2(Point{float64(r.Min.X), float64(r.Max.Y)})) // down left + d = math.Max(d, p.Dist2(Point{float64(r.Max.X), float64(r.Min.Y)})) // upper right + return math.Sqrt(d) +} + +// Negate returns a point with the opposite coordinates +func (p Point) Negate() Point { + return Point{-p.X, -p.Y} +} + +// Norm returns the norm/length of this vector +func (p Point) Norm() float64 { + return math.Sqrt(p.X*p.X + p.Y*p.Y) +} + +// Norm2 returns the squared norm/length of this vector +func (p Point) Norm2() float64 { + return p.X*p.X + p.Y*p.Y +} + +// Normalize returns the normalizes a vector to a certain length +func (p Point) Normalize(length ...float64) Point { + l := 1.0 + if len(length) != 0 { + l = length[0] + } + return p.Div(p.Norm() / l) +} + +// Normals are the two vectors perpendicular to line pq +func (p Point) Normals(q Point) (Point, Point) { + dx := q.X - p.X + dy := q.Y - p.Y + return Point{-dy, dx}, Point{dy, -dx} +} + +// Polar converts polar to cartesion coordinates +func (p Point) Polar(r, a float64) Point { + return p.Add(Point{r * math.Cos(a), r * math.Sin(a)}) +} + +// Prepend prepends a point to seperate x, y float slices +func (p Point) Prepend(x, y []float64) ([]float64, []float64) { + return append([]float64{p.X}, x...), append([]float64{p.Y}, y...) +} + +// Rect creates a rectangle with the point as center and certain size +func (p Point) Rect(width, height float64) Rectangle { + half := Point{width / 2, height / 2} + return Rectangle{p.Sub(half), p.Add(half)} +} + +// Tangent returns unit tangent from p to q +func (p Point) Tangent(q Point) Point { + return q.Sub(p).Normalize() +} + +// ZP is the zero Point. +var ZP Point + +// Pt is shorthand for Point{X, Y}. +func Pt(X, Y float64) Point { + return Point{X, Y} +} + +// A Rectangle contains the points with Min.X <= X < Max.X, Min.Y <= Y < Max.Y. +// It is well-formed if Min.X <= Max.X and likewise for Y. Points are always +// well-formed. A rectangle's methods always return well-formed outputs for +// well-formed inputs. type Rectangle struct { Min, Max Point } -func (r1 Rectangle) union(r2 Rectangle) Rectangle { +// Union returns the smallest rectangle that contains both r and s. +func (r1 Rectangle) Union(r2 Rectangle) Rectangle { return Rectangle{ Min: Point{ X: math.Min(r1.Min.X, r2.Min.X), @@ -65,6 +228,200 @@ func (r1 Rectangle) Overlaps(r2 Rectangle) bool { r1.Min.Y <= r2.Max.Y && r1.Max.Y >= r2.Min.Y } +// String returns a string representation of r like "(3,4)-(6,5)". +func (r Rectangle) String() string { + return fmt.Sprintf("%s-%s", r.Min, r.Max) +} + +// Dx returns r's width. +func (r Rectangle) Dx() float64 { + return r.Max.X - r.Min.X +} + +// Dy returns r's height. +func (r Rectangle) Dy() float64 { + return r.Max.Y - r.Min.Y +} + +// Size returns r's width and height. +func (r Rectangle) Size() Point { + return Point{ + r.Max.X - r.Min.X, + r.Max.Y - r.Min.Y, + } +} + +// Add returns the rectangle r translated by p. +func (r Rectangle) Add(p Point) Rectangle { + return Rectangle{ + Point{r.Min.X + p.X, r.Min.Y + p.Y}, + Point{r.Max.X + p.X, r.Max.Y + p.Y}, + } +} + +// Sub returns the rectangle r translated by -p. +func (r Rectangle) Sub(p Point) Rectangle { + return Rectangle{ + Point{r.Min.X - p.X, r.Min.Y - p.Y}, + Point{r.Max.X - p.X, r.Max.Y - p.Y}, + } +} + +// Inset returns the rectangle r inset by n, which may be negative. If either +// of r's dimensions is less than 2*n then an empty rectangle near the center +// of r will be returned. +func (r Rectangle) Inset(n float64) Rectangle { + if r.Dx() < 2*n { + r.Min.X = (r.Min.X + r.Max.X) / 2 + r.Max.X = r.Min.X + } else { + r.Min.X += n + r.Max.X -= n + } + if r.Dy() < 2*n { + r.Min.Y = (r.Min.Y + r.Max.Y) / 2 + r.Max.Y = r.Min.Y + } else { + r.Min.Y += n + r.Max.Y -= n + } + return r +} + +// Intersect returns the largest rectangle contained by both r and s. If the +// two rectangles do not overlap then the zero rectangle will be returned. +func (r Rectangle) Intersect(s Rectangle) Rectangle { + if r.Min.X < s.Min.X { + r.Min.X = s.Min.X + } + if r.Min.Y < s.Min.Y { + r.Min.Y = s.Min.Y + } + if r.Max.X > s.Max.X { + r.Max.X = s.Max.X + } + if r.Max.Y > s.Max.Y { + r.Max.Y = s.Max.Y + } + if r.Min.X > r.Max.X || r.Min.Y > r.Max.Y { + return ZR + } + return r +} + +// Empty reports whether the rectangle contains no points. +func (r Rectangle) Empty() bool { + return r.Min.X >= r.Max.X || r.Min.Y >= r.Max.Y +} + +// Equals reports whether r and s are equal. +func (r Rectangle) Equals(s Rectangle) bool { + return r.Min.X == s.Min.X && r.Min.Y == s.Min.Y && + r.Max.X == s.Max.X && r.Max.Y == s.Max.Y +} + +// In reports whether every point in r is in s. +func (r Rectangle) In(s Rectangle) bool { + if r.Empty() { + return true + } + // Note that r.Max is an exclusive bound for r, so that r.In(s) + // does not require that r.Max.In(s). + return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X && + s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y +} + +// Canon returns the canonical version of r. The returned rectangle has minimum +// and maximum coordinates swapped if necessary so that it is well-formed. +func (r Rectangle) Canon() Rectangle { + if r.Max.X < r.Min.X { + r.Min.X, r.Max.X = r.Max.X, r.Min.X + } + if r.Max.Y < r.Min.Y { + r.Min.Y, r.Max.Y = r.Max.Y, r.Min.Y + } + return r +} + +// AddPoint expands the rectangle so it contains the point +func (r Rectangle) AddPoint(p Point) Rectangle { + return r.Union(Rectangle{p, p}) +} + +// Center returns the center of the rectangle +func (r Rectangle) Center() Point { + return r.Min.Add(r.Max).Div(2) +} + +// Diagonal returns the diagonal of a rectangle +func (r Rectangle) Diagonal() float64 { + w := r.Dx() + h := r.Dy() + return math.Sqrt(w*w + h*h) +} + +// Expand returns an rectangle that has been expanded by dx, dy +func (r Rectangle) Expand(dx, dy float64) Rectangle { + return r.Center().Rect(r.Dx()+dx, r.Dy()+dy) +} + +// MaxRadius returns the maximum distance to the four corners of a rectangle +func (r Rectangle) MaxRadius(p Point) float64 { + d1 := math.Max( + p.Dist2(r.Min), // upper left + p.Dist2(r.Max)) // down right + d2 := math.Max( + p.Dist2(Point{r.Min.X, r.Max.Y}), // down left + p.Dist2(Point{r.Max.X, r.Min.Y}), // upper right + ) + return math.Sqrt(math.Max(d1, d2)) +} + +// Offset returns an rectangle that has been expanded by d on all sides +func (r Rectangle) Offset(d float64) Rectangle { + return r.Expand(2*d, 2*d) +} + +// Points return the points of an rectangle counter clock wise +func (r Rectangle) Points() []Point { + min := r.Min + max := r.Max + return []Point{min, Point{min.X, max.Y}, max, Point{max.X, min.Y}} + // reverse + // return []Point{Point{max.X, min.Y}, max, Point{min.X, max.Y}, min} +} + +// ZR is the zero Rectangle. +var ZR Rectangle + +// Rect is shorthand for Rectangle{Pt(x0, y0), Pt(x1, y1)}. +func Rect(x0, y0, x1, y1 float64) Rectangle { + if x0 > x1 { + x0, x1 = x1, x0 + } + if y0 > y1 { + y0, y1 = y1, y0 + } + return Rectangle{Point{x0, y0}, Point{x1, y1}} +} + +// RectFromPoints constructs a rect that contains the given points. +func RectFromPoints(pts ...Point) Rectangle { + switch len(pts) { + case 0: + return Rectangle{} + case 1: + return Rectangle{pts[0], pts[0]} + } + + r := Rect(pts[0].X, pts[0].Y, pts[1].X, pts[1].Y) + + for _, p := range pts[2:] { + r = r.AddPoint(p) + } + return r +} + // Used to represent an edge of a polygon. type segment struct { start, end Point @@ -179,7 +536,7 @@ func (p Polygon) NumVertices() int { func (p Polygon) BoundingBox() Rectangle { bb := p[0].BoundingBox() for _, c := range p[1:] { - bb = bb.union(c.BoundingBox()) + bb = bb.Union(c.BoundingBox()) } return bb diff --git a/geom_test.go b/geom_test.go index d0d2fff..fea78fb 100644 --- a/geom_test.go +++ b/geom_test.go @@ -43,6 +43,189 @@ func TestPoint(t *T) { verify(t, circa(Point{3, 4}.Length(), 5), "Expected length 5") } +func ExamplePoint_Add() { + p := Point{0.8, 2.5} + q := Point{0.2, 3} + fmt.Println(p.Add(q)) + // Output: (1.00,5.50) +} + +func ExamplePoint_Sub() { + p := Point{0.8, 5} + q := Point{0.2, 0.5} + fmt.Println(p.Sub(q)) + // Output: (0.60,4.50) +} + +func ExamplePoint_Mul() { + p := Point{0.2, 0.5} + fmt.Println(p.Mul(2)) + // Output: (0.40,1.00) +} + +func ExamplePoint_Div() { + p := Point{0.2, 0.5} + fmt.Println(p.Div(2)) + // Output: (0.10,0.25) +} + +func ExamplePoint_In() { + p := Point{3, 4} + q := Point{0, 0} + r := Rect(1, 2, 9, 8) + fmt.Println(p.In(r)) + fmt.Println(q.In(r)) + // Output: + // true + // false +} + +func ExamplePoint_Equals() { + p := Point{3, 4} + q := Point{0, 0} + r := Pt(3, 4) + fmt.Println(p.Equals(r)) + fmt.Println(q.Equals(r)) + // Output: + // true + // false +} + +// Extra functions (not supported by image.Point) + +func ExamplePoint_Angle() { + p := Point{1, 0} + fmt.Println(p.Angle(Point{1, 1})) // 45 + fmt.Println(p.Angle(Point{1, -1})) // -45 + // Output: + // 0.7853981633974484 + // -0.7853981633974484 +} + +func ExamplePoint_Angle3() { + p := Point{1, 1} + q := Point{2, 1} + fmt.Println(p.Angle3(q, Point{2, 2})) + fmt.Println(p.Angle3(q, Point{2, 0})) + // Output: + // 0.7853981633974484 + // -0.7853981633974484 +} + +func ExamplePoint_Append() { + p := Point{1, 2} + x := []float64{0, 1, 2} + y := []float64{3, 4, 5} + x, y = p.Append(x, y) + fmt.Println(x) + fmt.Println(y) + // Output: + // [0 1 2 1] + // [3 4 5 2] +} + +func ExamplePoint_Cross() { + p := Point{1, 2} + q := Point{3, 4} + fmt.Println(p.Cross(q)) + // Output: -2 +} + +func ExamplePoint_Dist() { + p := Point{1, 1} + q := Point{1, 5} + fmt.Println(p.Dist(q)) + // Output: 4 +} + +func ExamplePoint_Dist2() { + p := Point{1, 1} + q := Point{1, 5} + fmt.Println(p.Dist2(q)) + // Output: 16 +} + +func ExamplePoint_Dot() { + p := Point{1, 2} + q := Point{3, 4} + fmt.Println(p.Dot(q)) + // Output: 11 +} + +func ExamplePoint_MaxRadius() { + rect := Rect(1, 2, 6, 9) + p := Point{1, 2} + fmt.Println(p.MaxRadius(rect)) + // Output: 8.602325267042627 +} + +func ExamplePoint_Norm() { + p := Point{1, 2} + fmt.Println(p.Norm() == math.Sqrt(5)) + // Output: true +} + +func ExamplePoint_Norm2() { + p := Point{1, 2} + fmt.Println(p.Norm2()) + // Output: 5 +} + +func ExamplePoint_Normalize() { + p := Point{1, 2} + fmt.Println(p.Normalize()) + fmt.Println(p.Normalize() == p.Normalize(1)) + fmt.Println(p.Normalize(2)) + // Output: + // (0.45,0.89) + // true + // (0.89,1.79) +} + +func ExamplePoint_Negate() { + p := Point{1, 2} + fmt.Println(p.Negate()) + // Output: (-1.00,-2.00) +} + +func ExamplePoint_Normals() { + p := Point{1, 1} + q := Point{3, 3} + fmt.Println(p.Normals(q)) + // Output: (-2.00,2.00) (2.00,-2.00) +} + +func ExamplePoint_Polar() { + p := Point{3, 3} + fmt.Println(p.Polar(2, math.Pi/2)) + // Output: (3.00,5.00) +} + +func ExamplePoint_Prepend() { + p := Point{1, 2} + x := []float64{0, 1, 2} + y := []float64{3, 4, 5} + x, y = p.Prepend(x, y) + fmt.Println(x) + fmt.Println(y) + // Output: + // [1 0 1 2] + // [2 3 4 5] +} + +func ExamplePoint_Rect() { + p := Point{1, 2} + fmt.Println(p.Rect(8, 6)) + // Output: (-3.00,-1.00)-(5.00,5.00) +} + +func ExamplePoint_Tangent() { + p := Point{1, 2} + q := Point{3, 2} + fmt.Println(p.Tangent(q)) + // Output: (1.00,0.00) +} + func rect(x, y, w, h float64) Rectangle { return Rectangle{Min: Point{x, y}, Max: Point{x + w, y + h}} } @@ -53,7 +236,7 @@ func TestRectangleUnion(t *T) { {rect(10, 10, 10, 10), rect(-10, -10, 10, 10), rect(-10, -10, 30, 30)}, } for i, v := range cases { - u := v.a.union(v.b) + u := v.a.Union(v.b) r := v.result verify(t, u.Min.X == r.Min.X && u.Min.Y == r.Min.Y && u.Max.X == r.Max.X && u.Max.Y == r.Max.Y, "Expected equal rectangles in case %d", i) } @@ -82,6 +265,152 @@ func TestRectangleIntersects(t *T) { } } +func ExampleRectangle_String() { + fmt.Println(Rect(1, 2, 3, 4)) + // Output: (1.00,2.00)-(3.00,4.00) +} + +func ExampleRectFromPoints() { + fmt.Println(RectFromPoints()) + fmt.Println(RectFromPoints(Pt(2, 4))) + fmt.Println(RectFromPoints(Pt(0, 0), Pt(2, 4), Pt(-1, -2))) + // Output: + // (0.00,0.00)-(0.00,0.00) + // (2.00,4.00)-(2.00,4.00) + // (-1.00,-2.00)-(2.00,4.00) +} + +func ExampleRectangle_Dx() { + rect := Rect(1, 2, 3, 4) + fmt.Println(rect.Dx()) + // Output: 2 +} + +func ExampleRectangle_Dy() { + rect := Rect(1, 2, 3, 5) + fmt.Println(rect.Dy()) + // Output: 3 +} + +func ExampleRectangle_Size() { + rect := Rect(1, 2, 3, 5) + fmt.Println(rect.Size()) + // Output: (2.00,3.00) +} + +func ExampleRectangle_Add() { + rect := Rect(1, 2, 3, 5) + p := Point{6, 7} + fmt.Println(rect.Add(p)) + // Output: (7.00,9.00)-(9.00,12.00) +} + +func ExampleRectangle_Sub() { + rect := Rect(1, 2, 3, 5) + p := Point{6, 7} + fmt.Println(rect.Sub(p)) + // Output: (-5.00,-5.00)-(-3.00,-2.00) +} + +func ExampleRectangle_Inset() { + rect := Rect(1, 2, 3, 5) + fmt.Println(rect.Inset(0.25)) + fmt.Println(rect.Inset(2)) + // Output: + // (1.25,2.25)-(2.75,4.75) + // (2.00,3.50)-(2.00,3.50) +} + +func ExampleRectangle_Intersect() { + r := Rect(1, 2, 3, 5) + s := Rect(0, 0, 2, 3) + t := Rect(6, 7, 8, 9) + fmt.Println(r.Intersect(s)) + fmt.Println(r.Intersect(t) == ZR) + // Output: + // (1.00,2.00)-(2.00,3.00) + // true +} + +func ExampleRectangle_Union() { + r := Rect(1, 2, 3, 5) + s := Rect(0, 0, 3, 3) + t := Rect(6, 7, 8, 9) + fmt.Println(r.Union(s)) + fmt.Println(r.Union(t)) + // Output: + // (0.00,0.00)-(3.00,5.00) + // (1.00,2.00)-(8.00,9.00) +} + +func ExampleRectangle_Empty() { + fmt.Println(Rectangle{Min: Point{7, 8}, Max: Point{1, 2}}.Empty()) + fmt.Println(Rect(7, 8, 1, 2).Empty()) + // Output: + // true + // false +} + +func ExampleRectangle_Eq() { + r := Rectangle{Min: Point{1, 2}, Max: Point{7, 8}} + s := Rect(7, 8, 1, 2) + fmt.Println(r.Equals(s)) + // Output: + // true +} + +func ExampleRectangle_Overlaps() { + r := Rect(1, 2, 3, 5) + s := Rect(0, 0, 2, 3) + t := Rect(6, 7, 8, 9) + fmt.Println(r.Overlaps(s)) + fmt.Println(r.Overlaps(t)) + // Output: + // true + // false +} + +func ExampleRectangle_In() { + r := Rect(1, 2, 4, 5) + s := Rect(2, 3, 3, 4) + t := Rectangle{Min: Point{7, 8}, Max: Point{1, 2}} + fmt.Println(s.In(r)) + fmt.Println(t.In(r)) + // Output: + // true + // true +} + +func ExampleRectangle_Canon() { + r := Rectangle{Min: Point{7, 8}, Max: Point{1, 2}} + fmt.Println(r.Canon()) + // Output: (1.00,2.00)-(7.00,8.00) +} + +func ExampleRectangle_Diagonal() { + r := Rect(0, 0, 2, 2) + fmt.Println(r.Diagonal()) + // Output: 2.8284271247461903 +} + +func ExampleRectangle_MaxRadius() { + rect := Rect(1, 2, 6, 9) + p := Point{1, 2} + fmt.Println(rect.MaxRadius(p)) + // Output: 8.602325267042627 +} + +func ExampleRectangle_Offset() { + rect := Rect(1, 2, 6, 9) + fmt.Println(rect.Offset(4)) + // Output: (-3.00,-2.00)-(10.00,13.00) +} + +func ExampleRectangle_Points() { + rect := Rect(1, 2, 6, 9) + fmt.Println(rect.Points()) + // Output: [(1.00,2.00) (1.00,9.00) (6.00,9.00) (6.00,2.00)] +} func TestContourAdd(t *T) { c := Contour{} pp := []Point{{1, 2}, {3, 4}, {5, 6}} @@ -172,5 +501,5 @@ func ExamplePolygon_Construct() { } sort.Strings(out) fmt.Println(out) - // Output: [{1 1} {1 2} {2 1}] + // [(1.00,1.00) (1.00,2.00) (2.00,1.00)] } From 16a9a8f0ff96b0452606b83cda9d0f468722f62c Mon Sep 17 00:00:00 2001 From: Stani Date: Thu, 2 Jul 2015 17:08:20 +0200 Subject: [PATCH 2/4] remove deprecated comment --- geom.go | 1 - 1 file changed, 1 deletion(-) diff --git a/geom.go b/geom.go index fdc72ee..acd42ac 100644 --- a/geom.go +++ b/geom.go @@ -47,7 +47,6 @@ func (p Point) Length() float64 { // String returns a string representation of p like "(3,4)". func (p Point) String() string { return fmt.Sprintf("(%.2f,%.2f)", p.X, p.Y) - //return "(" + strconv.FormatFloat(p.X, 'f', 2, 64) + "," + strconv.FormatFloat(p.Y, 'f', 2, 64) + ")" } // Add returns the vector p+q. From e49b9371fdacf98290d557afa8d41f97af6425a8 Mon Sep 17 00:00:00 2001 From: Stani Date: Thu, 2 Jul 2015 17:29:54 +0200 Subject: [PATCH 3/4] pylint fixes --- endpoint.go | 32 ++++++++++++++++---------------- geom.go | 27 ++++++++++++++++----------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/endpoint.go b/endpoint.go index ea907ab..54646f8 100644 --- a/endpoint.go +++ b/endpoint.go @@ -48,18 +48,18 @@ func (e endpoint) String() string { " other:", e.other.p, " inout:", e.inout, " inside:", e.inside, " edgeType:", e.edgeType, "}") } -func (e1 *endpoint) equals(e2 *endpoint) bool { - return e1.p.Equals(e2.p) && - e1.left == e2.left && - e1.polygonType == e2.polygonType && - e1.other == e2.other && - e1.inout == e2.inout && - e1.edgeType == e2.edgeType && - e1.inside == e2.inside +func (e *endpoint) equals(ep *endpoint) bool { + return e.p.Equals(ep.p) && + e.left == ep.left && + e.polygonType == ep.polygonType && + e.other == ep.other && + e.inout == ep.inout && + e.edgeType == ep.edgeType && + e.inside == ep.inside } -func (se *endpoint) segment() segment { - return segment{se.p, se.other.p} +func (e *endpoint) segment() segment { + return segment{e.p, e.other.p} } func signedArea(p0, p1, p2 Point) float64 { @@ -68,13 +68,13 @@ func signedArea(p0, p1, p2 Point) float64 { } // Checks if this sweep event is below point p. -func (se *endpoint) below(x Point) bool { - if se.left { - return signedArea(se.p, se.other.p, x) > 0 +func (e *endpoint) below(x Point) bool { + if e.left { + return signedArea(e.p, e.other.p, x) > 0 } - return signedArea(se.other.p, se.p, x) > 0 + return signedArea(e.other.p, e.p, x) > 0 } -func (se *endpoint) above(x Point) bool { - return !se.below(x) +func (e *endpoint) above(x Point) bool { + return !e.below(x) } diff --git a/geom.go b/geom.go index acd42ac..60e9dda 100644 --- a/geom.go +++ b/geom.go @@ -30,13 +30,14 @@ import ( "math" ) +// Point is an X, Y coordinate pair. type Point struct { X, Y float64 } // Equals returns true if both p1 and p2 describe exactly the same point. -func (p1 Point) Equals(p2 Point) bool { - return p1.X == p2.X && p1.Y == p2.Y +func (p Point) Equals(q Point) bool { + return p.X == q.X && p.Y == q.Y } // Length returns distance from p to point (0, 0). @@ -209,22 +210,22 @@ type Rectangle struct { } // Union returns the smallest rectangle that contains both r and s. -func (r1 Rectangle) Union(r2 Rectangle) Rectangle { +func (r Rectangle) Union(s Rectangle) Rectangle { return Rectangle{ Min: Point{ - X: math.Min(r1.Min.X, r2.Min.X), - Y: math.Min(r1.Min.Y, r2.Min.Y), + X: math.Min(r.Min.X, s.Min.X), + Y: math.Min(r.Min.Y, s.Min.Y), }, Max: Point{ - X: math.Max(r1.Max.X, r2.Max.X), - Y: math.Max(r1.Max.Y, r2.Max.Y), + X: math.Max(r.Max.X, s.Max.X), + Y: math.Max(r.Max.Y, s.Max.Y), }} } // Overlaps returns whether r1 and r2 have a non-empty intersection. -func (r1 Rectangle) Overlaps(r2 Rectangle) bool { - return r1.Min.X <= r2.Max.X && r1.Max.X >= r2.Min.X && - r1.Min.Y <= r2.Max.Y && r1.Max.Y >= r2.Min.Y +func (r Rectangle) Overlaps(s Rectangle) bool { + return r.Min.X <= s.Max.X && r.Max.X >= s.Min.X && + r.Min.Y <= s.Max.Y && r.Max.Y >= s.Min.Y } // String returns a string representation of r like "(3,4)-(6,5)". @@ -467,7 +468,7 @@ func (c Contour) segment(index int) segment { // if out-of-bounds, we expect panic detected by runtime } -// Checks if a point is inside a contour using the "point in polygon" raycast method. +// Contains checks if a point is inside a contour using the "point in polygon" raycast method. // This works for all polygons, whether they are clockwise or counter clockwise, // convex or concave. // See: http://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm @@ -559,9 +560,13 @@ func (p Polygon) Clone() Polygon { type Op int const ( + // UNION operation UNION Op = iota + // INTERSECTION operation INTERSECTION + // DIFFERENCE operation DIFFERENCE + // XOR operation XOR ) From 3bdac32acd7428f2f2bc7d22ae2d8137664f4c0b Mon Sep 17 00:00:00 2001 From: Stani Date: Thu, 2 Jul 2015 20:18:00 +0200 Subject: [PATCH 4/4] fixed missing output comment for example --- geom_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geom_test.go b/geom_test.go index fea78fb..d7ee761 100644 --- a/geom_test.go +++ b/geom_test.go @@ -501,5 +501,5 @@ func ExamplePolygon_Construct() { } sort.Strings(out) fmt.Println(out) - // [(1.00,1.00) (1.00,2.00) (2.00,1.00)] + // Output: [(1.00,1.00) (1.00,2.00) (2.00,1.00)] }