图形搜索增强

This commit is contained in:
kercylan 2023-06-18 02:06:19 +08:00
parent 78ab5ac7fe
commit 016332b6be
5 changed files with 144 additions and 52 deletions

View File

@ -17,9 +17,25 @@ const (
)
var (
Directions = []Direction{DirectionUp, DirectionDown, DirectionLeft, DirectionRight} // 上下左右四个方向的数组
DirectionUDLR = []Direction{DirectionUp, DirectionDown, DirectionLeft, DirectionRight} // 上下左右四个方向的数组
DirectionLRUD = []Direction{DirectionLeft, DirectionRight, DirectionUp, DirectionDown} // 左右上下四个方向的数组
)
// GetOppositionDirection 获取特定方向的对立方向
func GetOppositionDirection(direction Direction) Direction {
switch direction {
case DirectionUp:
return DirectionDown
case DirectionDown:
return DirectionUp
case DirectionLeft:
return DirectionRight
case DirectionRight:
return DirectionLeft
}
return DirectionUnknown
}
// GetDirectionNextWithCoordinate 获取特定方向上的下一个坐标
func GetDirectionNextWithCoordinate[V generic.Number](direction Direction, x, y V) (nx, ny V) {
switch direction {

View File

@ -38,6 +38,11 @@ func (slf Point[V]) GetOffset(x, y V) Point[V] {
return NewPoint(slf.GetX()+x, slf.GetY()+y)
}
// Negative 返回该点是否是一个负数坐标
func (slf Point[V]) Negative() bool {
return slf.GetX() < V(0) || slf.GetY() < V(0)
}
// Equal 返回两个点是否相等
func (slf Point[V]) Equal(point Point[V]) bool {
return slf.GetX() == point.GetX() && slf.GetY() == point.GetY()

View File

@ -7,6 +7,10 @@ import (
"sort"
)
var (
ShapeStringHasBorder = false // 控制 Shape.String 是否拥有边界
)
// Shape 通过多个点表示了一个形状
type Shape[V generic.Number] []Point[V]
@ -21,22 +25,42 @@ func (slf Shape[V]) String() 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 !ShapeStringHasBorder {
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 += "# "
}
}
if exist {
result += "X "
} else {
result += "# "
}
result += "\r\n"
}
} else {
for y := V(0); y < top+height; y++ {
for x := V(0); 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"
}
result += "\r\n"
}
return result
}
@ -50,7 +74,7 @@ func (slf Shape[V]) String() string {
func (slf Shape[V]) ShapeSearch(options ...ShapeSearchOption) (result []Shape[V]) {
opt := &shapeSearchOptions{upperLimit: math.MaxInt}
opt.directionCountUpper = map[Direction]int{}
for _, d := range Directions {
for _, d := range DirectionUDLR {
opt.directionCountUpper[d] = math.MaxInt
}
@ -122,41 +146,9 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
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}
var directionCount = map[Direction]int{}
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)
directionCount[direction]++
pos := directionPoint.GetPos(areaWidth)
if _, exist := records[pos]; !exist {
result = append(result, Shape[V]{directionPoint})
records[pos] = struct{}{}
}
}
if direction == DirectionRight {
break
}
directionPoint = point
}
var match = func(links Shape[V], directionCount map[Direction]int, count int) bool {
match := true
for _, direction := range Directions {
for _, direction := range DirectionUDLR {
c := directionCount[direction]
if c < opt.directionCountLower[direction] || c > opt.directionCountUpper[direction] {
match = false
@ -164,17 +156,86 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
}
}
if match && opt.directionCount > 0 && len(directionCount) != opt.directionCount {
if opt.directionCount > 0 && len(directionCount) != opt.directionCount {
match = false
}
if match && (directionCount[DirectionUp] > 0 && directionCount[DirectionDown] > 0) || (directionCount[DirectionLeft] > 0 && directionCount[DirectionRight] > 0) {
if directionCount[GetOppositionDirection(opt.oppositionDirection)] > 0 {
match = false
}
if opt.ra {
match = false
if directionCount[DirectionLeft] > 0 && directionCount[DirectionUp] > 0 && count == directionCount[DirectionLeft]+directionCount[DirectionUp] {
match = true
} else if directionCount[DirectionUp] > 0 && directionCount[DirectionRight] > 0 && count == directionCount[DirectionUp]+directionCount[DirectionRight] {
match = true
} else if directionCount[DirectionRight] > 0 && directionCount[DirectionDown] > 0 && count == directionCount[DirectionRight]+directionCount[DirectionDown] {
match = true
} else if directionCount[DirectionDown] > 0 && directionCount[DirectionLeft] > 0 && count == directionCount[DirectionDown]+directionCount[DirectionLeft] {
match = true
}
}
if match {
result = append(result, links)
}
return match
}
// 通过每个点扩散图形
for _, point := range slf.Points() {
// 搜索四个方向
var next = -1
var directionPoint = point
var links = Shape[V]{point}
var directionCount = map[Direction]int{}
var count = 0
for i, directions := range [][]Direction{DirectionUDLR, DirectionLRUD} {
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
}
offset := directionPoint.GetOffset(-left, -top)
if offset.Negative() {
break
}
offsetPos := int(offset.GetPos(width))
if offsetPos < 0 || offsetPos >= len(rectangleShape) || !rectangleShape[offsetPos].Data {
break
}
links = append(links, directionPoint)
directionCount[direction]++
count++
match(links, directionCount, count)
pos := directionPoint.GetPos(areaWidth)
if _, exist := records[pos]; !exist {
result = append(result, Shape[V]{directionPoint})
records[pos] = struct{}{}
}
}
finish := false
switch i {
case 0:
if direction == DirectionRight {
finish = true
}
case 1:
if direction == DirectionDown {
finish = true
}
}
if finish {
break
}
directionPoint = point
}
}
return result

View File

@ -9,11 +9,19 @@ type shapeSearchOptions struct {
directionCountUpper map[Direction]int
directionCount int
oppositionDirection Direction
ra bool
}
// ShapeSearchOption 图形搜索可选项,用于 Shape.ShapeSearch 搜索支持
type ShapeSearchOption func(options *shapeSearchOptions)
// WithShapeSearchRightAngle 通过直角的方式进行搜索
func WithShapeSearchRightAngle() ShapeSearchOption {
return func(options *shapeSearchOptions) {
options.ra = true
}
}
// WithShapeSearchOppositionDirection 通过限制对立方向的方式搜索
// - 对立方向例如上不能与下共存
func WithShapeSearchOppositionDirection(direction Direction) ShapeSearchOption {

View File

@ -13,6 +13,8 @@ func TestShape_Search(t *testing.T) {
shape = append(shape, geometry.NewPoint(1, 1))
shape = append(shape, geometry.NewPoint(1, 2))
shape = append(shape, geometry.NewPoint(2, 2))
shape = append(shape, geometry.NewPoint(1, 3))
geometry.ShapeStringHasBorder = true
fmt.Println("形状:")
fmt.Println(shape)
@ -21,7 +23,7 @@ func TestShape_Search(t *testing.T) {
geometry.WithShapeSearchDesc(),
geometry.WithShapeSearchDeduplication(),
geometry.WithShapeSearchPointCountLowerLimit(3),
geometry.WithShapeSearchDirectionCount(1),
geometry.WithShapeSearchRightAngle(),
)
for _, shape := range shapes {