diff --git a/utils/g2d/dp/distribution_pattern.go b/utils/g2d/dp/distribution_pattern.go index 0717645..d99c572 100644 --- a/utils/g2d/dp/distribution_pattern.go +++ b/utils/g2d/dp/distribution_pattern.go @@ -1,7 +1,7 @@ package dp import ( - "github.com/kercylan98/minotaur/utils/g2d" + "github.com/kercylan98/minotaur/utils/geometry" ) // NewDistributionPattern 构建一个分布图实例 @@ -43,7 +43,7 @@ func (slf *DistributionPattern[Item]) HasLink(pos int) bool { // - 通过该函数加载的分布图使用的矩阵是复制后的矩阵,因此无法直接通过刷新(Refresh)来更新分布关系 // - 需要通过直接刷新的方式请使用 LoadMatrixWithPos func (slf *DistributionPattern[Item]) LoadMatrix(matrix [][]Item) { - slf.LoadMatrixWithPos(g2d.MatrixToPosMatrix(matrix)) + slf.LoadMatrixWithPos(geometry.CoordinateMatrixToPosMatrix(matrix)) slf.usePos = false } @@ -114,7 +114,7 @@ func (slf *DistributionPattern[Item]) buildRelationships(pos int, item Item) { slf.links[pos] = links } - for _, tp := range g2d.GetAdjacentTranslatePos(slf.matrix, slf.width, pos) { + for _, tp := range geometry.GetAdjacentTranslatePos(slf.matrix, slf.width, pos) { target := slf.matrix[tp] if _, exist := links[tp]; exist || !slf.sameKindVerifyHandle(item, target) { continue diff --git a/utils/g2d/g2d.go b/utils/g2d/g2d.go deleted file mode 100644 index 195a2f5..0000000 --- a/utils/g2d/g2d.go +++ /dev/null @@ -1,140 +0,0 @@ -package g2d - -import "github.com/kercylan98/minotaur/utils/generic" - -// CoordinateToCoordinateArray 将坐标转换为x、y的坐标数组 -func CoordinateToCoordinateArray(x, y int) [2]int { - return [2]int{x, y} -} - -// CoordinateArrayToCoordinate 将坐标数组转换为x和y坐标 -func CoordinateArrayToCoordinate(position [2]int) (x, y int) { - return position[0], position[1] -} - -// CoordinateArrayClone 克隆一个坐标数组 -func CoordinateArrayClone(position [2]int) [2]int { - return [2]int{position[0], position[1]} -} - -// GetAdjacentCoordinates 获取一个矩阵中,特定位置相邻的最多四个方向的坐标 -func GetAdjacentCoordinates[T any](matrix [][]T, x, y int) (result [][2]int) { - width, height := len(matrix), len(matrix[0]) - if tx := x - 1; tx >= 0 { - result = append(result, CoordinateToCoordinateArray(tx, y)) - } - if tx := x + 1; tx < width { - result = append(result, CoordinateToCoordinateArray(tx, y)) - } - if ty := y - 1; ty >= 0 { - result = append(result, CoordinateToCoordinateArray(x, ty)) - } - if ty := y + 1; ty < height { - result = append(result, CoordinateToCoordinateArray(x, ty)) - } - return -} - -// GetAdjacentTranslatePos 获取一个连续位置的矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置 -func GetAdjacentTranslatePos[T any](matrix []T, width, pos int) (result []int) { - size := len(matrix) - currentRow := pos / width - if up := pos - width; up >= 0 { - result = append(result, up) - } - if down := pos + width; down < size { - result = append(result, down) - } - if left := pos - 1; left >= 0 && currentRow == (left/width) { - result = append(result, left) - } - if right := pos + 1; right < size && currentRow == (right/width) { - result = append(result, right) - } - return -} - -// GetAdjacentDiagonalsPos 获取一个连续位置的矩阵中,特定位置相邻的对角线最多四个方向的位置 -func GetAdjacentDiagonalsPos[T any](matrix []T, width, pos int) (result []int) { - size := len(matrix) - currentRow := pos / width - if topLeft := pos - width - 1; topLeft >= 0 && currentRow-1 == (topLeft/width) { - result = append(result, topLeft) - } - if topRight := pos - width + 1; topRight >= 0 && currentRow-1 == (topRight/width) { - result = append(result, topRight) - } - if bottomLeft := pos + width - 1; bottomLeft < size && currentRow+1 == (bottomLeft/width) { - result = append(result, bottomLeft) - } - if bottomRight := pos + width + 1; bottomRight < size && currentRow+1 == (bottomRight/width) { - result = append(result, bottomRight) - } - return -} - -// GetAdjacentPos 获取一个连续位置的矩阵中,特定位置相邻的最多八个方向的位置 -func GetAdjacentPos[T any](matrix []T, width, pos int) (result []int) { - return append(GetAdjacentTranslatePos(matrix, width, pos), GetAdjacentDiagonalsPos(matrix, width, pos)...) -} - -// CoordinateToPos 将坐标转换为二维数组的顺序位置坐标 -// - 需要确保x的取值范围必须小于width,或者将会得到不正确的值 -func CoordinateToPos(width, x, y int) int { - return y*width + x -} - -// CoordinateArrayToPos 将坐标转换为二维数组的顺序位置 -// - 需要确保x的取值范围必须小于width,或者将会得到不正确的值 -func CoordinateArrayToPos(width int, xy [2]int) int { - return CoordinateToPos(width, xy[0], xy[1]) -} - -// CoordinateArrayToPosWithMulti 将一组坐标转换为二维数组的顺序位置 -// - 需要确保x的取值范围必须小于width,或者将会得到不正确的值 -func CoordinateArrayToPosWithMulti(width int, xys ...[2]int) []int { - var result = make([]int, len(xys), len(xys)) - for i := 0; i < len(xys); i++ { - result[i] = CoordinateArrayToPos(width, xys[i]) - } - return result -} - -// PosToCoordinate 通过宽度将一个二维数组的顺序位置转换为xy坐标 -func PosToCoordinate(width, pos int) (x, y int) { - x = pos % width - y = pos / width - return x, y -} - -// PosToCoordinateX 通过宽度将一个二维数组的顺序位置转换为X坐标 -func PosToCoordinateX(width, pos int) int { - return pos % width -} - -// PosToCoordinateY 通过宽度将一个二维数组的顺序位置转换为Y坐标 -func PosToCoordinateY(width, pos int) int { - return pos / width -} - -// MatrixToPosMatrix 将二维矩阵转换为顺序的二维矩阵 -func MatrixToPosMatrix[V any](matrix [][]V) (width int, posMatrix []V) { - width = len(matrix) - height := len(matrix[0]) - posMatrix = make([]V, width*height) - for x := 0; x < width; x++ { - for y := 0; y < height; y++ { - posMatrix[CoordinateToPos(width, x, y)] = matrix[x][y] - } - } - return -} - -// PointOnLine 接受六个参数 top、bottom、left、right 和 x、y,分别表示一个矩形位置尺寸和一个点的横纵坐标,判断这个点是否在一条线段上。 -// - 首先计算点 (x, y) 与线段起点 (left, top) 之间的斜率即 (x - left) / (y - top)。 -// - 然后计算线段起点 (left, top) 与线段终点 (right, bottom) 之间的斜率,即 (right - left) / (bottom - top)。 -// - 如果这两个斜率等,那么点 (x, y) 就在这条线段上。为了避免除法可能导致的浮点数误差,我们可以将两个斜率的计算转换为乘法形式,即比较 (x - left) * (bottom - top) 是否等于 (right - left) * y - top)。 -// - 如果上述等式成立,说明点 (x, y) 在线段上,函数返回 true;否则,返回 false。 -func PointOnLine[V generic.Number](top, bottom, left, right, x, y V) bool { - return (x-left)*(bottom-top) == (right-left)*(y-top) -} diff --git a/utils/g2d/g2d_test.go b/utils/g2d/g2d_test.go index 269cbdb..2da8e5e 100644 --- a/utils/g2d/g2d_test.go +++ b/utils/g2d/g2d_test.go @@ -2,15 +2,16 @@ package g2d import ( "fmt" + "github.com/kercylan98/minotaur/utils/geometry" "testing" ) func TestPositionIntToXY(t *testing.T) { - pos := CoordinateToPos(9, 7, 8) + pos := geometry.CoordinateToPos(9, 7, 8) fmt.Println(pos) - fmt.Println(PosToCoordinate(9, pos)) + fmt.Println(geometry.PosToCoordinate(9, pos)) - fmt.Println(CoordinateToPos(65000, 61411, 158266)) - fmt.Println(PosToCoordinate(65000, 10287351411)) + fmt.Println(geometry.CoordinateToPos(65000, 61411, 158266)) + fmt.Println(geometry.PosToCoordinate(65000, 10287351411)) } diff --git a/utils/g2d/matrix/matrix.go b/utils/g2d/matrix/matrix.go index 94884df..94061de 100644 --- a/utils/g2d/matrix/matrix.go +++ b/utils/g2d/matrix/matrix.go @@ -1,6 +1,8 @@ package matrix -import "github.com/kercylan98/minotaur/utils/g2d" +import ( + "github.com/kercylan98/minotaur/utils/geometry" +) // NewMatrix 生成特定宽高的二维矩阵 // - 虽然提供了通过x、y坐标的操作函数,但是建议无论如何使用pos进行处理 @@ -41,7 +43,7 @@ func (slf *Matrix[T]) GetMatrix() [][]T { for x := 0; x < slf.w; x++ { ys := make([]T, slf.h) for y := 0; y < slf.h; y++ { - ys[y] = slf.m[g2d.CoordinateToPos(slf.w, x, y)] + ys[y] = slf.m[geometry.CoordinateToPos(slf.w, x, y)] } result[x] = ys } @@ -55,7 +57,7 @@ func (slf *Matrix[T]) GetMatrixWithPos() []T { // Get 获取特定坐标的内容 func (slf *Matrix[T]) Get(x, y int) (value T) { - return slf.m[g2d.CoordinateToPos(slf.w, x, y)] + return slf.m[geometry.CoordinateToPos(slf.w, x, y)] } // GetWithPos 获取特定坐标的内容 @@ -65,7 +67,7 @@ func (slf *Matrix[T]) GetWithPos(pos int) (value T) { // Set 设置特定坐标的内容 func (slf *Matrix[T]) Set(x, y int, data T) { - slf.m[g2d.CoordinateToPos(slf.w, x, y)] = data + slf.m[geometry.CoordinateToPos(slf.w, x, y)] = data } // SetWithPos 设置特定坐标的内容 @@ -76,7 +78,7 @@ func (slf *Matrix[T]) SetWithPos(pos int, data T) { // Swap 交换两个位置的内容 func (slf *Matrix[T]) Swap(x1, y1, x2, y2 int) { a, b := slf.Get(x1, y1), slf.Get(x2, y2) - slf.m[g2d.CoordinateToPos(slf.w, x1, y1)], slf.m[g2d.CoordinateToPos(slf.w, x2, y2)] = b, a + slf.m[geometry.CoordinateToPos(slf.w, x1, y1)], slf.m[geometry.CoordinateToPos(slf.w, x2, y2)] = b, a } // SwapWithPos 交换两个位置的内容 @@ -87,8 +89,8 @@ func (slf *Matrix[T]) SwapWithPos(pos1, pos2 int) { // TrySwap 尝试交换两个位置的内容,交换后不满足表达式时进行撤销 func (slf *Matrix[T]) TrySwap(x1, y1, x2, y2 int, expressionHandle func(matrix *Matrix[T]) bool) { - pos1 := g2d.CoordinateToPos(slf.w, x1, y1) - pos2 := g2d.CoordinateToPos(slf.w, x2, y2) + pos1 := geometry.CoordinateToPos(slf.w, x1, y1) + pos2 := geometry.CoordinateToPos(slf.w, x2, y2) a, b := slf.Get(x1, y1), slf.Get(x2, y2) slf.m[pos1], slf.m[pos2] = b, a if !expressionHandle(slf) { @@ -109,7 +111,7 @@ func (slf *Matrix[T]) TrySwapWithPos(pos1, pos2 int, expressionHandle func(matri func (slf *Matrix[T]) FillFull(generateHandle func(x, y int) T) { for x := 0; x < slf.w; x++ { for y := 0; y < slf.h; y++ { - slf.m[g2d.CoordinateToPos(slf.w, x, y)] = generateHandle(x, y) + slf.m[geometry.CoordinateToPos(slf.w, x, y)] = generateHandle(x, y) } } } diff --git a/utils/g2d/path/landform.go b/utils/g2d/path/landform.go index 8578fb9..4beda66 100644 --- a/utils/g2d/path/landform.go +++ b/utils/g2d/path/landform.go @@ -1,7 +1,7 @@ package path import ( - "github.com/kercylan98/minotaur/utils/g2d" + "github.com/kercylan98/minotaur/utils/geometry" ) func NewLandform(pos int, features ...*LandformFeature) *Landform { @@ -29,7 +29,7 @@ type Landform struct { // GetCoordinate 获取这个路径地貌指向的 x 和 y 坐标 // - 建议通过 GetPos 来进行获取,这样可以避免一次转换 func (slf *Landform) GetCoordinate() (x, y int) { - return g2d.PosToCoordinate(slf.width, slf.pos) + return geometry.PosToCoordinate(slf.width, slf.pos) } // GetPos 获取这个路径地貌指向的 pos 位置 diff --git a/utils/g2d/path/terrain.go b/utils/g2d/path/terrain.go index 4ea76e0..0ca3177 100644 --- a/utils/g2d/path/terrain.go +++ b/utils/g2d/path/terrain.go @@ -2,7 +2,7 @@ package path import ( "container/heap" - "github.com/kercylan98/minotaur/utils/g2d" + "github.com/kercylan98/minotaur/utils/geometry" ) // NewTerrain 返回一个大小为 width 和 height 的新的路径覆盖信息,landformWidth 和 landformHeight 将对每 @@ -49,7 +49,7 @@ type Terrain struct { // Get 返回 x 和 y 指向的地貌信息 // - 通常更建议使用 GetWithPos 进行获取,因为这样可以减少一次转换 func (slf *Terrain) Get(x, y int) *Landform { - return slf.matrix[g2d.CoordinateToPos(slf.width, x, y)] + return slf.matrix[geometry.CoordinateToPos(slf.width, x, y)] } // GetWithPos 返回 pos 指向的地貌信息 @@ -105,7 +105,7 @@ func (slf *Terrain) GetPath(startPos, endPos int, diagonals, wallsBlockDiagonals break } - for _, adjacent := range g2d.GetAdjacentTranslatePos(slf.matrix, slf.width, node.landform.pos) { + for _, adjacent := range geometry.GetAdjacentTranslatePos(slf.matrix, slf.width, node.landform.pos) { landform := slf.GetWithPos(adjacent) n := &Node{landform: landform, parent: node, cost: landform.GetTotalCost() + node.cost} if _, exist := checkedLandforms[adjacent]; n.landform.Walkable() && !exist { diff --git a/utils/g2d/shape.go b/utils/g2d/shape.go index 10d0324..807d183 100644 --- a/utils/g2d/shape.go +++ b/utils/g2d/shape.go @@ -2,6 +2,7 @@ package g2d import ( "github.com/kercylan98/minotaur/utils/g2d/shape" + "github.com/kercylan98/minotaur/utils/geometry" "sort" ) @@ -149,7 +150,7 @@ func SearchNotRepeatCross(xys ...[2]int) (result [][][2]int) { for _, xy := range xys { var points [][2]int var find = map[int]bool{} - x, y := CoordinateArrayToCoordinate(xy) + x, y := geometry.CoordinateArrayToCoordinate(xy) x = x + (0 - left) y = y + (0 - top) // 搜索四个方向 @@ -204,7 +205,7 @@ func SearchNotRepeatCross(xys ...[2]int) (result [][][2]int) { for _, points := range result { var match = true for _, point := range points { - x, y := CoordinateArrayToCoordinate(point) + x, y := geometry.CoordinateArrayToCoordinate(point) x = x + (0 - left) y = y + (0 - top) if record[x][y] { @@ -235,7 +236,7 @@ func SearchContainCross(xys ...[2]int) bool { for _, xy := range xys { var points [][2]int var find = map[int]bool{} - x, y := CoordinateArrayToCoordinate(xy) + x, y := geometry.CoordinateArrayToCoordinate(xy) x = x + (0 - left) y = y + (0 - top) // 搜索四个方向 @@ -303,7 +304,7 @@ func SearchNotRepeatStraightLine(minLength int, xys ...[2]int) (result [][][2]in for _, xy := range xys { var points [][2]int var find = map[int]bool{} - x, y := CoordinateArrayToCoordinate(xy) + x, y := geometry.CoordinateArrayToCoordinate(xy) x = x + (0 - left) y = y + (0 - top) // 搜索四个方向 @@ -362,7 +363,7 @@ func SearchNotRepeatStraightLine(minLength int, xys ...[2]int) (result [][][2]in for _, points := range result { var match = true for _, point := range points { - x, y := CoordinateArrayToCoordinate(point) + x, y := geometry.CoordinateArrayToCoordinate(point) x = x + (0 - left) y = y + (0 - top) if record[x][y] { @@ -396,7 +397,7 @@ func SearchContainStraightLine(minLength int, xys ...[2]int) bool { for _, xy := range xys { var points [][2]int var find = map[int]bool{} - x, y := CoordinateArrayToCoordinate(xy) + x, y := geometry.CoordinateArrayToCoordinate(xy) x = x + (0 - left) y = y + (0 - top) // 搜索四个方向 @@ -467,7 +468,7 @@ func SearchNotRepeatT(minLength int, xys ...[2]int) (result [][][2]int) { for _, xy := range xys { var points [][2]int var find = map[int]bool{} - x, y := CoordinateArrayToCoordinate(xy) + x, y := geometry.CoordinateArrayToCoordinate(xy) x = x + (0 - left) y = y + (0 - top) // 搜索四个方向 @@ -513,7 +514,7 @@ func SearchNotRepeatT(minLength int, xys ...[2]int) (result [][][2]int) { for _, points := range result { var match = true for _, point := range points { - x, y := CoordinateArrayToCoordinate(point) + x, y := geometry.CoordinateArrayToCoordinate(point) x = x + (0 - left) y = y + (0 - top) if record[x][y] { @@ -547,7 +548,7 @@ func SearchContainT(minLength int, xys ...[2]int) bool { for _, xy := range xys { var points [][2]int var find = map[int]bool{} - x, y := CoordinateArrayToCoordinate(xy) + x, y := geometry.CoordinateArrayToCoordinate(xy) x = x + (0 - left) y = y + (0 - top) // 搜索四个方向 @@ -605,7 +606,7 @@ func SearchNotRepeatRightAngle(minLength int, xys ...[2]int) (result [][][2]int) for _, xy := range xys { var points [][2]int var find = map[int]bool{} - x, y := CoordinateArrayToCoordinate(xy) + x, y := geometry.CoordinateArrayToCoordinate(xy) x = x + (0 - left) y = y + (0 - top) // 搜索四个方向 @@ -665,7 +666,7 @@ func SearchNotRepeatRightAngle(minLength int, xys ...[2]int) (result [][][2]int) for _, points := range result { var match = true for _, point := range points { - x, y := CoordinateArrayToCoordinate(point) + x, y := geometry.CoordinateArrayToCoordinate(point) x = x + (0 - left) y = y + (0 - top) if record[x][y] { @@ -699,7 +700,7 @@ func SearchContainRightAngle(minLength int, xys ...[2]int) bool { for _, xy := range xys { var points [][2]int var find = map[int]bool{} - x, y := CoordinateArrayToCoordinate(xy) + x, y := geometry.CoordinateArrayToCoordinate(xy) x = x + (0 - left) y = y + (0 - top) // 搜索四个方向 @@ -783,7 +784,7 @@ func SearchNotRepeatFullRectangle(minWidth, minHeight int, xys ...[2]int) (resul points := GetRectangleFullPoints(s[0]+1, s[1]+1) find := 0 for _, point := range points { - px, py := CoordinateArrayToCoordinate(point) + px, py := geometry.CoordinateArrayToCoordinate(point) ox, oy := px+x, py+y if record[ox][oy] || !rectangleShape[ox][oy] { find = 0 @@ -793,7 +794,7 @@ func SearchNotRepeatFullRectangle(minWidth, minHeight int, xys ...[2]int) (resul } if find == len(points) { for _, point := range points { - px, py := CoordinateArrayToCoordinate(point) + px, py := geometry.CoordinateArrayToCoordinate(point) record[px+x][py+y] = true } result = append(result, [2][2]int{ @@ -834,7 +835,7 @@ func SearchContainFullRectangle(minWidth, minHeight int, xys ...[2]int) bool { points := GetRectangleFullPoints(s[0]+1, s[1]+1) find := 0 for _, point := range points { - px, py := CoordinateArrayToCoordinate(point) + px, py := geometry.CoordinateArrayToCoordinate(point) ox, oy := px+x, py+y if record[ox][oy] || !rectangleShape[ox][oy] { find = 0 @@ -844,7 +845,7 @@ func SearchContainFullRectangle(minWidth, minHeight int, xys ...[2]int) bool { } if find == len(points) { for _, point := range points { - px, py := CoordinateArrayToCoordinate(point) + px, py := geometry.CoordinateArrayToCoordinate(point) record[px+x][py+y] = true } return true @@ -931,7 +932,7 @@ func GenerateShape(xys ...[2]int) [][]bool { m[x] = make([]bool, h) } for _, xy := range xys { - x, y := CoordinateArrayToCoordinate(xy) + x, y := geometry.CoordinateArrayToCoordinate(xy) m[x-(r-right)][y-(b-bottom)] = true } return m @@ -953,7 +954,7 @@ func CoverageAreaBoundless(l, r, t, b int) (left, right, top, bottom int) { func GetShapeCoverageArea(xys ...[2]int) (left, right, top, bottom int) { left, top = -1, -1 for _, xy := range xys { - x, y := CoordinateArrayToCoordinate(xy) + x, y := geometry.CoordinateArrayToCoordinate(xy) if x < left || left == -1 { left = x } diff --git a/utils/geometry/line.go b/utils/geometry/line.go new file mode 100644 index 0000000..e03f9fa --- /dev/null +++ b/utils/geometry/line.go @@ -0,0 +1,23 @@ +package geometry + +import ( + "github.com/kercylan98/minotaur/utils/generic" +) + +// PointOnLineWithCoordinate 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 +func PointOnLineWithCoordinate[V generic.Number](x1, y1, x2, y2, x, y V) bool { + return (x-x1)*(y2-y1) == (x2-x1)*(y-y1) +} + +// +//func PointOnLineWithPos[V generic.Number](width, pos1, pos2, pos V) bool { +// x1, y1 := PosToCoordinate(width, pos1) +// x2, y2 := PosToCoordinate(width, pos2) +// return (x-x1)*(y2-y1) == (x2-x1)*(y-y1) +//} +// +//// PointOnSegment 通过一个线段两个点的位置和一个点的坐标,判断这个点是否在一条线段上 +//// - 与 PointOnLine 不同的是, PointOnSegment 中会判断线段及点的位置是否正确 +//func PointOnSegment[V generic.Number](x1, y1, x2, y2, x, y V) bool { +// return x >= x1 && x <= x2 && y >= y1 && y <= y2 && PointOnLine(x1, y1, x2, y2, x, y) +//} diff --git a/utils/geometry/position.go b/utils/geometry/position.go new file mode 100644 index 0000000..3de3147 --- /dev/null +++ b/utils/geometry/position.go @@ -0,0 +1,108 @@ +package geometry + +import ( + "github.com/kercylan98/minotaur/utils/generic" + "math" +) + +// Point 表示了一个由 x、y 坐标组成的点 +type Point[V generic.Number] [2]V + +// NewPoint 创建一个由 x、y 坐标组成的点 +func NewPoint[V generic.Number](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 CoordinateArrayToPos(width, slf) +} + +// Copy 复制一个点位置 +func (slf Point[V]) Copy() Point[V] { + return CoordinateArrayCopy(slf) +} + +// CoordinateToCoordinateArray 将坐标转换为x、y的坐标数组 +func CoordinateToCoordinateArray[V generic.Number](x, y V) Point[V] { + return [2]V{x, y} +} + +// CoordinateToPos 将坐标转换为二维数组的顺序位置坐标 +// - 需要确保x的取值范围必须小于width,或者将会得到不正确的值 +func CoordinateToPos[V generic.Number](width, x, y V) V { + return y*width + x +} + +// CoordinateArrayToCoordinate 将坐标数组转换为x和y坐标 +func CoordinateArrayToCoordinate[V generic.Number](position Point[V]) (x, y V) { + return position[0], position[1] +} + +// CoordinateArrayToPos 将坐标转换为二维数组的顺序位置 +// - 需要确保x的取值范围必须小于width,或者将会得到不正确的值 +func CoordinateArrayToPos[V generic.Number](width V, xy Point[V]) V { + return CoordinateToPos(width, xy[0], xy[1]) +} + +// PosToCoordinate 通过宽度将一个二维数组的顺序位置转换为xy坐标 +func PosToCoordinate[V generic.Number](width, pos V) (x, y V) { + + x = V(math.Mod(float64(pos), float64(width))) + y = pos / width + return x, y +} + +// PosToCoordinateArray 通过宽度将一个二维数组的顺序位置转换为x、y的坐标数组 +func PosToCoordinateArray[V generic.Number](width, pos V) Point[V] { + return [2]V{V(math.Mod(float64(pos), float64(width))), pos / width} +} + +// PosToCoordinateX 通过宽度将一个二维数组的顺序位置转换为X坐标 +func PosToCoordinateX[V generic.Number](width, pos V) V { + return V(math.Mod(float64(pos), float64(width))) +} + +// PosToCoordinateY 通过宽度将一个二维数组的顺序位置转换为Y坐标 +func PosToCoordinateY[V generic.Number](width, pos V) V { + return pos / width +} + +// CoordinateArrayCopy 复制一个坐标数组 +func CoordinateArrayCopy[V generic.Number](position Point[V]) Point[V] { + return NewPoint(position[0], position[1]) +} + +// CoordinateArrayToPosWithMulti 将一组坐标转换为二维数组的顺序位置 +// - 需要确保x的取值范围必须小于width,或者将会得到不正确的值 +func CoordinateArrayToPosWithMulti[V generic.Number](width V, xys ...Point[V]) []V { + var result = make([]V, len(xys), len(xys)) + for i := 0; i < len(xys); i++ { + result[i] = CoordinateArrayToPos(width, xys[i]) + } + return result +} + +// PosToCoordinateArrayWithMulti 将一组二维数组的顺序位置转换为一组数组坐标 +func PosToCoordinateArrayWithMulti[V generic.Number](width V, positions ...V) []Point[V] { + var result = make([]Point[V], len(positions)) + for i := 0; i < len(positions); i++ { + result[i] = PosToCoordinateArray(width, positions[i]) + } + return result +} diff --git a/utils/geometry/position_test.go b/utils/geometry/position_test.go new file mode 100644 index 0000000..d9e465f --- /dev/null +++ b/utils/geometry/position_test.go @@ -0,0 +1,11 @@ +package geometry + +import ( + "fmt" + "testing" +) + +func TestNewPoint(t *testing.T) { + p := [2]int{1, 1} + fmt.Println(CoordinateArrayToPos(9, p)) +} diff --git a/utils/geometry/rectangle.go b/utils/geometry/rectangle.go new file mode 100644 index 0000000..d60302e --- /dev/null +++ b/utils/geometry/rectangle.go @@ -0,0 +1,145 @@ +package geometry + +import "github.com/kercylan98/minotaur/utils/generic" + +// GetAdjacentTranslatePos 获取一个连续位置的矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置 +func GetAdjacentTranslatePos[T any, P generic.Number](matrix []T, width, pos P) (result []P) { + size := P(len(matrix)) + currentRow := pos / width + if up := pos - width; up >= 0 { + result = append(result, up) + } + if down := pos + width; down < size { + result = append(result, down) + } + if left := pos - 1; left >= 0 && currentRow == (left/width) { + result = append(result, left) + } + if right := pos + 1; right < size && currentRow == (right/width) { + result = append(result, right) + } + return +} + +// GetAdjacentTranslateCoordinateXY 获取一个基于 x、y 的二维矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置 +func GetAdjacentTranslateCoordinateXY[T any, P generic.Number](matrix [][]T, x, y P) (result []Point[P]) { + width := P(len(matrix)) + height := P(len(matrix[0])) + if up := y - 1; up >= 0 { + result = append(result, NewPoint(x, up)) + } + if down := y + 1; down < height { + result = append(result, NewPoint(x, down)) + } + if left := x - 1; left >= 0 { + result = append(result, NewPoint(left, y)) + } + if right := x + 1; right < width { + result = append(result, NewPoint(right, y)) + } + return +} + +// GetAdjacentTranslateCoordinateYX 获取一个基于 y、x 的二维矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置 +func GetAdjacentTranslateCoordinateYX[T any, P generic.Number](matrix [][]T, x, y P) (result []Point[P]) { + width := P(len(matrix[0])) + height := P(len(matrix)) + if up := y - 1; up >= 0 { + result = append(result, NewPoint(x, up)) + } + if down := y + 1; down < height { + result = append(result, NewPoint(x, down)) + } + if left := x - 1; left >= 0 { + result = append(result, NewPoint(left, y)) + } + if right := x + 1; right < width { + result = append(result, NewPoint(right, y)) + } + return +} + +// GetAdjacentDiagonalsPos 获取一个连续位置的矩阵中,特定位置相邻的对角线最多四个方向的位置 +func GetAdjacentDiagonalsPos[T any, P generic.Number](matrix []T, width, pos P) (result []P) { + size := P(len(matrix)) + currentRow := pos / width + if topLeft := pos - width - 1; topLeft >= 0 && currentRow-1 == (topLeft/width) { + result = append(result, topLeft) + } + if topRight := pos - width + 1; topRight >= 0 && currentRow-1 == (topRight/width) { + result = append(result, topRight) + } + if bottomLeft := pos + width - 1; bottomLeft < size && currentRow+1 == (bottomLeft/width) { + result = append(result, bottomLeft) + } + if bottomRight := pos + width + 1; bottomRight < size && currentRow+1 == (bottomRight/width) { + result = append(result, bottomRight) + } + return +} + +// GetAdjacentDiagonalsCoordinateXY 获取一个基于 x、y 的二维矩阵中,特定位置相邻的对角线最多四个方向的位置 +func GetAdjacentDiagonalsCoordinateXY[T any, P generic.Number](matrix [][]T, x, y P) (result []Point[P]) { + width := P(len(matrix[0])) + height := P(len(matrix)) + if nx, ny := x-1, y-1; nx >= 0 && ny >= 0 { + result = append(result, NewPoint(nx, ny)) + } + if nx, ny := x+1, y-1; nx < width && ny >= 0 { + result = append(result, NewPoint(nx, ny)) + } + if nx, ny := x-1, y+1; nx >= 0 && ny < height { + result = append(result, NewPoint(nx, ny)) + } + if nx, ny := x+1, y+1; nx < width && ny < height { + result = append(result, NewPoint(nx, ny)) + } + return +} + +// GetAdjacentDiagonalsCoordinateYX 获取一个基于 tx 的二维矩阵中,特定位置相邻的对角线最多四个方向的位置 +func GetAdjacentDiagonalsCoordinateYX[T any, P generic.Number](matrix [][]T, x, y P) (result []Point[P]) { + width := P(len(matrix)) + height := P(len(matrix[0])) + if nx, ny := x-1, y-1; nx >= 0 && ny >= 0 { + result = append(result, NewPoint(nx, ny)) + } + if nx, ny := x+1, y-1; nx < width && ny >= 0 { + result = append(result, NewPoint(nx, ny)) + } + if nx, ny := x-1, y+1; nx >= 0 && ny < height { + result = append(result, NewPoint(nx, ny)) + } + if nx, ny := x+1, y+1; nx < width && ny < height { + result = append(result, NewPoint(nx, ny)) + } + return +} + +// GetAdjacentPos 获取一个连续位置的矩阵中,特定位置相邻的最多八个方向的位置 +func GetAdjacentPos[T any, P generic.Number](matrix []T, width, pos P) (result []P) { + return append(GetAdjacentTranslatePos(matrix, width, pos), GetAdjacentDiagonalsPos(matrix, width, pos)...) +} + +// GetAdjacentCoordinateXY 获取一个基于 x、y 的二维矩阵中,特定位置相邻的最多八个方向的位置 +func GetAdjacentCoordinateXY[T any, P generic.Number](matrix [][]T, x, y P) (result []Point[P]) { + return append(GetAdjacentTranslateCoordinateXY(matrix, x, y), GetAdjacentDiagonalsCoordinateXY(matrix, x, y)...) +} + +// GetAdjacentCoordinateYX 获取一个基于 yx 的二维矩阵中,特定位置相邻的最多八个方向的位置 +func GetAdjacentCoordinateYX[T any, P generic.Number](matrix [][]T, x, y P) (result []Point[P]) { + return append(GetAdjacentTranslateCoordinateYX(matrix, x, y), GetAdjacentDiagonalsCoordinateYX(matrix, x, y)...) +} + +// CoordinateMatrixToPosMatrix 将二维矩阵转换为顺序的二维矩阵 +func CoordinateMatrixToPosMatrix[V any](matrix [][]V) (width int, posMatrix []V) { + width = len(matrix) + height := len(matrix[0]) + posMatrix = make([]V, width*height) + for x := 0; x < width; x++ { + for y := 0; y < height; y++ { + posMatrix[CoordinateToPos(width, x, y)] = matrix[x][y] + } + } + return +}