vRp.CD2g_test/utils/geometry/position.go

246 lines
7.6 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 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()))))
}
// Distance 返回两个点之间的距离
func (slf Point[V]) Distance(point Point[V]) float64 {
return math.Sqrt(float64(slf.DistanceSquared(point)))
}
// DistanceSquared 返回两个点之间的距离的平方
func (slf Point[V]) DistanceSquared(point Point[V]) V {
x, y := slf.GetXY()
px, py := point.GetXY()
return (x-px)*(x-px) + (y-py)*(y-py)
}
// 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))
}