2017-11-01 05:51:41 +00:00
|
|
|
package geom
|
|
|
|
|
|
|
|
import (
|
|
|
|
"math"
|
|
|
|
)
|
|
|
|
|
|
|
|
// PointF is an X, Y coordinate pair (floating point).
|
|
|
|
type PointF struct {
|
|
|
|
X, Y float64
|
|
|
|
}
|
|
|
|
|
2017-11-06 06:04:21 +00:00
|
|
|
// ZeroPtF is initialized on (0, 0).
|
|
|
|
var ZeroPtF = PointF{X: 0, Y: 0}
|
|
|
|
|
2017-11-01 05:51:41 +00:00
|
|
|
// PtF is a shorthand function to create a point.
|
|
|
|
func PtF(x, y float64) PointF {
|
|
|
|
return PointF{X: x, Y: y}
|
|
|
|
}
|
|
|
|
|
2017-11-06 21:14:48 +00:00
|
|
|
// Add adds q as a vector to p.
|
2017-11-01 05:51:41 +00:00
|
|
|
func (p PointF) Add(q PointF) PointF {
|
|
|
|
return PointF{p.X + q.X, p.Y + q.Y}
|
|
|
|
}
|
|
|
|
|
2019-12-24 09:48:49 +00:00
|
|
|
// Add2D adds x and y to X and Y of point p and returns the sum.
|
|
|
|
func (p PointF) Add2D(x, y float64) PointF {
|
|
|
|
return PtF(p.X+x, p.Y+y)
|
|
|
|
}
|
|
|
|
|
2017-11-01 05:51:41 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2021-08-08 23:35:30 +00:00
|
|
|
// Atan2 returns the arc tangent of y/x.
|
|
|
|
func (p PointF) Atan2() float64 {
|
|
|
|
return Atan2(p.Y, p.X)
|
|
|
|
}
|
|
|
|
|
2017-11-01 05:51:41 +00:00
|
|
|
// Distance calculates the distance between points p and q.
|
|
|
|
func (p PointF) Distance(q PointF) float64 {
|
2021-08-08 23:35:30 +00:00
|
|
|
return Sqrt(p.Distance2(q))
|
2017-11-01 05:51:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2019-04-11 17:12:45 +00:00
|
|
|
// 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]))
|
|
|
|
}
|
|
|
|
|
2019-12-23 14:09:33 +00:00
|
|
|
// Div divides the X and Y values of point p with t and returns the result.
|
|
|
|
func (p PointF) Div(t float64) PointF {
|
|
|
|
return PtF(p.X/t, p.Y/t)
|
|
|
|
}
|
|
|
|
|
2021-08-08 23:35:30 +00:00
|
|
|
// Dot returns the dot product of p and q.
|
|
|
|
func (p PointF) Dot(q PointF) float64 {
|
|
|
|
return p.X*q.X + p.Y*q.Y
|
|
|
|
}
|
|
|
|
|
2017-11-01 05:51:41 +00:00
|
|
|
// 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
|
|
|
|
}
|
2019-03-06 18:06:08 +00:00
|
|
|
|
2019-03-06 18:24:03 +00:00
|
|
|
// Invert changes the sign of the components.
|
|
|
|
func (p PointF) Invert() PointF {
|
|
|
|
return PointF{-p.X, -p.Y}
|
|
|
|
}
|
|
|
|
|
2021-08-08 23:35:30 +00:00
|
|
|
// Len returns the length of the vector.
|
|
|
|
func (p PointF) Len() float64 {
|
|
|
|
return Sqrt(p.X*p.X + p.Y*p.Y)
|
|
|
|
}
|
|
|
|
|
2021-08-06 17:11:58 +00:00
|
|
|
// Mul multiplies the X and Y values of point p with t and returns the result.
|
2019-12-23 14:09:33 +00:00
|
|
|
func (p PointF) Mul(t float64) PointF {
|
|
|
|
return PtF(p.X*t, p.Y*t)
|
|
|
|
}
|
|
|
|
|
2021-08-06 17:11:58 +00:00
|
|
|
// Mul2D multiplies the X and Y values of point p with x and y and returns the result.
|
|
|
|
func (p PointF) Mul2D(x, y float64) PointF {
|
|
|
|
return PtF(p.X*x, p.Y*y)
|
|
|
|
}
|
|
|
|
|
2021-08-08 23:35:30 +00:00
|
|
|
// Norm returns the normalized vector of x and y.
|
|
|
|
func (p PointF) Norm() PointF {
|
|
|
|
return p.Mul(1 / p.Len())
|
|
|
|
}
|
|
|
|
|
2020-05-17 09:21:41 +00:00
|
|
|
// Rect returns a rectangle starting from point p to given point q
|
|
|
|
func (p PointF) Rect(q PointF) RectangleF {
|
|
|
|
return RectangleF{Min: p, Max: q}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rect2D returns a rectangle starting from point p to given coordinate (x, y)
|
|
|
|
func (p PointF) Rect2D(x, y float64) RectangleF {
|
|
|
|
return RectangleF{Min: p, Max: PtF(x, y)}
|
|
|
|
}
|
|
|
|
|
|
|
|
// RectRel returns a rectangle starting from point p with the given size.
|
|
|
|
func (p PointF) RectRel(size PointF) RectangleF {
|
|
|
|
return RectRelF(p.X, p.Y, size.X, size.Y)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RectRel2D returns a rectangle starting from point p with the given width and height
|
|
|
|
func (p PointF) RectRel2D(width, height float64) RectangleF {
|
|
|
|
return RectRelF(p.X, p.Y, width, height)
|
|
|
|
}
|
|
|
|
|
2019-03-06 18:06:08 +00:00
|
|
|
// Sub subtracts q as a vector from p.
|
|
|
|
func (p PointF) Sub(q PointF) PointF {
|
|
|
|
return PointF{p.X - q.X, p.Y - q.Y}
|
|
|
|
}
|
|
|
|
|
2020-05-17 08:38:01 +00:00
|
|
|
// ToF32 transforms the point p into a PointF32.
|
|
|
|
func (p PointF) ToF32() PointF32 {
|
2019-03-06 18:06:08 +00:00
|
|
|
return PointF32{float32(p.X), float32(p.Y)}
|
|
|
|
}
|
2019-12-28 17:26:08 +00:00
|
|
|
|
2020-05-16 17:52:05 +00:00
|
|
|
// ToInt transforms the point p into a Point.
|
|
|
|
func (p PointF) ToInt() Point {
|
|
|
|
return Point{int(p.X), int(p.Y)}
|
|
|
|
}
|
|
|
|
|
2021-08-06 14:08:30 +00:00
|
|
|
// XY returns the X and Y component of the coordinate.
|
|
|
|
func (p PointF) XY() (float64, float64) { return p.X, p.Y }
|
|
|
|
|
2019-12-28 17:26:08 +00:00
|
|
|
// MaxPtF returns the point that is at the largest X & Y position of a and b.
|
|
|
|
func MaxPtF(a, b PointF) PointF {
|
|
|
|
return PtF(Max(a.X, b.X), Max(a.Y, b.Y))
|
|
|
|
}
|
|
|
|
|
|
|
|
// MinPtF returns the point that is at the smallest X & Y position of a and b.
|
|
|
|
func MinPtF(a, b PointF) PointF {
|
|
|
|
return PtF(Min(a.X, b.X), Min(a.Y, b.Y))
|
|
|
|
}
|