💥 几何库优化
This commit is contained in:
parent
d972261164
commit
18b8729a94
|
@ -2,7 +2,7 @@ package components
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/component"
|
"github.com/kercylan98/minotaur/component"
|
||||||
"github.com/kercylan98/minotaur/utils/g2d"
|
"github.com/kercylan98/minotaur/utils/geometry"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -117,13 +117,13 @@ func (slf *Moving2D) handle() {
|
||||||
for guid, entity := range slf.entities {
|
for guid, entity := range slf.entities {
|
||||||
entity := entity
|
entity := entity
|
||||||
x, y := entity.GetPosition()
|
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()
|
moveTime := time.Now().UnixMilli()
|
||||||
interval := float64(moveTime - entity.lastMoveTime)
|
interval := float64(moveTime - entity.lastMoveTime)
|
||||||
if interval == 0 {
|
if interval == 0 {
|
||||||
continue
|
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))
|
moveDistance := interval * (entity.GetSpeed() / (slf.timeUnit / 1000 / 1000))
|
||||||
if moveDistance >= distance || (x == entity.x && y == entity.y) {
|
if moveDistance >= distance || (x == entity.x && y == entity.y) {
|
||||||
entity.SetPosition(entity.x, entity.y)
|
entity.SetPosition(entity.x, entity.y)
|
||||||
|
@ -131,7 +131,7 @@ func (slf *Moving2D) handle() {
|
||||||
slf.OnPosition2DDestinationEvent(entity)
|
slf.OnPosition2DDestinationEvent(entity)
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
nx, ny := g2d.CalculateNewCoordinate(x, y, angle, moveDistance)
|
nx, ny := geometry.CalculateNewCoordinate(x, y, angle, moveDistance)
|
||||||
entity.SetPosition(nx, ny)
|
entity.SetPosition(nx, ny)
|
||||||
entity.lastMoveTime = moveTime
|
entity.lastMoveTime = moveTime
|
||||||
slf.OnPosition2DChangeEvent(entity, x, y)
|
slf.OnPosition2DChangeEvent(entity, x, y)
|
||||||
|
|
|
@ -2,7 +2,7 @@ package builtin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/kercylan98/minotaur/game"
|
"github.com/kercylan98/minotaur/game"
|
||||||
"github.com/kercylan98/minotaur/utils/g2d"
|
"github.com/kercylan98/minotaur/utils/geometry"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/hash"
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -177,7 +177,7 @@ func (slf *AOI2D) refresh(entity game.AOIEntity2D) {
|
||||||
focus := slf.focus[guid]
|
focus := slf.focus[guid]
|
||||||
for eg, e := range focus {
|
for eg, e := range focus {
|
||||||
ex, ey := e.GetPosition()
|
ex, ey := e.GetPosition()
|
||||||
if g2d.CalcDistance(x, y, ex, ey) > vision {
|
if geometry.CalcDistance(x, y, ex, ey) > vision {
|
||||||
delete(focus, eg)
|
delete(focus, eg)
|
||||||
delete(slf.focus[eg], guid)
|
delete(slf.focus[eg], guid)
|
||||||
}
|
}
|
||||||
|
@ -243,13 +243,13 @@ func (slf *AOI2D) rangeVisionAreaEntities(entity game.AOIEntity2D, handle func(g
|
||||||
} else {
|
} else {
|
||||||
areaY = y
|
areaY = y
|
||||||
}
|
}
|
||||||
areaDistance := g2d.CalcDistance(x, y, areaX, areaY)
|
areaDistance := geometry.CalcDistance(x, y, areaX, areaY)
|
||||||
if areaDistance <= vision {
|
if areaDistance <= vision {
|
||||||
for eg, e := range slf.areas[w][h] {
|
for eg, e := range slf.areas[w][h] {
|
||||||
if eg == guid {
|
if eg == guid {
|
||||||
continue
|
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
|
continue
|
||||||
}
|
}
|
||||||
handle(eg, e)
|
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)
|
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 复制一个点位置
|
// Copy 复制一个点位置
|
||||||
func (slf Point[V]) Copy() Point[V] {
|
func (slf Point[V]) Copy() Point[V] {
|
||||||
return CoordinateArrayCopy(slf)
|
return CoordinateArrayCopy(slf)
|
||||||
|
@ -132,3 +142,8 @@ func PosToCoordinateArrayWithMulti[V generic.Number](width V, positions ...V) []
|
||||||
}
|
}
|
||||||
return result
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRectangleFullPoints 获取一个矩形包含的所有点
|
// GetRectangleFullPoints 获取一个矩形填充满后包含的所有点
|
||||||
func GetRectangleFullPoints[V generic.Number](width, height V) (result []Point[V]) {
|
func GetRectangleFullPoints[V generic.Number](width, height V) (result []Point[V]) {
|
||||||
for x := V(0); x < width; x++ {
|
for x := V(0); x < width; x++ {
|
||||||
for y := V(0); y < height; y++ {
|
for y := V(0); y < height; y++ {
|
||||||
|
@ -292,3 +292,13 @@ func GetRectangleFullPoints[V generic.Number](width, height V) (result []Point[V
|
||||||
}
|
}
|
||||||
return
|
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
|
package geometry_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/kercylan98/minotaur/utils/geometry"
|
"github.com/kercylan98/minotaur/utils/geometry"
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -46,12 +47,14 @@ func TestCoverageAreaBoundless(t *testing.T) {
|
||||||
|
|
||||||
func TestGenerateShapeOnRectangle(t *testing.T) {
|
func TestGenerateShapeOnRectangle(t *testing.T) {
|
||||||
Convey("TestGenerateShapeOnRectangle", t, func() {
|
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(1, 1))
|
||||||
points = append(points, geometry.NewPoint(2, 1))
|
points = append(points, geometry.NewPoint(2, 1))
|
||||||
points = append(points, geometry.NewPoint(2, 2))
|
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].GetX(), ShouldEqual, 0)
|
||||||
So(ps[0].GetY(), 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)
|
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