vRp.CD2g_test/utils/geometry/rectangle.go

341 lines
11 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"
// GetAdjacentTranslatePos 获取一个连续位置的矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置
func GetAdjacentTranslatePos[T any, P generic.SignedNumber](matrix []T, width, pos P) (result []P) {
wf, pf := float64(width), float64(pos)
size := float64(len(matrix))
currentRow := pf / wf
if up := -wf; up >= 0 {
result = append(result, P(up))
}
if down := pf + wf; down < size {
result = append(result, P(down))
}
if left := pf - 1; left >= 0 && currentRow == (left/wf) {
result = append(result, P(left))
}
if right := pf + 1; right < size && currentRow == (right/wf) {
result = append(result, P(right))
}
return
}
// GetAdjacentTranslateCoordinateXY 获取一个基于 x、y 的二维矩阵中,特定位置相邻的最多四个平移方向(上下左右)的位置
func GetAdjacentTranslateCoordinateXY[T any, P generic.SignedNumber](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.SignedNumber](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.SignedNumber](matrix []T, width, pos P) (result []P) {
size := float64(len(matrix))
wf, pf := float64(width), float64(pos)
currentRow := pf / wf
if topLeft := pf - wf - 1; topLeft >= 0 && currentRow-1 == (topLeft/wf) {
result = append(result, P(topLeft))
}
if topRight := pf - wf + 1; topRight >= 0 && currentRow-1 == (topRight/wf) {
result = append(result, P(topRight))
}
if bottomLeft := pf + wf - 1; bottomLeft < size && currentRow+1 == (bottomLeft/wf) {
result = append(result, P(bottomLeft))
}
if bottomRight := pf + wf + 1; bottomRight < size && currentRow+1 == (bottomRight/wf) {
result = append(result, P(bottomRight))
}
return
}
// GetAdjacentDiagonalsCoordinateXY 获取一个基于 x、y 的二维矩阵中,特定位置相邻的对角线最多四个方向的位置
func GetAdjacentDiagonalsCoordinateXY[T any, P generic.SignedNumber](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.SignedNumber](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.SignedNumber](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.SignedNumber](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.SignedNumber](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
}
// GetShapeCoverageAreaWithPoint 通过传入的一组坐标 points 计算一个图形覆盖的矩形范围
func GetShapeCoverageAreaWithPoint[V generic.SignedNumber](points ...Point[V]) (left, right, top, bottom V) {
hasLeft, hasTop := false, false
for _, xy := range points {
x, y := PointToCoordinate(xy)
if x < left || !hasLeft {
hasLeft = true
left = x
}
if x > right {
right = x
}
if y < top || !hasTop {
hasTop = true
top = y
}
if y > bottom {
bottom = y
}
}
return
}
// GetShapeCoverageAreaWithPos 通过传入的一组坐标 positions 计算一个图形覆盖的矩形范围
func GetShapeCoverageAreaWithPos[V generic.SignedNumber](width V, positions ...V) (left, right, top, bottom V) {
hasLeft, hasTop := false, false
for _, pos := range positions {
x, y := PosToCoordinate(width, pos)
if x < left || !hasLeft {
hasLeft = true
left = x
}
if x > right {
right = x
}
if y < top || !hasTop {
hasTop = true
top = y
}
if y > bottom {
bottom = y
}
}
return
}
// CoverageAreaBoundless 将一个图形覆盖矩形范围设置为无边的
// - 无边化表示会将多余的部分进行裁剪,例如图形左边从 2 开始的时候,那么左边将会被裁剪到从 0 开始
func CoverageAreaBoundless[V generic.SignedNumber](l, r, t, b V) (left, right, top, bottom V) {
differentX := 0 - l
differentY := 0 - t
left = l + differentX
right = r + differentX
top = t + differentY
bottom = b + differentY
return
}
// GenerateShapeOnRectangle 生成一组二维坐标的形状
// - 这个形状将被在一个刚好能容纳形状的矩形中表示
// - 为 true 的位置表示了形状的每一个点
func GenerateShapeOnRectangle[V generic.SignedNumber](points ...Point[V]) (result []PointCap[V, bool]) {
left, r, top, b := GetShapeCoverageAreaWithPoint(points...)
_, right, _, bottom := CoverageAreaBoundless(left, r, top, b)
w, h := right+1, bottom+1
result = make([]PointCap[V, bool], int(w*h))
for _, xy := range points {
x, y := xy.GetXY()
sx := x - (r - right)
sy := y - (b - bottom)
pos := CoordinateToPos(w, sx, sy)
pointCap := &result[int(pos)]
pointCap.Point[0] = sx
pointCap.Point[1] = sy
pointCap.Data = true
}
for pos, pointCap := range result {
if !pointCap.Data {
pointCap := &result[pos]
sx, sy := PosToCoordinate(w, V(pos))
pointCap.Point[0] = sx
pointCap.Point[1] = sy
}
}
return
}
// GenerateShapeOnRectangleWithCoordinate 生成一组二维坐标的形状
// - 这个形状将被在一个刚好能容纳形状的矩形中表示
// - 为 true 的位置表示了形状的每一个点
func GenerateShapeOnRectangleWithCoordinate[V generic.SignedNumber](points ...Point[V]) (result [][]bool) {
left, r, top, b := GetShapeCoverageAreaWithPoint(points...)
_, right, _, bottom := CoverageAreaBoundless(left, r, top, b)
w, h := right+1, bottom+1
result = make([][]bool, int(w))
for x := V(0); x < w; x++ {
result[int(x)] = make([]bool, int(h))
}
for _, xy := range points {
x, y := xy.GetXY()
sx := x - (r - right)
sy := y - (b - bottom)
result[int(sx)][int(sy)] = true
}
return
}
// GetExpressibleRectangleBySize 获取一个宽高可表达的所有特定尺寸以上的矩形形状
// - 返回值表示了每一个矩形右下角的x,y位置左上角始终为0, 0
// - 矩形尺寸由大到小
func GetExpressibleRectangleBySize[V generic.SignedNumber](width, height, minWidth, minHeight V) (result []Point[V]) {
sourceWidth := width
if width == 0 || height == 0 {
return nil
}
if width < minWidth || height < minHeight {
return nil
}
width--
height--
for {
rightBottom := NewPoint(width, height)
result = append(result, rightBottom)
if width == 0 && height == 0 || (width < minWidth && height < minHeight) {
return
}
if width == height {
width--
} else if width < height {
if width+1 == sourceWidth {
height--
} else {
width++
height--
}
} else if width > height {
width--
}
}
}
// GetExpressibleRectangle 获取一个宽高可表达的所有矩形形状
// - 返回值表示了每一个矩形右下角的x,y位置左上角始终为0, 0
// - 矩形尺寸由大到小
func GetExpressibleRectangle[V generic.SignedNumber](width, height V) (result []Point[V]) {
return GetExpressibleRectangleBySize(width, height, 1, 1)
}
// GetRectangleFullPointsByXY 通过开始结束坐标获取一个矩形包含的所有点
// - 例如 1,1 到 2,2 的矩形结果为 1,1 2,1 1,2 2,2
func GetRectangleFullPointsByXY[V generic.SignedNumber](startX, startY, endX, endY V) (result []Point[V]) {
for x := startX; x <= endX; x++ {
for y := startY; y <= endY; y++ {
result = append(result, NewPoint(x, y))
}
}
return
}
// GetRectangleFullPoints 获取一个矩形填充满后包含的所有点
func GetRectangleFullPoints[V generic.SignedNumber](width, height V) (result []Point[V]) {
for x := V(0); x < width; x++ {
for y := V(0); y < height; y++ {
result = append(result, NewPoint(x, y))
}
}
return
}
// GetRectangleFullPos 获取一个矩形填充满后包含的所有位置
func GetRectangleFullPos[V generic.SignedNumber](width, height V) (result []V) {
size := int(width * height)
result = make([]V, 0, size)
for pos := 0; pos < size; pos++ {
result[pos] = V(pos)
}
return
}
// CalcRectangleCentroid 计算矩形质心
// - 非多边形质心计算,仅为顶点的平均值 - 该区域中多边形因子的适当质心
func CalcRectangleCentroid[V generic.SignedNumber](shape Shape[V]) Point[V] {
var x, y float64
length := float64(shape.PointCount())
for _, point := range shape.Points() {
x += float64(point.GetX())
y += float64(point.GetY())
}
x /= length
y /= length
return NewPoint(V(x), V(x))
}