vRp.CD2g_test/utils/geometry/navmesh/navmesh.go

114 lines
4.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package navmesh
import (
"github.com/kercylan98/minotaur/utils/generic"
"github.com/kercylan98/minotaur/utils/geometry"
"github.com/kercylan98/minotaur/utils/maths"
)
func NewNavMesh[V generic.SignedNumber](shapes []geometry.Shape[V], meshShrinkAmount V) *NavMesh[V] {
nm := &NavMesh[V]{
meshShapes: make([]*shape[V], len(shapes)),
meshShrinkAmount: meshShrinkAmount,
}
for i, shape := range shapes {
nm.meshShapes[i] = newShape(shape)
}
nm.generateLink()
return nm
}
type NavMesh[V generic.SignedNumber] struct {
meshShapes []*shape[V]
meshShrinkAmount V
}
// Find 在网格中找到与给定点最近的点。如果该点已经在网格中,这将为您提供该点。如果点在网格之外,这将尝试将此点投影到网格中(直到给定的 maxDistance
func (slf *NavMesh[V]) Find(point geometry.Point[V], maxDistance V) (distance V, findPoint geometry.Point[V], findShape geometry.Shape[V]) {
var minDistance = maxDistance
var closest *shape[V]
var pointOnClosest geometry.Point[V]
for _, meshShape := range slf.meshShapes {
if meshShape.Contains(point) || geometry.IsPointOnEdge(meshShape.Edges(), point) {
minDistance = 0
closest = meshShape
pointOnClosest = point
break
}
br := geometry.CalcBoundingRadius(meshShape.Shape)
distance := geometry.CalcDistance(geometry.DoublePointToCoordinate(
geometry.CalcRectangleCentroid(meshShape.Shape),
point,
))
if distance-br < minDistance {
point, distance := geometry.ProjectionPointToShape(point, meshShape.Shape)
if distance < minDistance {
minDistance = distance
closest = meshShape
pointOnClosest = point
}
}
}
return minDistance, pointOnClosest, closest.Shape
}
func (slf *NavMesh[V]) generateLink() {
refer := len(slf.meshShapes)
for i := 0; i < refer; i++ {
shapePkg := slf.meshShapes[i]
shape := shapePkg.Shape
shapeCentroid := geometry.CalcRectangleCentroid(shape)
shapeBoundingRadius := geometry.CalcBoundingRadiusWithCentroid(shape, shapeCentroid)
shapeEdges := shape.Edges()
for t := i + 1; t < len(slf.meshShapes); t++ {
targetShapePkg := slf.meshShapes[t]
targetShape := targetShapePkg.Shape
targetShapeCentroid := geometry.CalcRectangleCentroid(targetShape)
targetShapeBoundingRadius := geometry.CalcBoundingRadiusWithCentroid(shape, targetShapeCentroid)
centroidDistance := geometry.CalcDistance(geometry.DoublePointToCoordinate(shapeCentroid, targetShapeCentroid))
if centroidDistance > shapeBoundingRadius+targetShapeBoundingRadius {
continue
}
for _, shapeEdge := range shapeEdges {
for _, targetEdge := range targetShape.Edges() {
if !geometry.CalcLineIsCollinear(shapeEdge, targetEdge, V(maths.GetDefaultTolerance())) {
continue
}
var overlapLine, overlap = geometry.CalcLineIsOverlap(shapeEdge, targetEdge)
if !overlap {
continue
}
shapePkg.links = append(shapePkg.links, targetShapePkg)
targetShapePkg.links = append(targetShapePkg.links, shapePkg)
edgeAngle := geometry.CalcAngle(geometry.DoublePointToCoordinate(shapeCentroid, shapeEdge.GetStart()))
a1 := geometry.CalcAngle(geometry.DoublePointToCoordinate(shapeCentroid, overlapLine.GetStart()))
a2 := geometry.CalcAngle(geometry.DoublePointToCoordinate(shapeCentroid, overlapLine.GetEnd()))
a3 := geometry.CalcAngleDifference(edgeAngle, a1)
a4 := geometry.CalcAngleDifference(edgeAngle, a2)
if a3 < a4 {
shapePkg.portals = append(shapePkg.portals, geometry.NewLine(overlapLine.GetStart(), overlapLine.GetEnd()))
} else {
shapePkg.portals = append(shapePkg.portals, geometry.NewLine(overlapLine.GetEnd(), overlapLine.GetStart()))
}
edgeAngle = geometry.CalcAngle(geometry.DoublePointToCoordinate(targetShapeCentroid, targetEdge.GetStart()))
a1 = geometry.CalcAngle(geometry.DoublePointToCoordinate(targetShapeCentroid, overlapLine.GetStart()))
a2 = geometry.CalcAngle(geometry.DoublePointToCoordinate(targetShapeCentroid, overlapLine.GetEnd()))
a3 = geometry.CalcAngleDifference(edgeAngle, a1)
a4 = geometry.CalcAngleDifference(edgeAngle, a2)
if a3 < a4 {
targetShapePkg.portals = append(targetShapePkg.portals, geometry.NewLine(overlapLine.GetStart(), overlapLine.GetEnd()))
} else {
targetShapePkg.portals = append(targetShapePkg.portals, geometry.NewLine(overlapLine.GetEnd(), overlapLine.GetStart()))
}
}
}
}
}
}