图形搜索增强

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 ( 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 获取特定方向上的下一个坐标 // GetDirectionNextWithCoordinate 获取特定方向上的下一个坐标
func GetDirectionNextWithCoordinate[V generic.Number](direction Direction, x, y V) (nx, ny V) { func GetDirectionNextWithCoordinate[V generic.Number](direction Direction, x, y V) (nx, ny V) {
switch direction { 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) 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 返回两个点是否相等 // Equal 返回两个点是否相等
func (slf Point[V]) Equal(point Point[V]) bool { func (slf Point[V]) Equal(point Point[V]) bool {
return slf.GetX() == point.GetX() && slf.GetY() == point.GetY() return slf.GetX() == point.GetX() && slf.GetY() == point.GetY()

View File

@ -7,6 +7,10 @@ import (
"sort" "sort"
) )
var (
ShapeStringHasBorder = false // 控制 Shape.String 是否拥有边界
)
// Shape 通过多个点表示了一个形状 // Shape 通过多个点表示了一个形状
type Shape[V generic.Number] []Point[V] type Shape[V generic.Number] []Point[V]
@ -21,22 +25,42 @@ func (slf Shape[V]) String() string {
left, right, top, bottom := GetShapeCoverageAreaWithCoordinateArray(slf.Points()...) left, right, top, bottom := GetShapeCoverageAreaWithCoordinateArray(slf.Points()...)
width := right - left + 1 width := right - left + 1
height := bottom - top + 1 height := bottom - top + 1
for y := top; y < top+height; y++ { if !ShapeStringHasBorder {
for x := left; x < left+width; x++ { for y := top; y < top+height; y++ {
exist := false for x := left; x < left+width; x++ {
for _, p := range slf { exist := false
if x == p.GetX() && y == p.GetY() { for _, p := range slf {
exist = true if x == p.GetX() && y == p.GetY() {
break exist = true
break
}
}
if exist {
result += "X "
} else {
result += "# "
} }
} }
if exist { result += "\r\n"
result += "X " }
} else { } else {
result += "# " 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 return result
} }
@ -50,7 +74,7 @@ func (slf Shape[V]) String() string {
func (slf Shape[V]) ShapeSearch(options ...ShapeSearchOption) (result []Shape[V]) { func (slf Shape[V]) ShapeSearch(options ...ShapeSearchOption) (result []Shape[V]) {
opt := &shapeSearchOptions{upperLimit: math.MaxInt} opt := &shapeSearchOptions{upperLimit: math.MaxInt}
opt.directionCountUpper = map[Direction]int{} opt.directionCountUpper = map[Direction]int{}
for _, d := range Directions { for _, d := range DirectionUDLR {
opt.directionCountUpper[d] = math.MaxInt opt.directionCountUpper[d] = math.MaxInt
} }
@ -122,41 +146,9 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
rectangleShape := GenerateShapeOnRectangle(slf.Points()...) rectangleShape := GenerateShapeOnRectangle(slf.Points()...)
records := make(map[V]struct{}) records := make(map[V]struct{})
// 通过每个点扩散图形 var match = func(links Shape[V], directionCount map[Direction]int, count int) bool {
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
}
match := true match := true
for _, direction := range Directions { for _, direction := range DirectionUDLR {
c := directionCount[direction] c := directionCount[direction]
if c < opt.directionCountLower[direction] || c > opt.directionCountUpper[direction] { if c < opt.directionCountLower[direction] || c > opt.directionCountUpper[direction] {
match = false 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 match = false
} }
if match && (directionCount[DirectionUp] > 0 && directionCount[DirectionDown] > 0) || (directionCount[DirectionLeft] > 0 && directionCount[DirectionRight] > 0) { if directionCount[GetOppositionDirection(opt.oppositionDirection)] > 0 {
match = false 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 { if match {
result = append(result, links) 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 return result

View File

@ -9,11 +9,19 @@ type shapeSearchOptions struct {
directionCountUpper map[Direction]int directionCountUpper map[Direction]int
directionCount int directionCount int
oppositionDirection Direction oppositionDirection Direction
ra bool
} }
// ShapeSearchOption 图形搜索可选项,用于 Shape.ShapeSearch 搜索支持 // ShapeSearchOption 图形搜索可选项,用于 Shape.ShapeSearch 搜索支持
type ShapeSearchOption func(options *shapeSearchOptions) type ShapeSearchOption func(options *shapeSearchOptions)
// WithShapeSearchRightAngle 通过直角的方式进行搜索
func WithShapeSearchRightAngle() ShapeSearchOption {
return func(options *shapeSearchOptions) {
options.ra = true
}
}
// WithShapeSearchOppositionDirection 通过限制对立方向的方式搜索 // WithShapeSearchOppositionDirection 通过限制对立方向的方式搜索
// - 对立方向例如上不能与下共存 // - 对立方向例如上不能与下共存
func WithShapeSearchOppositionDirection(direction Direction) ShapeSearchOption { 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, 1))
shape = append(shape, geometry.NewPoint(1, 2)) shape = append(shape, geometry.NewPoint(1, 2))
shape = append(shape, geometry.NewPoint(2, 2)) shape = append(shape, geometry.NewPoint(2, 2))
shape = append(shape, geometry.NewPoint(1, 3))
geometry.ShapeStringHasBorder = true
fmt.Println("形状:") fmt.Println("形状:")
fmt.Println(shape) fmt.Println(shape)
@ -21,7 +23,7 @@ func TestShape_Search(t *testing.T) {
geometry.WithShapeSearchDesc(), geometry.WithShapeSearchDesc(),
geometry.WithShapeSearchDeduplication(), geometry.WithShapeSearchDeduplication(),
geometry.WithShapeSearchPointCountLowerLimit(3), geometry.WithShapeSearchPointCountLowerLimit(3),
geometry.WithShapeSearchDirectionCount(1), geometry.WithShapeSearchRightAngle(),
) )
for _, shape := range shapes { for _, shape := range shapes {