💥 几何库优化
This commit is contained in:
parent
d972261164
commit
18b8729a94
|
@ -2,7 +2,7 @@ package components
|
|||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/component"
|
||||
"github.com/kercylan98/minotaur/utils/g2d"
|
||||
"github.com/kercylan98/minotaur/utils/geometry"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
@ -117,13 +117,13 @@ func (slf *Moving2D) handle() {
|
|||
for guid, entity := range slf.entities {
|
||||
entity := entity
|
||||
x, y := entity.GetPosition()
|
||||
angle := g2d.CalcAngle(x, y, entity.x, entity.y)
|
||||
angle := geometry.CalcAngle(x, y, entity.x, entity.y)
|
||||
moveTime := time.Now().UnixMilli()
|
||||
interval := float64(moveTime - entity.lastMoveTime)
|
||||
if interval == 0 {
|
||||
continue
|
||||
}
|
||||
distance := g2d.CalcDistance(x, y, entity.x, entity.y)
|
||||
distance := geometry.CalcDistance(x, y, entity.x, entity.y)
|
||||
moveDistance := interval * (entity.GetSpeed() / (slf.timeUnit / 1000 / 1000))
|
||||
if moveDistance >= distance || (x == entity.x && y == entity.y) {
|
||||
entity.SetPosition(entity.x, entity.y)
|
||||
|
@ -131,7 +131,7 @@ func (slf *Moving2D) handle() {
|
|||
slf.OnPosition2DDestinationEvent(entity)
|
||||
continue
|
||||
} else {
|
||||
nx, ny := g2d.CalculateNewCoordinate(x, y, angle, moveDistance)
|
||||
nx, ny := geometry.CalculateNewCoordinate(x, y, angle, moveDistance)
|
||||
entity.SetPosition(nx, ny)
|
||||
entity.lastMoveTime = moveTime
|
||||
slf.OnPosition2DChangeEvent(entity, x, y)
|
||||
|
|
|
@ -2,7 +2,7 @@ package builtin
|
|||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/game"
|
||||
"github.com/kercylan98/minotaur/utils/g2d"
|
||||
"github.com/kercylan98/minotaur/utils/geometry"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"math"
|
||||
"sync"
|
||||
|
@ -177,7 +177,7 @@ func (slf *AOI2D) refresh(entity game.AOIEntity2D) {
|
|||
focus := slf.focus[guid]
|
||||
for eg, e := range focus {
|
||||
ex, ey := e.GetPosition()
|
||||
if g2d.CalcDistance(x, y, ex, ey) > vision {
|
||||
if geometry.CalcDistance(x, y, ex, ey) > vision {
|
||||
delete(focus, eg)
|
||||
delete(slf.focus[eg], guid)
|
||||
}
|
||||
|
@ -243,13 +243,13 @@ func (slf *AOI2D) rangeVisionAreaEntities(entity game.AOIEntity2D, handle func(g
|
|||
} else {
|
||||
areaY = y
|
||||
}
|
||||
areaDistance := g2d.CalcDistance(x, y, areaX, areaY)
|
||||
areaDistance := geometry.CalcDistance(x, y, areaX, areaY)
|
||||
if areaDistance <= vision {
|
||||
for eg, e := range slf.areas[w][h] {
|
||||
if eg == guid {
|
||||
continue
|
||||
}
|
||||
if ex, ey := e.GetPosition(); g2d.CalcDistance(x, y, ex, ey) > vision {
|
||||
if ex, ey := e.GetPosition(); geometry.CalcDistance(x, y, ex, ey) > vision {
|
||||
continue
|
||||
}
|
||||
handle(eg, e)
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
package g2d
|
||||
|
||||
import "math"
|
||||
|
||||
// CalcDistance 计算两点之间的距离
|
||||
func CalcDistance(x1, y1, x2, y2 float64) float64 {
|
||||
return math.Sqrt(math.Pow(x2-x1, 2) + math.Pow(y2-y1, 2))
|
||||
}
|
||||
|
||||
// CalcAngle 计算点2位于点1之间的角度
|
||||
func CalcAngle(x1, y1, x2, y2 float64) float64 {
|
||||
return math.Atan2(y2-y1, x2-x1) * 180 / math.Pi
|
||||
}
|
||||
|
||||
// CalculateNewCoordinate 根据给定的x、y坐标、角度和距离计算新的坐标
|
||||
func CalculateNewCoordinate(x, y, angle, distance float64) (newX, newY float64) {
|
||||
// 将角度转换为弧度
|
||||
radians := angle * math.Pi / 180.0
|
||||
|
||||
// 计算新的坐标
|
||||
newX = x + distance*math.Cos(radians)
|
||||
newY = y + distance*math.Sin(radians)
|
||||
|
||||
return newX, newY
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package g2d
|
||||
|
||||
// Direction 方向
|
||||
type Direction uint8
|
||||
|
||||
const (
|
||||
DirectionUp = Direction(iota) // 上方
|
||||
DirectionDown // 下方
|
||||
DirectionLeft // 左方
|
||||
DirectionRight // 右方
|
||||
)
|
||||
|
||||
// CalcDirection 计算点2位于点1的方向
|
||||
func CalcDirection(x1, y1, x2, y2 float64) Direction {
|
||||
angle := CalcAngle(x1, y1, x2, y2)
|
||||
if angle > -45 && angle < 45 {
|
||||
return DirectionRight
|
||||
} else if angle > 135 && angle < -135 {
|
||||
return DirectionLeft
|
||||
} else if angle > 45 && angle < 135 {
|
||||
return DirectionUp
|
||||
} else if angle > -135 && angle < -45 {
|
||||
return DirectionDown
|
||||
}
|
||||
return 0
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
// Package g2d 提供了大量用于2D计算的辅助函数及组件,例如图形搜索、辐射关系、适用于矩阵的计算函数等
|
||||
package g2d
|
|
@ -1,821 +0,0 @@
|
|||
package g2d
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/geometry"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// 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 := geometry.CoordinateArrayToCoordinate(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 := geometry.CoordinateArrayToCoordinate(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
|
||||
}
|
||||
|
||||
// SearchContainCross 在一组二维坐标中查找是否存在交叉(十字)线
|
||||
func SearchContainCross(xys ...[2]int) bool {
|
||||
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 := geometry.CoordinateArrayToCoordinate(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
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 := geometry.CoordinateArrayToCoordinate(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 {
|
||||
var match = true
|
||||
for _, point := range points {
|
||||
x, y := geometry.CoordinateArrayToCoordinate(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
|
||||
}
|
||||
|
||||
// SearchContainStraightLine 在一组二维坐标中查找是否存在直线
|
||||
func SearchContainStraightLine(minLength int, xys ...[2]int) bool {
|
||||
if minLength < 3 {
|
||||
return false
|
||||
}
|
||||
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 := geometry.CoordinateArrayToCoordinate(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
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 := geometry.CoordinateArrayToCoordinate(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 || len(points) < minLength {
|
||||
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 := geometry.CoordinateArrayToCoordinate(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
|
||||
}
|
||||
|
||||
// SearchContainT 在一组二维坐标中查找是否存在T型(T)线
|
||||
func SearchContainT(minLength int, xys ...[2]int) bool {
|
||||
if minLength < 4 {
|
||||
return false
|
||||
}
|
||||
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 := geometry.CoordinateArrayToCoordinate(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 || len(points) < minLength-1 {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 := geometry.CoordinateArrayToCoordinate(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 || 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 {
|
||||
var match = true
|
||||
for _, point := range points {
|
||||
x, y := geometry.CoordinateArrayToCoordinate(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
|
||||
}
|
||||
|
||||
// SearchContainRightAngle 在一组二维坐标中查找是否存在直角(L)线
|
||||
func SearchContainRightAngle(minLength int, xys ...[2]int) bool {
|
||||
if minLength < 3 {
|
||||
return false
|
||||
}
|
||||
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 := geometry.CoordinateArrayToCoordinate(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 || len(points) < minLength-1 {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 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 := geometry.CoordinateArrayToCoordinate(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 := geometry.CoordinateArrayToCoordinate(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
|
||||
}
|
||||
|
||||
// SearchContainFullRectangle 在一组二维坐标中查找是否存在填充满的矩形
|
||||
func SearchContainFullRectangle(minWidth, minHeight int, xys ...[2]int) bool {
|
||||
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 := geometry.CoordinateArrayToCoordinate(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 := geometry.CoordinateArrayToCoordinate(point)
|
||||
record[px+x][py+y] = true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
x++
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// 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) {
|
||||
sourceWidth := width
|
||||
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 {
|
||||
if width+1 == sourceWidth {
|
||||
height--
|
||||
} else {
|
||||
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 := geometry.CoordinateArrayToCoordinate(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
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package geometry
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Direction 方向
|
||||
type Direction uint8
|
||||
|
||||
const (
|
||||
DirectionUnknown = Direction(iota) // 未知
|
||||
DirectionUp // 上方
|
||||
DirectionDown // 下方
|
||||
DirectionLeft // 左方
|
||||
DirectionRight // 右方
|
||||
)
|
||||
|
||||
var (
|
||||
Directions = []Direction{DirectionUp, DirectionDown, DirectionLeft, DirectionRight} // 上下左右四个方向的数组
|
||||
)
|
||||
|
||||
// GetDirectionNextWithCoordinate 获取特定方向上的下一个坐标
|
||||
func GetDirectionNextWithCoordinate[V generic.Number](direction Direction, x, y V) (nx, ny V) {
|
||||
switch direction {
|
||||
case DirectionUp:
|
||||
nx, ny = x, y-1
|
||||
case DirectionDown:
|
||||
nx, ny = x, y+1
|
||||
case DirectionLeft:
|
||||
nx, ny = x-1, y
|
||||
case DirectionRight:
|
||||
nx, ny = x+1, y
|
||||
default:
|
||||
panic("unexplained direction")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetDirectionNextWithCoordinateArray 获取特定方向上的下一个坐标
|
||||
func GetDirectionNextWithCoordinateArray[V generic.Number](direction Direction, point Point[V]) Point[V] {
|
||||
x, y := point.GetXY()
|
||||
switch direction {
|
||||
case DirectionUp:
|
||||
return NewPoint(x, y-1)
|
||||
case DirectionDown:
|
||||
return NewPoint(x, y+1)
|
||||
case DirectionLeft:
|
||||
return NewPoint(x-1, y)
|
||||
case DirectionRight:
|
||||
return NewPoint(x+1, y)
|
||||
default:
|
||||
panic("unexplained direction")
|
||||
}
|
||||
}
|
||||
|
||||
// GetDirectionNextWithPos 获取位置在特定宽度和特定方向上的下一个位置
|
||||
// - 需要注意的是,在左右方向时,当下一个位置不在游戏区域内时,将会返回上一行的末位置或下一行的首位置
|
||||
func GetDirectionNextWithPos[V generic.Number](direction Direction, width, pos V) V {
|
||||
switch direction {
|
||||
case DirectionUp:
|
||||
return pos - width
|
||||
case DirectionDown:
|
||||
return pos + width
|
||||
case DirectionLeft:
|
||||
return pos - 1
|
||||
case DirectionRight:
|
||||
return pos + 1
|
||||
default:
|
||||
panic("unexplained direction")
|
||||
}
|
||||
}
|
||||
|
||||
// CalcDirection 计算点2位于点1的方向
|
||||
func CalcDirection[V generic.Number](x1, y1, x2, y2 V) Direction {
|
||||
var oneEighty = 180
|
||||
var fortyFive = 45
|
||||
var oneThirtyFive = 135
|
||||
var twoTwentyFive = 225
|
||||
var threeFifteen = 315
|
||||
var end = 360
|
||||
var start = 0
|
||||
angle := CalcAngle(x1, y1, x2, y2) + V(oneEighty)
|
||||
if angle > V(oneThirtyFive) && angle <= V(twoTwentyFive) {
|
||||
return DirectionRight
|
||||
} else if (angle > V(threeFifteen) && angle <= V(end)) || (angle >= V(start) && angle <= V(fortyFive)) {
|
||||
return DirectionLeft
|
||||
} else if angle > V(twoTwentyFive) && angle <= V(threeFifteen) {
|
||||
return DirectionUp
|
||||
} else if angle > V(fortyFive) && angle <= V(oneThirtyFive) {
|
||||
return DirectionDown
|
||||
}
|
||||
return DirectionUnknown
|
||||
}
|
||||
|
||||
// CalcDistance 计算两点之间的距离
|
||||
func CalcDistance[V generic.Number](x1, y1, x2, y2 V) V {
|
||||
return V(math.Sqrt(math.Pow(float64(x2-x1), 2) + math.Pow(float64(y2-y1), 2)))
|
||||
}
|
||||
|
||||
// CalcAngle 计算点2位于点1之间的角度
|
||||
func CalcAngle[V generic.Number](x1, y1, x2, y2 V) V {
|
||||
return V(math.Atan2(float64(y2-y1), float64(x2-x1)) * 180 / math.Pi)
|
||||
}
|
||||
|
||||
// CalculateNewCoordinate 根据给定的x、y坐标、角度和距离计算新的坐标
|
||||
func CalculateNewCoordinate[V generic.Number](x, y, angle, distance V) (newX, newY V) {
|
||||
// 将角度转换为弧度
|
||||
var pi = math.Pi
|
||||
var dividend = 180.0
|
||||
radians := angle * V(pi) / V(dividend)
|
||||
|
||||
// 计算新的坐标
|
||||
newX = x + distance*V(math.Cos(float64(radians)))
|
||||
newY = y + distance*V(math.Sin(float64(radians)))
|
||||
|
||||
return newX, newY
|
||||
}
|
|
@ -33,6 +33,16 @@ func (slf Point[V]) GetPos(width V) V {
|
|||
return CoordinateArrayToPos(width, slf)
|
||||
}
|
||||
|
||||
// GetOffset 获取偏移后的新坐标
|
||||
func (slf Point[V]) GetOffset(x, y V) Point[V] {
|
||||
return NewPoint(slf.GetX()+x, slf.GetY()+y)
|
||||
}
|
||||
|
||||
// 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 CoordinateArrayCopy(slf)
|
||||
|
@ -132,3 +142,8 @@ func PosToCoordinateArrayWithMulti[V generic.Number](width V, positions ...V) []
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// PosSameRow 返回两个顺序位置在同一宽度是否位于同一行
|
||||
func PosSameRow[V generic.Number](width, pos1, pos2 V) bool {
|
||||
return (pos1 / width) == (pos2 / width)
|
||||
}
|
||||
|
|
|
@ -283,7 +283,7 @@ func GetRectangleFullPointsByXY[V generic.Number](startX, startY, endX, endY V)
|
|||
return
|
||||
}
|
||||
|
||||
// GetRectangleFullPoints 获取一个矩形包含的所有点
|
||||
// GetRectangleFullPoints 获取一个矩形填充满后包含的所有点
|
||||
func GetRectangleFullPoints[V generic.Number](width, height V) (result []Point[V]) {
|
||||
for x := V(0); x < width; x++ {
|
||||
for y := V(0); y < height; y++ {
|
||||
|
@ -292,3 +292,13 @@ func GetRectangleFullPoints[V generic.Number](width, height V) (result []Point[V
|
|||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetRectangleFullPos 获取一个矩形填充满后包含的所有位置
|
||||
func GetRectangleFullPos[V generic.Number](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
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package geometry_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/geometry"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
"testing"
|
||||
|
@ -46,12 +47,14 @@ func TestCoverageAreaBoundless(t *testing.T) {
|
|||
|
||||
func TestGenerateShapeOnRectangle(t *testing.T) {
|
||||
Convey("TestGenerateShapeOnRectangle", t, func() {
|
||||
var points []geometry.Point[int]
|
||||
var points geometry.Shape[int]
|
||||
points = append(points, geometry.NewPoint(1, 1))
|
||||
points = append(points, geometry.NewPoint(2, 1))
|
||||
points = append(points, geometry.NewPoint(2, 2))
|
||||
|
||||
ps := geometry.GenerateShapeOnRectangle(points...)
|
||||
fmt.Println(points)
|
||||
|
||||
ps := geometry.GenerateShapeOnRectangle(points.Points()...)
|
||||
|
||||
So(ps[0].GetX(), ShouldEqual, 0)
|
||||
So(ps[0].GetY(), ShouldEqual, 0)
|
||||
|
|
|
@ -0,0 +1,970 @@
|
|||
package geometry
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Shape 通过多个点表示了一个形状
|
||||
type Shape[V generic.Number] []Point[V]
|
||||
|
||||
// Points 获取这个形状的所有点
|
||||
func (slf Shape[V]) Points() []Point[V] {
|
||||
return slf
|
||||
}
|
||||
|
||||
// String 将该形状转换为可视化的字符串进行返回
|
||||
func (slf Shape[V]) String() string {
|
||||
var result string
|
||||
left, right, top, bottom := GetShapeCoverageAreaWithCoordinateArray(slf.Points()...)
|
||||
width := right - left + 1
|
||||
height := bottom - top + 1
|
||||
for y := top; y < top+height; y++ {
|
||||
for x := left; x < left+width; x++ {
|
||||
exist := false
|
||||
for _, p := range slf {
|
||||
if x == p.GetX() && y == p.GetY() {
|
||||
exist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if exist {
|
||||
result += "X "
|
||||
} else {
|
||||
result += "# "
|
||||
}
|
||||
}
|
||||
result += "\r\n"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ShapeSearch 获取该形状中包含的所有图形组合及其位置
|
||||
// - 需要注意的是,即便图形最终表示为相同的,但是只要位置组合顺序不同,那么也将被认定为一种图形组合
|
||||
// - [[1 0] [1 1] [1 2]] 和 [[1 1] [1 0] [1 2]] 可以被视为两个图形组合
|
||||
// - 返回的坐标为原始形状的坐标
|
||||
//
|
||||
// 可通过可选项对搜索结果进行过滤
|
||||
func (slf Shape[V]) ShapeSearch(options ...ShapeSearchOption) (result []Shape[V]) {
|
||||
opt := &shapeSearchOptions{upperLimit: math.MaxInt}
|
||||
for _, option := range options {
|
||||
option(opt)
|
||||
}
|
||||
|
||||
var shapes []Shape[V]
|
||||
switch opt.sort {
|
||||
case 1:
|
||||
shapes = slf.getAllGraphicCompositionWithAsc(opt)
|
||||
case -1:
|
||||
shapes = slf.getAllGraphicCompositionWithDesc(opt)
|
||||
default:
|
||||
shapes = slf.getAllGraphicComposition(opt)
|
||||
}
|
||||
result = shapes
|
||||
|
||||
if opt.deduplication {
|
||||
deduplication := make(map[V]struct{})
|
||||
w := V(len(slf.Points()))
|
||||
|
||||
var notRepeat = make([]Shape[V], 0, len(result))
|
||||
for _, points := range result {
|
||||
count := len(points)
|
||||
if count < opt.lowerLimit || count > opt.upperLimit {
|
||||
continue
|
||||
}
|
||||
var match = true
|
||||
for _, point := range points {
|
||||
pos := point.GetPos(w)
|
||||
if _, exist := deduplication[pos]; exist {
|
||||
match = false
|
||||
break
|
||||
}
|
||||
deduplication[pos] = struct{}{}
|
||||
}
|
||||
if match {
|
||||
notRepeat = append(notRepeat, points)
|
||||
}
|
||||
}
|
||||
|
||||
result = notRepeat
|
||||
} else {
|
||||
limit := make([]Shape[V], 0, len(result))
|
||||
for _, shape := range result {
|
||||
count := len(shape.Points())
|
||||
if count < opt.lowerLimit || count > opt.upperLimit {
|
||||
continue
|
||||
}
|
||||
limit = append(limit, shape)
|
||||
}
|
||||
result = limit
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// getAllGraphicComposition 获取该形状中包含的所有图形组合及其位置
|
||||
// - 需要注意的是,即便图形最终表示为相同的,但是只要位置组合顺序不同,那么也将被认定为一种图形组合
|
||||
// - [[1 0] [1 1] [1 2]] 和 [[1 1] [1 0] [1 2]] 可以被视为两个图形组合
|
||||
// - 返回的坐标为原始形状的坐标
|
||||
func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []Shape[V]) {
|
||||
left, right, top, bottom := GetShapeCoverageAreaWithCoordinateArray(slf.Points()...)
|
||||
width := right - left + 1
|
||||
height := bottom - top + 1
|
||||
areaWidth := width + left
|
||||
areaHeight := height + top
|
||||
rectangleShape := GenerateShapeOnRectangle(slf.Points()...)
|
||||
records := make(map[V]struct{})
|
||||
|
||||
// 通过每个点扩散图形
|
||||
for _, point := range slf.Points() {
|
||||
// 搜索四个方向
|
||||
var next = -1
|
||||
var directionPoint = point
|
||||
var links = Shape[V]{point}
|
||||
for {
|
||||
var direction Direction
|
||||
next, direction = slice.NextLoop(Directions, next)
|
||||
for {
|
||||
directionPoint = GetDirectionNextWithCoordinateArray(direction, directionPoint)
|
||||
if px, py := directionPoint.GetXY(); px < 0 || px >= areaWidth || py < 0 || py >= areaHeight {
|
||||
break
|
||||
}
|
||||
if offsetPos := int(CoordinateArrayToPos(width, directionPoint.GetOffset(-left, -top))); offsetPos < 0 || offsetPos >= len(rectangleShape) || !rectangleShape[offsetPos].Data {
|
||||
break
|
||||
}
|
||||
links = append(links, directionPoint)
|
||||
pos := directionPoint.GetPos(areaWidth)
|
||||
if _, exist := records[pos]; !exist {
|
||||
result = append(result, Shape[V]{directionPoint})
|
||||
records[pos] = struct{}{}
|
||||
}
|
||||
}
|
||||
if direction == DirectionRight {
|
||||
break
|
||||
}
|
||||
directionPoint = point
|
||||
}
|
||||
result = append(result, links)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// getAllGraphicCompositionWithAsc 通过升序的方式获取该形状中包含的所有图形组合及其位置
|
||||
// - 升序指标为图形包含的点数量
|
||||
// - 其余内容可参考 getAllGraphicComposition
|
||||
func (slf Shape[V]) getAllGraphicCompositionWithAsc(opt *shapeSearchOptions) (result []Shape[V]) {
|
||||
result = slf.getAllGraphicComposition(opt)
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return len(result[i].Points()) < len(result[j].Points())
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// getAllGraphicCompositionWithDesc 通过降序的方式获取该形状中包含的所有图形组合及其位置
|
||||
// - 降序指标为图形包含的点数量
|
||||
// - 其余内容可参考 GetAllGraphicComposition
|
||||
func (slf Shape[V]) getAllGraphicCompositionWithDesc(opt *shapeSearchOptions) (result []Shape[V]) {
|
||||
result = slf.getAllGraphicComposition(opt)
|
||||
sort.Slice(result, func(i, j int) bool {
|
||||
return len(result[i].Points()) > len(result[j].Points())
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
//
|
||||
//// SearchNotRepeatCross 在一组二维坐标中从大到小搜索不重复交叉(十字)线
|
||||
//// - 不重复指一个位置被使用后将不会被其他交叉线(十字)使用
|
||||
//func SearchNotRepeatCross[V generic.Number](findHandle func(findCount map[Direction]int, nextDirection func(direction Direction), stop func()), points []Point[V]) (result [][]Point[V]) {
|
||||
// left, right, top, bottom := GetShapeCoverageAreaWithCoordinateArray(points...)
|
||||
// width := right - left + 1
|
||||
// height := bottom - top + 1
|
||||
// size := width * height
|
||||
// rectangleShape := GenerateShapeOnRectangle(points...)
|
||||
// record := map[V]map[V]bool{}
|
||||
// for x := V(0); x < width; x++ {
|
||||
// for y := V(0); y < height; y++ {
|
||||
// record[x] = map[V]bool{}
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// var findCount = map[Direction]int{}
|
||||
//
|
||||
// for _, point := range points {
|
||||
//
|
||||
// var next = -1
|
||||
// for {
|
||||
// var direction Direction
|
||||
// next, direction = slice.NextLoop(Directions, next)
|
||||
// nextPoint := point
|
||||
// for {
|
||||
// nextPoint = GetDirectionNextWithCoordinateArray(direction, point)
|
||||
// nextPos := nextPoint.GetPos(width)
|
||||
// if nextPos < 0 || nextPos >= size {
|
||||
// break
|
||||
// }
|
||||
// if rectangleShape[int(nextPos)].Data {
|
||||
// findCount[direction]++
|
||||
// var goToNextDirection bool
|
||||
// var stop bool
|
||||
// findHandle(findCount, func(direction Direction) {
|
||||
// switch direction {
|
||||
// case DirectionUp:
|
||||
// next = -1
|
||||
// case DirectionDown:
|
||||
// next = 0
|
||||
// case DirectionLeft:
|
||||
// next = 1
|
||||
// case DirectionRight:
|
||||
// next = 2
|
||||
// }
|
||||
// goToNextDirection = true
|
||||
// }, func() {
|
||||
// stop = true
|
||||
// })
|
||||
// if stop {
|
||||
// return
|
||||
// }
|
||||
// if goToNextDirection {
|
||||
// break
|
||||
// }
|
||||
// } else {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for _, direction := range Directions {
|
||||
// for {
|
||||
// nextPoint := GetDirectionNextWithCoordinateArray(direction, point)
|
||||
// nextPos := nextPoint.GetPos(width)
|
||||
// if nextPos < 0 || nextPos >= size {
|
||||
// break
|
||||
// }
|
||||
// if rectangleShape[int(nextPos)].Data {
|
||||
// findCount[direction]++
|
||||
// } else {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 十字至少各边需要长度1
|
||||
// totalCount := hash.Sum(findCount)
|
||||
// if totalCount < 4 {
|
||||
// continue
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// for _, xy := range xys {
|
||||
// var points []Point[V]
|
||||
// var find = map[int]bool{}
|
||||
// x, y := xy.GetXY()
|
||||
// x = x + (0 - left)
|
||||
// y = y + (0 - top)
|
||||
// // 搜索四个方向
|
||||
// for sx := x - 1; sx >= 0; sx-- {
|
||||
// if !rectangleShape[int(CoordinateToPos(width, sx, y))].Data {
|
||||
// break
|
||||
// }
|
||||
// find[1] = true
|
||||
// points = append(points, NewPoint(sx+left, y+top))
|
||||
// }
|
||||
// if !find[1] {
|
||||
// continue
|
||||
// }
|
||||
// for sx := x + 1; sx < V(len(rectangleShape)); sx++ {
|
||||
// if !rectangleShape[int(CoordinateToPos(width, sx, y))].Data {
|
||||
// break
|
||||
// }
|
||||
// find[2] = true
|
||||
// points = append(points, NewPoint(sx+left, y+top))
|
||||
// }
|
||||
// if !find[2] {
|
||||
// continue
|
||||
// }
|
||||
// for sy := y - 1; sy >= 0; sy-- {
|
||||
// if !rectangleShape[int(CoordinateToPos(width, x, sy))].Data {
|
||||
// break
|
||||
// }
|
||||
// find[3] = true
|
||||
// points = append(points, NewPoint(x+left, sy+top))
|
||||
// }
|
||||
// if !find[3] {
|
||||
// continue
|
||||
// }
|
||||
// for sy := y + 1; sy < V(len(rectangleShape)); sy++ {
|
||||
// if !rectangleShape[int(CoordinateToPos(width, x, sy))].Data {
|
||||
// break
|
||||
// }
|
||||
// find[4] = true
|
||||
// points = append(points, NewPoint(x+left, sy+top))
|
||||
// }
|
||||
// if !find[4] {
|
||||
// continue
|
||||
// }
|
||||
// result = append(result, append(points, NewPoint(x+left, y+top)))
|
||||
// }
|
||||
//
|
||||
// sort.Slice(result, func(i, j int) bool {
|
||||
// return len(result[i]) > len(result[j])
|
||||
// })
|
||||
//
|
||||
// var notRepeat [][]Point[V]
|
||||
// for _, points := range result {
|
||||
// var match = true
|
||||
// for _, point := range points {
|
||||
// x, y := CoordinateArrayToCoordinate(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
|
||||
//}
|
||||
//
|
||||
//// SearchContainCross 在一组二维坐标中查找是否存在交叉(十字)线
|
||||
//func SearchContainCross(xys ...[2]int) bool {
|
||||
// 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 := CoordinateArrayToCoordinate(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
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// return false
|
||||
//}
|
||||
//
|
||||
//// 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 := CoordinateArrayToCoordinate(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 {
|
||||
// var match = true
|
||||
// for _, point := range points {
|
||||
// x, y := CoordinateArrayToCoordinate(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
|
||||
//}
|
||||
//
|
||||
//// SearchContainStraightLine 在一组二维坐标中查找是否存在直线
|
||||
//func SearchContainStraightLine(minLength int, xys ...[2]int) bool {
|
||||
// if minLength < 3 {
|
||||
// return false
|
||||
// }
|
||||
// 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 := CoordinateArrayToCoordinate(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
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return false
|
||||
//}
|
||||
//
|
||||
//// 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 := CoordinateArrayToCoordinate(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 || len(points) < minLength {
|
||||
// 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 := CoordinateArrayToCoordinate(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
|
||||
//}
|
||||
//
|
||||
//// SearchContainT 在一组二维坐标中查找是否存在T型(T)线
|
||||
//func SearchContainT(minLength int, xys ...[2]int) bool {
|
||||
// if minLength < 4 {
|
||||
// return false
|
||||
// }
|
||||
// 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 := CoordinateArrayToCoordinate(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 || len(points) < minLength-1 {
|
||||
// continue
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// return false
|
||||
//}
|
||||
//
|
||||
//// 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 := CoordinateArrayToCoordinate(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 || 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 {
|
||||
// var match = true
|
||||
// for _, point := range points {
|
||||
// x, y := CoordinateArrayToCoordinate(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
|
||||
//}
|
||||
//
|
||||
//// SearchContainRightAngle 在一组二维坐标中查找是否存在直角(L)线
|
||||
//func SearchContainRightAngle(minLength int, xys ...[2]int) bool {
|
||||
// if minLength < 3 {
|
||||
// return false
|
||||
// }
|
||||
// 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 := CoordinateArrayToCoordinate(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 || len(points) < minLength-1 {
|
||||
// continue
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return false
|
||||
//}
|
||||
//
|
||||
//// 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 := CoordinateArrayToCoordinate(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 := CoordinateArrayToCoordinate(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
|
||||
//}
|
||||
//
|
||||
//// SearchContainFullRectangle 在一组二维坐标中查找是否存在填充满的矩形
|
||||
//func SearchContainFullRectangle(minWidth, minHeight int, xys ...[2]int) bool {
|
||||
// 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 := CoordinateArrayToCoordinate(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 := CoordinateArrayToCoordinate(point)
|
||||
// record[px+x][py+y] = true
|
||||
// }
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// x++
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return false
|
||||
//}
|
|
@ -0,0 +1,49 @@
|
|||
package geometry
|
||||
|
||||
type shapeSearchOptions struct {
|
||||
lowerLimit int
|
||||
upperLimit int
|
||||
sort int
|
||||
deduplication bool
|
||||
}
|
||||
|
||||
// ShapeSearchOption 图形搜索可选项,用于 Shape.ShapeSearch 搜索支持
|
||||
type ShapeSearchOption func(options *shapeSearchOptions)
|
||||
|
||||
// WithShapeSearchDeduplication 通过去重的方式进行搜索
|
||||
// - 去重方式中每个点仅会被使用一次
|
||||
func WithShapeSearchDeduplication() ShapeSearchOption {
|
||||
return func(options *shapeSearchOptions) {
|
||||
options.deduplication = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithShapeSearchPointCountLowerLimit 通过限制图形构成的最小点数进行搜索
|
||||
// - 当搜索到的图形的点数量低于 lowerLimit 时,将被忽略
|
||||
func WithShapeSearchPointCountLowerLimit(lowerLimit int) ShapeSearchOption {
|
||||
return func(options *shapeSearchOptions) {
|
||||
options.lowerLimit = lowerLimit
|
||||
}
|
||||
}
|
||||
|
||||
// WithShapeSearchPointCountUpperLimit 通过限制图形构成的最大点数进行搜索
|
||||
// - 当搜索到的图形的点数量大于 upperLimit 时,将被忽略
|
||||
func WithShapeSearchPointCountUpperLimit(upperLimit int) ShapeSearchOption {
|
||||
return func(options *shapeSearchOptions) {
|
||||
options.upperLimit = upperLimit
|
||||
}
|
||||
}
|
||||
|
||||
// WithShapeSearchAsc 通过升序的方式进行搜索
|
||||
func WithShapeSearchAsc() ShapeSearchOption {
|
||||
return func(options *shapeSearchOptions) {
|
||||
options.sort = 1
|
||||
}
|
||||
}
|
||||
|
||||
// WithShapeSearchDesc 通过降序的方式进行搜索
|
||||
func WithShapeSearchDesc() ShapeSearchOption {
|
||||
return func(options *shapeSearchOptions) {
|
||||
options.sort = -1
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package geometry_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kercylan98/minotaur/utils/geometry"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestShape_Search(t *testing.T) {
|
||||
var shape geometry.Shape[int]
|
||||
// 生成一个L形的shape
|
||||
shape = append(shape, geometry.NewPoint(1, 0))
|
||||
shape = append(shape, geometry.NewPoint(1, 1))
|
||||
shape = append(shape, geometry.NewPoint(1, 2))
|
||||
shape = append(shape, geometry.NewPoint(2, 2))
|
||||
|
||||
fmt.Println(shape)
|
||||
|
||||
shapes := shape.ShapeSearch(geometry.WithShapeSearchAsc(), geometry.WithShapeSearchDeduplication())
|
||||
|
||||
for _, shape := range shapes {
|
||||
fmt.Println("图形", shape.Points())
|
||||
fmt.Println(shape)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package hash
|
||||
|
||||
import "github.com/kercylan98/minotaur/utils/generic"
|
||||
|
||||
// Sum 计算一个 map 中的 value 总和
|
||||
func Sum[K comparable, V generic.Number](m map[K]V) V {
|
||||
var sum V
|
||||
for _, v := range m {
|
||||
sum += v
|
||||
}
|
||||
return sum
|
||||
}
|
|
@ -54,3 +54,16 @@ func Move[V any](slice *[]V, index, to int) {
|
|||
Del[V](slice, index)
|
||||
}
|
||||
}
|
||||
|
||||
// NextLoop 返回 i 的下一个数组成员,当 i 达到数组长度时从 0 开始
|
||||
// - 当 i 为 -1 时将返回第一个元素
|
||||
func NextLoop[V any](slice []V, i int) (next int, value V) {
|
||||
if i == -1 {
|
||||
return 0, slice[0]
|
||||
}
|
||||
next = i + 1
|
||||
if next == len(slice) {
|
||||
next = 0
|
||||
}
|
||||
return next, slice[next]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue