vRp.CD2g_test/utils/g2d/shape.go

662 lines
15 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 g2d
import (
"github.com/kercylan98/minotaur/utils/g2d/shape"
"sort"
)
type MatrixShapeSearchResult[Mark any] struct {
Shape *shape.Shape[Mark]
Points []shape.Point
}
// MatrixShapeSearchWithYX 二维矩阵形状搜索
func MatrixShapeSearchWithYX[T any, Mark any](matrix [][]T, shapes []*shape.Shape[Mark], checkMatchHandle func(val T) bool) []MatrixShapeSearchResult[Mark] {
record := map[int]map[int]bool{}
width := len(matrix[0])
height := len(matrix)
for x := 0; x < width; x++ {
for y := 0; y < height; y++ {
record[x] = map[int]bool{y: true}
}
}
var result []MatrixShapeSearchResult[Mark]
for _, s := range shapes {
points := s.GetPoints()
x, y := 0, 0
mx, my := s.GetMaxXY()
for {
if x+mx >= width {
x = 0
y++
}
if y+my >= height {
break
}
var count int
for _, point := range points {
px, py := point.GetXY()
px, py = px+x, py+y
if record[px][py] {
break
}
if checkMatchHandle(matrix[py][px]) {
count++
} else {
break
}
}
if count == len(points) {
target := MatrixShapeSearchResult[Mark]{
Shape: s,
}
for _, point := range points {
px, py := point.GetXY()
px, py = px+x, py+y
ys, exist := record[px]
if !exist {
ys = map[int]bool{}
record[px] = ys
}
ys[py] = true
target.Points = append(target.Points, shape.NewPoint(px, py))
}
result = append(result, target)
}
x++
}
}
return result
}
// MatrixShapeSearchWithXY 二维矩阵形状搜索
func MatrixShapeSearchWithXY[T any, Mark any](matrix [][]T, shapes []*shape.Shape[Mark], checkMatchHandle func(val T) bool) []MatrixShapeSearchResult[Mark] {
width := len(matrix)
height := len(matrix[0])
record := map[int]map[int]bool{}
for x := 0; x < width; x++ {
for y := 0; y < height; y++ {
record[x] = map[int]bool{y: true}
}
}
var result []MatrixShapeSearchResult[Mark]
for _, s := range shapes {
points := s.GetPoints()
x, y := 0, 0
mx, my := s.GetMaxXY()
for {
if x+mx >= width {
x = 0
y++
}
if y+my >= height {
break
}
var count int
for _, point := range points {
px, py := point.GetXY()
px, py = px+x, py+y
if record[px][py] {
break
}
if checkMatchHandle(matrix[px][py]) {
count++
} else {
break
}
}
if count == len(points) {
target := MatrixShapeSearchResult[Mark]{
Shape: s,
}
for _, point := range points {
px, py := point.GetXY()
px, py = px+x, py+y
ys, exist := record[px]
if !exist {
ys = map[int]bool{}
record[px] = ys
}
ys[py] = true
target.Points = append(target.Points, shape.NewPoint(px, py))
}
result = append(result, target)
}
x++
}
}
return result
}
// SearchNotRepeatCross 在一组二维坐标中从大到小搜索不重复交叉(十字)线
// - 不重复指一个位置被使用后将不会被其他交叉线(十字)使用
func SearchNotRepeatCross(xys ...[2]int) (result [][][2]int) {
left, _, top, _ := GetShapeCoverageArea(xys...)
rectangleShape := GenerateShape(xys...)
record := map[int]map[int]bool{}
for x := 0; x < len(rectangleShape); x++ {
for y := 0; y < len(rectangleShape[0]); y++ {
record[x] = map[int]bool{}
}
}
for _, xy := range xys {
var points [][2]int
var find = map[int]bool{}
x, y := PositionArrayToXY(xy)
x = x + (0 - left)
y = y + (0 - top)
// 搜索四个方向
for sx := x - 1; sx >= 0; sx-- {
if !rectangleShape[sx][y] {
break
}
find[1] = true
points = append(points, [2]int{sx + left, y + top})
}
if !find[1] {
continue
}
for sx := x + 1; sx < len(rectangleShape); sx++ {
if !rectangleShape[sx][y] {
break
}
find[2] = true
points = append(points, [2]int{sx + left, y + top})
}
if !find[2] {
continue
}
for sy := y - 1; sy >= 0; sy-- {
if !rectangleShape[x][sy] {
break
}
find[3] = true
points = append(points, [2]int{x + left, sy + top})
}
if !find[3] {
continue
}
for sy := y + 1; sy < len(rectangleShape[0]); sy++ {
if !rectangleShape[x][sy] {
break
}
find[4] = true
points = append(points, [2]int{x + left, sy + top})
}
if !find[4] {
continue
}
result = append(result, append(points, [2]int{x + left, y + top}))
}
sort.Slice(result, func(i, j int) bool {
return len(result[i]) > len(result[j])
})
var notRepeat [][][2]int
for _, points := range result {
var match = true
for _, point := range points {
x, y := PositionArrayToXY(point)
x = x + (0 - left)
y = y + (0 - top)
if record[x][y] {
match = false
break
}
record[x][y] = true
}
if match {
notRepeat = append(notRepeat, points)
}
}
return notRepeat
}
// SearchNotRepeatStraightLine 在一组二维坐标中从大到小搜索不重复的直线
// - 最低需要长度为3
func SearchNotRepeatStraightLine(minLength int, xys ...[2]int) (result [][][2]int) {
if minLength < 3 {
return nil
}
left, _, top, _ := GetShapeCoverageArea(xys...)
rectangleShape := GenerateShape(xys...)
record := map[int]map[int]bool{}
for x := 0; x < len(rectangleShape); x++ {
for y := 0; y < len(rectangleShape[0]); y++ {
record[x] = map[int]bool{}
}
}
for _, xy := range xys {
var points [][2]int
var find = map[int]bool{}
x, y := PositionArrayToXY(xy)
x = x + (0 - left)
y = y + (0 - top)
// 搜索四个方向
for sx := x - 1; sx >= 0; sx-- {
if !rectangleShape[sx][y] {
break
}
find[1] = true
points = append(points, [2]int{sx + left, y + top})
}
for sx := x + 1; sx < len(rectangleShape); sx++ {
if !rectangleShape[sx][y] {
break
}
find[2] = true
points = append(points, [2]int{sx + left, y + top})
}
if len(find) == 0 {
points = nil
} else if len(points) >= minLength-1 {
goto end
} else {
points = nil
}
for sy := y - 1; sy >= 0; sy-- {
if !rectangleShape[x][sy] {
break
}
find[3] = true
points = append(points, [2]int{x + left, sy + top})
}
for sy := y + 1; sy < len(rectangleShape[0]); sy++ {
if !rectangleShape[x][sy] {
break
}
find[4] = true
points = append(points, [2]int{x + left, sy + top})
}
if !find[3] && !find[4] {
continue
}
end:
{
if len(points) < minLength-1 {
continue
}
result = append(result, append(points, [2]int{x + left, y + top}))
}
}
sort.Slice(result, func(i, j int) bool {
return len(result[i]) > len(result[j])
})
var notRepeat [][][2]int
for _, points := range result {
if len(points) < minLength {
continue
}
var match = true
for _, point := range points {
x, y := PositionArrayToXY(point)
x = x + (0 - left)
y = y + (0 - top)
if record[x][y] {
match = false
break
}
record[x][y] = true
}
if match {
notRepeat = append(notRepeat, points)
}
}
return notRepeat
}
// SearchNotRepeatT 在一组二维坐标中从大到小搜索不重复T型T线
func SearchNotRepeatT(minLength int, xys ...[2]int) (result [][][2]int) {
if minLength < 4 {
return nil
}
left, _, top, _ := GetShapeCoverageArea(xys...)
rectangleShape := GenerateShape(xys...)
record := map[int]map[int]bool{}
for x := 0; x < len(rectangleShape); x++ {
for y := 0; y < len(rectangleShape[0]); y++ {
record[x] = map[int]bool{}
}
}
for _, xy := range xys {
var points [][2]int
var find = map[int]bool{}
x, y := PositionArrayToXY(xy)
x = x + (0 - left)
y = y + (0 - top)
// 搜索四个方向
for sx := x - 1; sx >= 0; sx-- {
if !rectangleShape[sx][y] {
break
}
find[1] = true
points = append(points, [2]int{sx + left, y + top})
}
for sx := x + 1; sx < len(rectangleShape); sx++ {
if !rectangleShape[sx][y] {
break
}
find[2] = true
points = append(points, [2]int{sx + left, y + top})
}
for sy := y - 1; sy >= 0; sy-- {
if !rectangleShape[x][sy] {
break
}
find[3] = true
points = append(points, [2]int{x + left, sy + top})
}
for sy := y + 1; sy < len(rectangleShape[0]); sy++ {
if !rectangleShape[x][sy] {
break
}
find[4] = true
points = append(points, [2]int{x + left, sy + top})
}
if len(find) != 3 {
continue
}
result = append(result, append(points, [2]int{x + left, y + top}))
}
sort.Slice(result, func(i, j int) bool {
return len(result[i]) > len(result[j])
})
var notRepeat [][][2]int
for _, points := range result {
if len(points) < minLength {
continue
}
var match = true
for _, point := range points {
x, y := PositionArrayToXY(point)
x = x + (0 - left)
y = y + (0 - top)
if record[x][y] {
match = false
break
}
record[x][y] = true
}
if match {
notRepeat = append(notRepeat, points)
}
}
return notRepeat
}
// SearchNotRepeatRightAngle 在一组二维坐标中从大到小搜索不重复的直角L线
func SearchNotRepeatRightAngle(minLength int, xys ...[2]int) (result [][][2]int) {
if minLength < 3 {
return nil
}
left, _, top, _ := GetShapeCoverageArea(xys...)
rectangleShape := GenerateShape(xys...)
record := map[int]map[int]bool{}
for x := 0; x < len(rectangleShape); x++ {
for y := 0; y < len(rectangleShape[0]); y++ {
record[x] = map[int]bool{}
}
}
for _, xy := range xys {
var points [][2]int
var find = map[int]bool{}
x, y := PositionArrayToXY(xy)
x = x + (0 - left)
y = y + (0 - top)
// 搜索四个方向
for sx := x - 1; sx >= 0; sx-- {
if !rectangleShape[sx][y] {
break
}
find[1] = true
points = append(points, [2]int{sx + left, y + top})
}
if find[1] {
goto up
}
for sx := x + 1; sx < len(rectangleShape); sx++ {
if !rectangleShape[sx][y] {
break
}
find[2] = true
points = append(points, [2]int{sx + left, y + top})
}
up:
for sy := y - 1; sy >= 0; sy-- {
if !rectangleShape[x][sy] {
break
}
find[3] = true
points = append(points, [2]int{x + left, sy + top})
}
if find[3] {
goto end
}
// down
for sy := y + 1; sy < len(rectangleShape[0]); sy++ {
if !rectangleShape[x][sy] {
break
}
find[4] = true
points = append(points, [2]int{x + left, sy + top})
}
if !find[4] {
continue
}
end:
{
if len(find) != 2 {
continue
}
result = append(result, append(points, [2]int{x + left, y + top}))
}
}
sort.Slice(result, func(i, j int) bool {
return len(result[i]) > len(result[j])
})
var notRepeat [][][2]int
for _, points := range result {
if len(points) < minLength {
continue
}
var match = true
for _, point := range points {
x, y := PositionArrayToXY(point)
x = x + (0 - left)
y = y + (0 - top)
if record[x][y] {
match = false
break
}
record[x][y] = true
}
if match {
notRepeat = append(notRepeat, points)
}
}
return notRepeat
}
// SearchNotRepeatFullRectangle 在一组二维坐标中从大到小搜索不重复的填充满的矩形
// - 不重复指一个位置被使用后将不会被其他矩形使用
// - 返回值表示了匹配的形状的左上角和右下角的点坐标
func SearchNotRepeatFullRectangle(minWidth, minHeight int, xys ...[2]int) (result [][2][2]int) {
left, _, top, _ := GetShapeCoverageArea(xys...)
rectangleShape := GenerateShape(xys...)
record := map[int]map[int]bool{}
width := len(rectangleShape)
height := len(rectangleShape[0])
for x := 0; x < width; x++ {
for y := 0; y < height; y++ {
record[x] = map[int]bool{}
}
}
shapes := GetExpressibleRectangleBySize(width, height, minWidth, minHeight)
for _, s := range shapes {
x, y := 0, 0
for {
if x+s[0] >= width {
x = 0
y++
}
if y+s[1] >= height {
break
}
points := GetRectangleFullPoints(s[0]+1, s[1]+1)
find := 0
for _, point := range points {
px, py := PositionArrayToXY(point)
ox, oy := px+x, py+y
if record[ox][oy] || !rectangleShape[ox][oy] {
find = 0
break
}
find++
}
if find == len(points) {
for _, point := range points {
px, py := PositionArrayToXY(point)
record[px+x][py+y] = true
}
result = append(result, [2][2]int{
{x + left, y + top}, {x + left + s[0], y + top + s[1]},
})
}
x++
}
}
return result
}
// GetRectangleFullPoints 获取一个矩形包含的所有点
func GetRectangleFullPoints(width, height int) (result [][2]int) {
for x := 0; x < width; x++ {
for y := 0; y < height; y++ {
result = append(result, [2]int{x, y})
}
}
return
}
// GetRectangleFullPointsByXY 通过开始结束坐标获取一个矩形包含的所有点
// - 例如 1,1 到 2,2 的矩形结果为 1,1 2,1 1,2 2,2
func GetRectangleFullPointsByXY(startX, startY, endX, endY int) (result [][2]int) {
for x := startX; x <= endX; x++ {
for y := startY; y <= endY; y++ {
result = append(result, [2]int{x, y})
}
}
return
}
// GetExpressibleRectangle 获取一个宽高可表达的所有矩形形状
// - 返回值表示了每一个矩形右下角的x,y位置左上角始终为0, 0
// - 矩形尺寸由大到小
func GetExpressibleRectangle(width, height int) (result [][2]int) {
return GetExpressibleRectangleBySize(width, height, 1, 1)
}
// GetExpressibleRectangleBySize 获取一个宽高可表达的所有特定尺寸以上的矩形形状
// - 返回值表示了每一个矩形右下角的x,y位置左上角始终为0, 0
// - 矩形尺寸由大到小
func GetExpressibleRectangleBySize(width, height, minWidth, minHeight int) (result [][2]int) {
if width == 0 || height == 0 {
return nil
}
if width < minWidth || height < minHeight {
return nil
}
width--
height--
for {
rightBottom := [2]int{width, height}
result = append(result, rightBottom)
if width == 0 && height == 0 || (width < minWidth && height < minHeight) {
return
}
if width == height {
width--
} else if width < height {
width++
height--
} else if width > height {
width--
}
}
}
// GenerateShape 生成一组二维坐标的形状
// - 这个形状将被在一个刚好能容纳形状的矩形中表示
// - 为true的位置表示了形状的每一个点
func GenerateShape(xys ...[2]int) [][]bool {
left, r, top, b := GetShapeCoverageArea(xys...)
_, right, _, bottom := CoverageAreaBoundless(left, r, top, b)
w, h := right+1, bottom+1
m := make([][]bool, w)
for x := 0; x < w; x++ {
m[x] = make([]bool, h)
}
for _, xy := range xys {
x, y := PositionArrayToXY(xy)
m[x-(r-right)][y-(b-bottom)] = true
}
return m
}
// CoverageAreaBoundless 将一个图形覆盖范围设置为无边的
// - 例如一个图形的left和top从2开始那么将被转换到从0开始
func CoverageAreaBoundless(l, r, t, b int) (left, right, top, bottom int) {
differentX := 0 - l
differentY := 0 - t
left = l + differentX
right = r + differentX
top = t + differentY
bottom = b + differentY
return
}
// GetShapeCoverageArea 获取一个图形覆盖的范围
func GetShapeCoverageArea(xys ...[2]int) (left, right, top, bottom int) {
left, top = -1, -1
for _, xy := range xys {
x, y := PositionArrayToXY(xy)
if x < left || left == -1 {
left = x
}
if x > right {
right = x
}
if y < top || top == -1 {
top = y
}
if y > bottom {
bottom = y
}
}
return
}