c7a6e09926
CalcRayIsIntersect 根据给定的位置和角度生成射线,检测射线是否与多边形发生碰撞 CalcRadianWithAngle 根据角度 angle 计算弧度 CalcLineSegmentIsIntersect 计算两条线段是否相交 CalcLineSegmentSlope 计算线段的斜率 CalcLineSegmentIntercept 计算线段的截距 ConvertLineSegmentGeneric 转换线段的泛型类型为特定类型 Point.Min 注释为 Max 的问题修复
234 lines
7.3 KiB
Go
234 lines
7.3 KiB
Go
package geometry
|
|
|
|
import (
|
|
"github.com/kercylan98/minotaur/utils/generic"
|
|
"github.com/kercylan98/minotaur/utils/maths"
|
|
"math"
|
|
)
|
|
|
|
// Point 表示了一个由 x、y 坐标组成的点
|
|
type Point[V generic.SignedNumber] [2]V
|
|
|
|
// NewPoint 创建一个由 x、y 坐标组成的点
|
|
func NewPoint[V generic.SignedNumber](x, y V) Point[V] {
|
|
return Point[V]{x, y}
|
|
}
|
|
|
|
// GetX 返回该点的 x 坐标
|
|
func (slf Point[V]) GetX() V {
|
|
return slf[0]
|
|
}
|
|
|
|
// GetY 返回该点的 y 坐标
|
|
func (slf Point[V]) GetY() V {
|
|
return slf[1]
|
|
}
|
|
|
|
// GetXY 返回该点的 x、y 坐标
|
|
func (slf Point[V]) GetXY() (x, y V) {
|
|
return slf[0], slf[1]
|
|
}
|
|
|
|
// GetPos 返回该点位于特定宽度的二维数组的顺序位置
|
|
func (slf Point[V]) GetPos(width V) V {
|
|
return PointToPos(width, slf)
|
|
}
|
|
|
|
// GetOffset 获取偏移后的新坐标
|
|
func (slf Point[V]) GetOffset(x, y V) Point[V] {
|
|
return NewPoint(slf.GetX()+x, slf.GetY()+y)
|
|
}
|
|
|
|
// Negative 返回该点是否是一个负数坐标
|
|
func (slf Point[V]) Negative() bool {
|
|
return slf.GetX() < V(0) || slf.GetY() < V(0)
|
|
}
|
|
|
|
// OutOf 返回该点在特定宽高下是否越界f
|
|
func (slf Point[V]) OutOf(minWidth, minHeight, maxWidth, maxHeight V) bool {
|
|
return slf.GetX() < minWidth || slf.GetY() < minHeight || slf.GetX() >= maxWidth || slf.GetY() >= maxHeight
|
|
}
|
|
|
|
// Equal 返回两个点是否相等
|
|
func (slf Point[V]) Equal(point Point[V]) bool {
|
|
return slf.GetX() == point.GetX() && slf.GetY() == point.GetY()
|
|
}
|
|
|
|
// Copy 复制一个点位置
|
|
func (slf Point[V]) Copy() Point[V] {
|
|
return PointCopy(slf)
|
|
}
|
|
|
|
// Add 得到加上 point 后的点
|
|
func (slf Point[V]) Add(point Point[V]) Point[V] {
|
|
return slf.GetOffset(point.GetXY())
|
|
}
|
|
|
|
// Sub 得到减去 point 后的点
|
|
func (slf Point[V]) Sub(point Point[V]) Point[V] {
|
|
return NewPoint(slf.GetX()-point.GetX(), slf.GetY()-point.GetY())
|
|
}
|
|
|
|
// Mul 得到乘以 point 后的点
|
|
func (slf Point[V]) Mul(point Point[V]) Point[V] {
|
|
return NewPoint(slf.GetX()*point.GetX(), slf.GetY()*point.GetY())
|
|
}
|
|
|
|
// Div 得到除以 point 后的点
|
|
func (slf Point[V]) Div(point Point[V]) Point[V] {
|
|
return NewPoint(slf.GetX()/point.GetX(), slf.GetY()/point.GetY())
|
|
}
|
|
|
|
// Abs 返回位置的绝对值
|
|
func (slf Point[V]) Abs() Point[V] {
|
|
return NewPoint(V(math.Abs(float64(slf.GetX()))), V(math.Abs(float64(slf.GetY()))))
|
|
}
|
|
|
|
// Max 返回两个位置中每个维度的最大值组成的新的位置
|
|
func (slf Point[V]) Max(point Point[V]) Point[V] {
|
|
x, y := slf.GetXY()
|
|
px, py := point.GetXY()
|
|
if px > x {
|
|
x = px
|
|
}
|
|
if py > y {
|
|
y = py
|
|
}
|
|
return NewPoint(x, y)
|
|
}
|
|
|
|
// Min 返回两个位置中每个维度的最小值组成的新的位置
|
|
func (slf Point[V]) Min(point Point[V]) Point[V] {
|
|
x, y := slf.GetXY()
|
|
px, py := point.GetXY()
|
|
if px < x {
|
|
x = px
|
|
}
|
|
if py < y {
|
|
y = py
|
|
}
|
|
return NewPoint(x, y)
|
|
}
|
|
|
|
// NewPointCap 创建一个由 x、y 坐标组成的点,这个点具有一个数据容量
|
|
func NewPointCap[V generic.SignedNumber, D any](x, y V) PointCap[V, D] {
|
|
return PointCap[V, D]{
|
|
Point: NewPoint(x, y),
|
|
}
|
|
}
|
|
|
|
// NewPointCapWithData 通过设置数据的方式创建一个由 x、y 坐标组成的点,这个点具有一个数据容量
|
|
func NewPointCapWithData[V generic.SignedNumber, D any](x, y V, data D) PointCap[V, D] {
|
|
return PointCap[V, D]{
|
|
Point: NewPoint(x, y),
|
|
Data: data,
|
|
}
|
|
}
|
|
|
|
// NewPointCapWithPoint 通过设置数据的方式创建一个由已有坐标组成的点,这个点具有一个数据容量
|
|
func NewPointCapWithPoint[V generic.SignedNumber, D any](point Point[V], data D) PointCap[V, D] {
|
|
return PointCap[V, D]{
|
|
Point: point,
|
|
Data: data,
|
|
}
|
|
}
|
|
|
|
// PointCap 表示了一个由 x、y 坐标组成的点,这个点具有一个数据容量
|
|
type PointCap[V generic.SignedNumber, D any] struct {
|
|
Point[V]
|
|
Data D
|
|
}
|
|
|
|
// GetData 获取数据
|
|
func (slf PointCap[V, D]) GetData() D {
|
|
return slf.Data
|
|
}
|
|
|
|
// CoordinateToPoint 将坐标转换为x、y的坐标数组
|
|
func CoordinateToPoint[V generic.SignedNumber](x, y V) Point[V] {
|
|
return [2]V{x, y}
|
|
}
|
|
|
|
// CoordinateToPos 将坐标转换为二维数组的顺序位置坐标
|
|
// - 需要确保x的取值范围必须小于width,或者将会得到不正确的值
|
|
func CoordinateToPos[V generic.SignedNumber](width, x, y V) V {
|
|
return y*width + x
|
|
}
|
|
|
|
// PointToCoordinate 将坐标数组转换为x和y坐标
|
|
func PointToCoordinate[V generic.SignedNumber](position Point[V]) (x, y V) {
|
|
return position[0], position[1]
|
|
}
|
|
|
|
// PointToPos 将坐标转换为二维数组的顺序位置
|
|
// - 需要确保x的取值范围必须小于width,或者将会得到不正确的值
|
|
func PointToPos[V generic.SignedNumber](width V, xy Point[V]) V {
|
|
return CoordinateToPos(width, xy[0], xy[1])
|
|
}
|
|
|
|
// PosToCoordinate 通过宽度将一个二维数组的顺序位置转换为xy坐标
|
|
func PosToCoordinate[V generic.SignedNumber](width, pos V) (x, y V) {
|
|
x = V(math.Mod(float64(pos), float64(width)))
|
|
y = pos / width
|
|
return x, y
|
|
}
|
|
|
|
// PosToPoint 通过宽度将一个二维数组的顺序位置转换为x、y的坐标数组
|
|
func PosToPoint[V generic.SignedNumber](width, pos V) Point[V] {
|
|
return [2]V{V(math.Mod(float64(pos), float64(width))), pos / width}
|
|
}
|
|
|
|
// PosToCoordinateX 通过宽度将一个二维数组的顺序位置转换为X坐标
|
|
func PosToCoordinateX[V generic.SignedNumber](width, pos V) V {
|
|
return V(math.Mod(float64(pos), float64(width)))
|
|
}
|
|
|
|
// PosToCoordinateY 通过宽度将一个二维数组的顺序位置转换为Y坐标
|
|
func PosToCoordinateY[V generic.SignedNumber](width, pos V) V {
|
|
return pos / width
|
|
}
|
|
|
|
// PointCopy 复制一个坐标数组
|
|
func PointCopy[V generic.SignedNumber](point Point[V]) Point[V] {
|
|
return NewPoint(point.GetXY())
|
|
}
|
|
|
|
// PointToPosWithMulti 将一组坐标转换为二维数组的顺序位置
|
|
// - 需要确保x的取值范围必须小于width,或者将会得到不正确的值
|
|
func PointToPosWithMulti[V generic.SignedNumber](width V, points ...Point[V]) []V {
|
|
var result = make([]V, len(points), len(points))
|
|
for i := 0; i < len(points); i++ {
|
|
result[i] = PointToPos(width, points[i])
|
|
}
|
|
return result
|
|
}
|
|
|
|
// PosToPointWithMulti 将一组二维数组的顺序位置转换为一组数组坐标
|
|
func PosToPointWithMulti[V generic.SignedNumber](width V, positions ...V) []Point[V] {
|
|
var result = make([]Point[V], len(positions))
|
|
for i := 0; i < len(positions); i++ {
|
|
result[i] = PosToPoint(width, positions[i])
|
|
}
|
|
return result
|
|
}
|
|
|
|
// PosSameRow 返回两个顺序位置在同一宽度是否位于同一行
|
|
func PosSameRow[V generic.SignedNumber](width, pos1, pos2 V) bool {
|
|
return (pos1 / width) == (pos2 / width)
|
|
}
|
|
|
|
// DoublePointToCoordinate 将两个位置转换为 x1, y1, x2, y2 的坐标进行返回
|
|
func DoublePointToCoordinate[V generic.SignedNumber](point1, point2 Point[V]) (x1, y1, x2, y2 V) {
|
|
return point1.GetX(), point1.GetY(), point2.GetX(), point2.GetY()
|
|
}
|
|
|
|
// CalcProjectionPoint 计算一个点到一条线段的最近点(即投影点)的。这个函数接收一个点和一条线段作为输入,线段由两个端点组成。
|
|
// - 该函数的主要用于需要计算一个点到一条线段的最近点的情况下
|
|
func CalcProjectionPoint[V generic.SignedNumber](line LineSegment[V], point Point[V]) Point[V] {
|
|
ax, ay, bx, by := DoublePointToCoordinate(line.GetStart(), line.GetEnd())
|
|
ds := CalcDistanceSquared(ax, ay, bx, by)
|
|
px, py := point.GetXY()
|
|
clamp := maths.Clamp((px-ax)*(bx-ax)+(py-ay)*(by-ay)/ds, V(0), V(1))
|
|
return NewPoint(ax+clamp*(bx-ax), ay+clamp*(by-ay))
|
|
}
|