Files
vRp.CD2g_test/utils/geometry/position.go
T
kercylan98 c7a6e09926 feat: 增加几何运算辅助函数
CalcRayIsIntersect 根据给定的位置和角度生成射线,检测射线是否与多边形发生碰撞
CalcRadianWithAngle 根据角度 angle 计算弧度
CalcLineSegmentIsIntersect 计算两条线段是否相交
CalcLineSegmentSlope 计算线段的斜率
CalcLineSegmentIntercept 计算线段的截距
ConvertLineSegmentGeneric 转换线段的泛型类型为特定类型
Point.Min 注释为 Max 的问题修复
2023-06-25 15:14:56 +08:00

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))
}