From 26bac636bc968fcb3ccdec940fe652b798312941 Mon Sep 17 00:00:00 2001 From: Sander Schobers Date: Wed, 1 Nov 2017 06:51:41 +0100 Subject: [PATCH] Initial version of the geom package - Includes PointF, RectangleF and PolygonF. Additional it includes the 32 bit floating point PointF32 and wrappers to image.Point and image.Rectangle. --- LICENSE | 21 +++++++++++++ README.md | 3 ++ point.go | 13 ++++++++ pointf.go | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ pointf32.go | 6 ++++ pointf_test.go | 31 +++++++++++++++++++ polygonf.go | 14 +++++++++ rectangle.go | 13 ++++++++ rectanglef.go | 22 ++++++++++++++ 9 files changed, 204 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 point.go create mode 100644 pointf.go create mode 100644 pointf32.go create mode 100644 pointf_test.go create mode 100644 polygonf.go create mode 100644 rectangle.go create mode 100644 rectanglef.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1307f2d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Sander Schobers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ae7bdf7 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# geom package + +The geom packages exposes some basic geometries using floating point numbers. The package is licensed under [MIT](LICENSE). Currently this package is still under development and thus the API may break. diff --git a/point.go b/point.go new file mode 100644 index 0000000..7b25eb1 --- /dev/null +++ b/point.go @@ -0,0 +1,13 @@ +package geom + +import ( + _image "image" +) + +// Point is exposing the standard library image.Point in the geom package. +type Point _image.Point + +// Pt is a shorthand function to create a point. +func Pt(x, y int) Point { + return Point{x, y} +} diff --git a/pointf.go b/pointf.go new file mode 100644 index 0000000..c18a04d --- /dev/null +++ b/pointf.go @@ -0,0 +1,81 @@ +package geom + +import ( + "math" + "strconv" +) + +// PointF is an X, Y coordinate pair (floating point). +type PointF struct { + X, Y float64 +} + +// PtF is a shorthand function to create a point. +func PtF(x, y float64) PointF { + return PointF{X: x, Y: y} +} + +// String formats the point p as a string. +func (p Point) String() string { + return "(" + strconv.Itoa(p.X) + "," + strconv.Itoa(p.Y) + ")" +} + +// To32 transforms the point p into a PointF32. +func (p PointF) To32() PointF32 { + return PointF32{float32(p.X), float32(p.Y)} +} + +// Add adds q as a vector to p +func (p PointF) Add(q PointF) PointF { + return PointF{p.X + q.X, p.Y + q.Y} +} + +// AngleTo calculates the angle [0..2*Pi) from point p to point q. +func (p PointF) AngleTo(q PointF) float64 { + a := math.Atan((p.Y - q.Y) / (p.X - q.X)) + if q.X < p.X { + return a + math.Pi + } + if a < 0 { + a += 2 * math.Pi + } + return a +} + +// Distance calculates the distance between points p and q. +func (p PointF) Distance(q PointF) float64 { + return math.Sqrt(p.Distance2(q)) +} + +// Distance2 calculates the squared distance between points p and q. +func (p PointF) Distance2(q PointF) float64 { + dx := q.X - p.X + dy := q.Y - p.Y + return dx*dx + dy*dy +} + +// In tests if the point p is inside the rectangle r. +func (p PointF) In(r RectangleF) bool { + if p.X < r.Min.X || p.X >= r.Max.X || p.Y < r.Min.Y || p.Y >= r.Max.Y { + return false + } + return true +} + +// InPolygon tests is the point p is inside the polygon q. +func (p PointF) InPolygon(q PolygonF) bool { + var n = len(q.Points) + var c = false + + var i = 0 + var j = n - 1 + for i < n { + if ((q.Points[i].Y >= p.Y) != (q.Points[j].Y >= p.Y)) && + (p.X <= (q.Points[j].X-q.Points[i].X)*(p.Y-q.Points[i].Y)/(q.Points[j].Y-q.Points[i].Y)+q.Points[i].X) { + c = !c + } + j = i + i++ + } + return c +} diff --git a/pointf32.go b/pointf32.go new file mode 100644 index 0000000..5bb2096 --- /dev/null +++ b/pointf32.go @@ -0,0 +1,6 @@ +package geom + +// PointF32 is an X, Y coordinate pair (floating point, 32 bits). +type PointF32 struct { + X, Y float32 +} diff --git a/pointf_test.go b/pointf_test.go new file mode 100644 index 0000000..8041e9e --- /dev/null +++ b/pointf_test.go @@ -0,0 +1,31 @@ +package geom + +import ( + "math" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestPointFAngleTo(t *testing.T) { + for a := .0; a <= 2*math.Pi; a += math.Pi * 0.25 { + assert.InDelta(t, a, PtF(0, 0).AngleTo(PtF(100*math.Cos(a), 100*math.Sin(a))), 0.000001) + } +} + +func TestPointInPolygon(t *testing.T) { + pol := PolygonF{[]PointF{PtF(0, 0), PtF(0, 3), PtF(1, 3), PtF(2, 1), PtF(3, 3), PtF(5, 3), PtF(5, 2), PtF(3, 1), PtF(5, 0)}} + assert.True(t, PtF(1, 2).InPolygon(pol)) + assert.True(t, PtF(3, 2).InPolygon(pol)) + assert.True(t, PtF(4, 2).InPolygon(pol)) + // assert.True(t, PtF(0, 0).InPolygon(pol)) // Corner = out + // assert.True(t, PtF(0, 3).InPolygon(pol)) // Corner = out + // assert.True(t, PtF(5, 3).InPolygon(pol)) // Corner = in + // assert.True(t, PtF(5, 0).InPolygon(pol)) // Corner = out + assert.False(t, PtF(0, -1).InPolygon(pol)) + assert.False(t, PtF(-1, 0).InPolygon(pol)) + assert.False(t, PtF(2, 2).InPolygon(pol)) + assert.False(t, PtF(2, 3).InPolygon(pol)) + assert.False(t, PtF(4, 1).InPolygon(pol)) + assert.False(t, PtF(5, 1).InPolygon(pol)) +} diff --git a/polygonf.go b/polygonf.go new file mode 100644 index 0000000..72cd871 --- /dev/null +++ b/polygonf.go @@ -0,0 +1,14 @@ +package geom + +// PolygonF is defined by a set of points (floating point). +type PolygonF struct { + Points []PointF +} + +// Add creates a new polyqon based on p with one or more extra points q. +func (p PolygonF) Add(q ...PointF) PolygonF { + var t = PolygonF{make([]PointF, len(p.Points)+len(q))} + copy(t.Points, p.Points) + copy(t.Points[len(p.Points):], q) + return t +} diff --git a/rectangle.go b/rectangle.go new file mode 100644 index 0000000..0a9c3d4 --- /dev/null +++ b/rectangle.go @@ -0,0 +1,13 @@ +package geom + +import ( + _image "image" +) + +// Rectangle is exposing the standard library image.Rectangle in the geom package. +type Rectangle _image.Rectangle + +// Rect is a shorthand function to create a rectangle. +func Rect(x0, y0, x1, y1 int) Rectangle { + return Rectangle(_image.Rect(x0, y0, x1, y1)) +} diff --git a/rectanglef.go b/rectanglef.go new file mode 100644 index 0000000..c5c24ba --- /dev/null +++ b/rectanglef.go @@ -0,0 +1,22 @@ +package geom + +// RectangleF is defined by two points, the minimum and maximum (floating point). +type RectangleF struct { + Min, Max PointF +} + +// RectF is a shorthand function to create a rectangle. +func RectF(x0, y0, x1, y1 float64) RectangleF { + if x0 > x1 { + x0, x1 = x1, x0 + } + if y0 > y1 { + y0, y1 = y1, y0 + } + return RectangleF{PtF(x0, y0), PtF(x1, y1)} +} + +// Size returns the size of the rectangle. +func (r RectangleF) Size() PointF { + return PtF(r.Max.X-r.Min.X, r.Max.Y-r.Min.Y) +}