diff --git a/math.go b/math.go index 43bc996..41eb240 100644 --- a/math.go +++ b/math.go @@ -22,16 +22,6 @@ func IsNaN32(f float32) bool { return f != f } -// NaN returns not a floating point number. -func NaN() float64 { - return math.NaN() -} - -// NaN32 returns not a floating point number. -func NaN32() float32 { - return float32(NaN()) -} - // Max the maximum of the two values. func Max(a, b float64) float64 { return math.Max(a, b) @@ -51,3 +41,33 @@ func Min(a, b float64) float64 { func Min32(a, b float32) float32 { return float32(math.Min(float64(a), float64(b))) } + +// NaN returns not a floating point number. +func NaN() float64 { + return math.NaN() +} + +// NaN32 returns not a floating point number. +func NaN32() float32 { + return float32(NaN()) +} + +// Sq the square root of the value +func Sq(f float64) float64 { + return f * f +} + +// Sq32 the square root of the value +func Sq32(f float32) float32 { + return f * f +} + +// Sqrt the square root of the value +func Sqrt(f float64) float64 { + return math.Sqrt(f) +} + +// Sqrt32 the square root of the value +func Sqrt32(f float32) float32 { + return float32(Sqrt(float64(f))) +} diff --git a/pointf.go b/pointf.go index c7b91b1..d81b147 100644 --- a/pointf.go +++ b/pointf.go @@ -46,6 +46,51 @@ func (p PointF) Distance2(q PointF) float64 { return dx*dx + dy*dy } +// DistanceToLine calculates the distance to the line segment a, b. +func (p PointF) DistanceToLine(a, b PointF) float64 { + dx1, dy1 := (a.X - p.X), (a.Y - p.Y) + dx2, dy2 := (b.X - a.X), (b.Y - a.Y) + t := -((dx1*dx2 + dy1*dy2) / (dx2*dx2 + dy2*dy2)) + if 0 <= t && t <= 1 { + return Abs(dx2*dy1-dy2*dx1) / Sqrt(dx2*dx2+dy2*dy2) + } + d1, d2 := Sqrt(Sq(b.X-p.X)+Sq(b.Y-p.Y)), Sqrt(dx1*dx1+dy1*dy1) + if d1 < d2 { + return d1 + } + return d2 +} + +// DistanceToLines calculates the smallest distance to the line segments of q. +func (p PointF) DistanceToLines(q PointsF) float64 { + n := len(q) + if n == 0 { + return NaN() + } + if n == 1 { + return p.Distance(q[0]) + } + min := p.DistanceToLine(q[0], q[1]) + for i := range q { + if i < 2 { + continue + } + d := p.DistanceToLine(q[i-1], q[i]) + if d < min { + min = d + } + } + return min +} + +// DistanceToPolygon calculates the smallest distance to the polygon q. +func (p PointF) DistanceToPolygon(q PolygonF) float64 { + if len(q.Points) == 0 { + return NaN() + } + return p.DistanceToLines(append(q.Points, q.Points[0])) +} + // 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 { diff --git a/pointf32.go b/pointf32.go index d66e3a2..00c76a0 100644 --- a/pointf32.go +++ b/pointf32.go @@ -46,6 +46,51 @@ func (p PointF32) Distance2(q PointF32) float32 { return dx*dx + dy*dy } +// DistanceToLine calculates the distance to the line segment a, b. +func (p PointF32) DistanceToLine(a, b PointF32) float32 { + dx1, dy1 := (a.X - p.X), (a.Y - p.Y) + dx2, dy2 := (b.X - a.X), (b.Y - a.Y) + t := -((dx1*dx2 + dy1*dy2) / (dx2*dx2 + dy2*dy2)) + if 0 <= t && t <= 1 { + return Abs32(dx2*dy1-dy2*dx1) / Sqrt32(dx2*dx2+dy2*dy2) + } + d1, d2 := Sqrt32(Sq32(b.X-p.X)+Sq32(b.Y-p.Y)), Sqrt32(dx1*dx1+dy1*dy1) + if d1 < d2 { + return d1 + } + return d2 +} + +// DistanceToLines calculates the smallest distance to the line segments of q. +func (p PointF32) DistanceToLines(q PointsF32) float32 { + n := len(q) + if n == 0 { + return NaN32() + } + if n == 1 { + return p.Distance(q[0]) + } + min := p.DistanceToLine(q[0], q[1]) + for i := range q { + if i < 2 { + continue + } + d := p.DistanceToLine(q[i-1], q[i]) + if d < min { + min = d + } + } + return min +} + +// DistanceToPolygon calculates the smallest distance to the polygon q. +func (p PointF32) DistanceToPolygon(q PolygonF32) float32 { + if len(q.Points) == 0 { + return NaN32() + } + return p.DistanceToLines(append(q.Points, q.Points[0])) +} + // In tests if the point p is inside the rectangle r. func (p PointF32) In(r RectangleF32) bool { if p.X < r.Min.X || p.X >= r.Max.X || p.Y < r.Min.Y || p.Y >= r.Max.Y { @@ -54,6 +99,24 @@ func (p PointF32) In(r RectangleF32) bool { return true } +// InPolygon tests is the point p is inside the polygon q. +func (p PointF32) InPolygon(q PolygonF32) 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 +} + // Invert changes the sign of the components. func (p PointF32) Invert() PointF32 { return PointF32{-p.X, -p.Y} diff --git a/polygonf.go b/polygonf.go index 72cd871..ec01adf 100644 --- a/polygonf.go +++ b/polygonf.go @@ -1,13 +1,19 @@ package geom +// PointsF is a set of points. +type PointsF []PointF + +// PolF creates a polygon of points q. +func PolF(q ...PointF) PolygonF { return PolygonF{Points: q} } + // PolygonF is defined by a set of points (floating point). type PolygonF struct { - Points []PointF + Points PointsF } // 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))} + var t = PolygonF{make(PointsF, len(p.Points)+len(q))} copy(t.Points, p.Points) copy(t.Points[len(p.Points):], q) return t diff --git a/polygonf32.go b/polygonf32.go new file mode 100644 index 0000000..1313eb3 --- /dev/null +++ b/polygonf32.go @@ -0,0 +1,20 @@ +package geom + +// PointsF32 is a set of points. +type PointsF32 []PointF32 + +// PolF32 creates a polygon of points q. +func PolF32(q ...PointF32) PolygonF32 { return PolygonF32{Points: q} } + +// PolygonF32 is defined by a set of points (floating point). +type PolygonF32 struct { + Points PointsF32 +} + +// Add creates a new polyqon based on p with one or more extra points q. +func (p PolygonF32) Add(q ...PointF32) PolygonF32 { + var t = PolygonF32{make(PointsF32, len(p.Points)+len(q))} + copy(t.Points, p.Points) + copy(t.Points[len(p.Points):], q) + return t +}