vRp.CD2g_test/utils/geometry/geometry.go

167 lines
5.2 KiB
Go

package geometry
import (
"github.com/kercylan98/minotaur/utils/generic"
"math"
)
// Direction 方向
type Direction uint8
const (
DirectionUnknown = Direction(iota) // 未知
DirectionUp // 上方
DirectionDown // 下方
DirectionLeft // 左方
DirectionRight // 右方
)
var (
DirectionUDLR = []Direction{DirectionUp, DirectionDown, DirectionLeft, DirectionRight} // 上下左右四个方向的数组
DirectionLRUD = []Direction{DirectionLeft, DirectionRight, DirectionUp, DirectionDown} // 左右上下四个方向的数组
)
// GetOppositionDirection 获取特定方向的对立方向
func GetOppositionDirection(direction Direction) Direction {
switch direction {
case DirectionUp:
return DirectionDown
case DirectionDown:
return DirectionUp
case DirectionLeft:
return DirectionRight
case DirectionRight:
return DirectionLeft
case DirectionUnknown:
return DirectionUnknown
}
return DirectionUnknown
}
// GetDirectionNextWithCoordinate 获取特定方向上的下一个坐标
func GetDirectionNextWithCoordinate[V generic.SignedNumber](direction Direction, x, y V) (nx, ny V) {
switch direction {
case DirectionUp:
nx, ny = x, y-1
case DirectionDown:
nx, ny = x, y+1
case DirectionLeft:
nx, ny = x-1, y
case DirectionRight:
nx, ny = x+1, y
default:
panic(ErrUnexplainedDirection)
}
return
}
// GetDirectionNextWithPoint 获取特定方向上的下一个坐标
func GetDirectionNextWithPoint[V generic.SignedNumber](direction Direction, point Point[V]) Point[V] {
x, y := point.GetXY()
switch direction {
case DirectionUp:
return NewPoint(x, y-1)
case DirectionDown:
return NewPoint(x, y+1)
case DirectionLeft:
return NewPoint(x-1, y)
case DirectionRight:
return NewPoint(x+1, y)
default:
panic(ErrUnexplainedDirection)
}
}
// GetDirectionNextWithPos 获取位置在特定宽度和特定方向上的下一个位置
// - 需要注意的是,在左右方向时,当下一个位置不在矩形区域内时,将会返回上一行的末位置或下一行的首位置
func GetDirectionNextWithPos[V generic.SignedNumber](direction Direction, width, pos V) V {
switch direction {
case DirectionUp:
return pos - width
case DirectionDown:
return pos + width
case DirectionLeft:
return pos - 1
case DirectionRight:
return pos + 1
default:
panic(ErrUnexplainedDirection)
}
}
// CalcDirection 计算点2位于点1的方向
func CalcDirection[V generic.SignedNumber](x1, y1, x2, y2 V) Direction {
angle := CalcAngle(float64(x1), float64(y1), float64(x2), float64(y2)) + 180
if angle > 45 && angle <= 225 {
return DirectionRight
} else if (angle > 315 && angle <= 360) || (angle >= 0 && angle <= 45) {
return DirectionLeft
} else if angle > 225 && angle <= 315 {
return DirectionUp
} else if angle > 45 && angle <= 134 {
return DirectionDown
}
return DirectionUnknown
}
// CalcDistanceWithCoordinate 计算两点之间的距离
func CalcDistanceWithCoordinate[V generic.SignedNumber](x1, y1, x2, y2 V) V {
return V(math.Sqrt(math.Pow(float64(x2-x1), 2) + math.Pow(float64(y2-y1), 2)))
}
// CalcDistanceWithPoint 计算两点之间的距离
func CalcDistanceWithPoint[V generic.SignedNumber](point1, point2 Point[V]) V {
return CalcDistanceWithCoordinate(DoublePointToCoordinate(point1, point2))
}
// CalcDistanceSquared 计算两点之间的平方距离
// - 这个函数的主要用途是在需要计算两点之间距离的情况下,但不需要得到实际的距离值,而只需要比较距离大小。因为平方根运算相对较为耗时,所以在只需要比较大小的情况下,通常会使用平方距离。
func CalcDistanceSquared[V generic.SignedNumber](x1, y1, x2, y2 V) V {
dx, dy := x2-x1, y2-y1
return dx*dx + dy*dy
}
// CalcAngle 计算点2位于点1之间的角度
func CalcAngle[V generic.SignedNumber](x1, y1, x2, y2 V) V {
return V(math.Atan2(float64(y2-y1), float64(x2-x1)) * 180 / math.Pi)
}
// CalcNewCoordinate 根据给定的x、y坐标、角度和距离计算新的坐标
func CalcNewCoordinate[V generic.SignedNumber](x, y, angle, distance V) (newX, newY V) {
radian := CalcRadianWithAngle(angle)
newX = x + distance*V(math.Cos(float64(radian)))
newY = y + distance*V(math.Sin(float64(radian)))
return newX, newY
}
// CalcRadianWithAngle 根据角度 angle 计算弧度
func CalcRadianWithAngle[V generic.SignedNumber](angle V) V {
return V(float64(angle) * math.Pi / 180)
}
// CalcAngleDifference 计算两个角度之间的最小角度差
func CalcAngleDifference[V generic.SignedNumber](angleA, angleB V) V {
pi := math.Pi
t := float64(angleA) - float64(angleB)
a := t + math.Pi
b := math.Pi * 2
t = math.Floor(a/b) * b
t -= pi
return V(t)
}
// CalcRayIsIntersect 根据给定的位置和角度生成射线,检测射线是否与多边形发生碰撞
func CalcRayIsIntersect[V generic.SignedNumber](x, y, angle V, shape Shape[V]) bool {
fx, fy := float64(x), float64(y)
radian := CalcRadianWithAngle(float64(angle))
end := NewPoint(fx+math.Cos(radian), fy+math.Sin(radian))
edges := shape.Edges()
for i := 0; i < len(edges); i++ {
if CalcLineSegmentIsIntersect(NewLineSegment(NewPoint(fx, fy), end), ConvertLineSegmentGeneric[V, float64](edges[i])) {
return true
}
}
return false
}