Files
sjy01-image-proc/vendor/github.com/paulmach/orb/planar/distance_from.go
2024-10-24 15:46:01 +08:00

174 lines
3.4 KiB
Go

package planar
import (
"fmt"
"math"
"github.com/paulmach/orb"
)
// DistanceFromSegment returns the point's distance from the segment [a, b].
func DistanceFromSegment(a, b, point orb.Point) float64 {
return math.Sqrt(DistanceFromSegmentSquared(a, b, point))
}
// DistanceFromSegmentSquared returns point's squared distance from the segement [a, b].
func DistanceFromSegmentSquared(a, b, point orb.Point) float64 {
x := a[0]
y := a[1]
dx := b[0] - x
dy := b[1] - y
if dx != 0 || dy != 0 {
t := ((point[0]-x)*dx + (point[1]-y)*dy) / (dx*dx + dy*dy)
if t > 1 {
x = b[0]
y = b[1]
} else if t > 0 {
x += dx * t
y += dy * t
}
}
dx = point[0] - x
dy = point[1] - y
return dx*dx + dy*dy
}
// DistanceFrom returns the distance from the boundary of the geometry in
// the units of the geometry.
func DistanceFrom(g orb.Geometry, p orb.Point) float64 {
d, _ := DistanceFromWithIndex(g, p)
return d
}
// DistanceFromWithIndex returns the minimum euclidean distance
// from the boundary of the geometry plus the index of the sub-geometry
// that was the match.
func DistanceFromWithIndex(g orb.Geometry, p orb.Point) (float64, int) {
if g == nil {
return math.Inf(1), -1
}
switch g := g.(type) {
case orb.Point:
return Distance(g, p), 0
case orb.MultiPoint:
return multiPointDistanceFrom(g, p)
case orb.LineString:
return lineStringDistanceFrom(g, p)
case orb.MultiLineString:
dist := math.Inf(1)
index := -1
for i, ls := range g {
if d, _ := lineStringDistanceFrom(ls, p); d < dist {
dist = d
index = i
}
}
return dist, index
case orb.Ring:
return lineStringDistanceFrom(orb.LineString(g), p)
case orb.Polygon:
return polygonDistanceFrom(g, p)
case orb.MultiPolygon:
dist := math.Inf(1)
index := -1
for i, poly := range g {
if d, _ := polygonDistanceFrom(poly, p); d < dist {
dist = d
index = i
}
}
return dist, index
case orb.Collection:
dist := math.Inf(1)
index := -1
for i, ge := range g {
if d, _ := DistanceFromWithIndex(ge, p); d < dist {
dist = d
index = i
}
}
return dist, index
case orb.Bound:
return DistanceFromWithIndex(g.ToRing(), p)
}
panic(fmt.Sprintf("geometry type not supported: %T", g))
}
func multiPointDistanceFrom(mp orb.MultiPoint, p orb.Point) (float64, int) {
dist := math.Inf(1)
index := -1
for i := range mp {
if d := DistanceSquared(mp[i], p); d < dist {
dist = d
index = i
}
}
return math.Sqrt(dist), index
}
func lineStringDistanceFrom(ls orb.LineString, p orb.Point) (float64, int) {
dist := math.Inf(1)
index := -1
for i := 0; i < len(ls)-1; i++ {
if d := segmentDistanceFromSquared(ls[i], ls[i+1], p); d < dist {
dist = d
index = i
}
}
return math.Sqrt(dist), index
}
func polygonDistanceFrom(p orb.Polygon, point orb.Point) (float64, int) {
if len(p) == 0 {
return math.Inf(1), -1
}
dist, index := lineStringDistanceFrom(orb.LineString(p[0]), point)
for i := 1; i < len(p); i++ {
d, i := lineStringDistanceFrom(orb.LineString(p[i]), point)
if d < dist {
dist = d
index = i
}
}
return dist, index
}
func segmentDistanceFromSquared(p1, p2, point orb.Point) float64 {
x := p1[0]
y := p1[1]
dx := p2[0] - x
dy := p2[1] - y
if dx != 0 || dy != 0 {
t := ((point[0]-x)*dx + (point[1]-y)*dy) / (dx*dx + dy*dy)
if t > 1 {
x = p2[0]
y = p2[1]
} else if t > 0 {
x += dx * t
y += dy * t
}
}
dx = point[0] - x
dy = point[1] - y
return dx*dx + dy*dy
}