支持矩形搜索

This commit is contained in:
kercylan 2023-06-18 13:41:45 +08:00
parent 016332b6be
commit e5b092a75f
4 changed files with 133 additions and 152 deletions

View File

@ -231,6 +231,26 @@ func GenerateShapeOnRectangle[V generic.Number](xys ...Point[V]) (result []Point
return
}
// GenerateShapeOnRectangleWithCoordinate 生成一组二维坐标的形状
// - 这个形状将被在一个刚好能容纳形状的矩形中表示
// - 为 true 的位置表示了形状的每一个点
func GenerateShapeOnRectangleWithCoordinate[V generic.Number](xys ...Point[V]) (result [][]bool) {
left, r, top, b := GetShapeCoverageAreaWithCoordinateArray(xys...)
_, right, _, bottom := CoverageAreaBoundless(left, r, top, b)
w, h := right+1, bottom+1
result = make([][]bool, int(w))
for x := V(0); x < w; x++ {
result[int(x)] = make([]bool, int(h))
}
for _, xy := range xys {
x, y := xy.GetXY()
sx := x - (r - right)
sy := y - (b - bottom)
result[int(sx)][int(sy)] = true
}
return
}
// GetExpressibleRectangleBySize 获取一个宽高可表达的所有特定尺寸以上的矩形形状
// - 返回值表示了每一个矩形右下角的x,y位置左上角始终为0, 0
// - 矩形尺寸由大到小

View File

@ -72,7 +72,7 @@ func (slf Shape[V]) String() string {
//
// 可通过可选项对搜索结果进行过滤
func (slf Shape[V]) ShapeSearch(options ...ShapeSearchOption) (result []Shape[V]) {
opt := &shapeSearchOptions{upperLimit: math.MaxInt}
opt := &shapeSearchOptions{upperLimit: math.MaxInt, rectangleMaxWidth: math.MaxInt, rectangleMaxHeight: math.MaxInt}
opt.directionCountUpper = map[Direction]int{}
for _, d := range DirectionUDLR {
opt.directionCountUpper[d] = math.MaxInt
@ -147,6 +147,9 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
records := make(map[V]struct{})
var match = func(links Shape[V], directionCount map[Direction]int, count int) bool {
if opt.rectangle {
return false
}
match := true
for _, direction := range DirectionUDLR {
c := directionCount[direction]
@ -183,59 +186,99 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
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)
if opt.rectangle {
l, r, t, b := GetShapeCoverageAreaWithCoordinateArray(slf.Points()...)
rs := GenerateShapeOnRectangleWithCoordinate(slf.Points()...)
w := r - l + 1
h := b - t + 1
shapes := GetExpressibleRectangleBySize(w, h, V(opt.rectangleMinWidth), V(opt.rectangleMinHeight))
for _, s := range shapes {
x, y := 0, 0
for {
directionPoint = GetDirectionNextWithCoordinateArray(direction, directionPoint)
if px, py := directionPoint.GetXY(); px < 0 || px >= areaWidth || py < 0 || py >= areaHeight {
if V(x)+s.GetX() >= w {
x = 0
y++
}
if V(y)+s.GetY() >= h {
break
}
offset := directionPoint.GetOffset(-left, -top)
if offset.Negative() {
break
points := GetRectangleFullPoints(s[0]+1, s[1]+1)
find := 0
for _, point := range points {
px, py := CoordinateArrayToCoordinate(point)
ox, oy := px+V(x), py+V(y)
if !rs[int(ox)][int(oy)] {
find = 0
break
}
find++
}
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{}{}
if find == len(points) {
sw := s.GetX() + 1
sh := s.GetY() + 1
if !(sw < V(opt.rectangleMinWidth) || sw > V(opt.rectangleMaxWidth) || sh < V(opt.rectangleMinHeight) || sh > V(opt.rectangleMaxHeight)) {
result = append(result, points)
}
}
x++
}
finish := false
switch i {
case 0:
if direction == DirectionRight {
finish = true
}
case 1:
if direction == DirectionDown {
finish = true
}
}
if finish {
break
}
directionPoint = point
}
} else {
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
@ -262,107 +305,3 @@ func (slf Shape[V]) getAllGraphicCompositionWithDesc(opt *shapeSearchOptions) (r
})
return
}
//
//// 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
//}

View File

@ -10,11 +10,34 @@ type shapeSearchOptions struct {
directionCount int
oppositionDirection Direction
ra bool
rectangle bool
rectangleMinWidth int
rectangleMinHeight int
rectangleMaxWidth int
rectangleMaxHeight int
}
// ShapeSearchOption 图形搜索可选项,用于 Shape.ShapeSearch 搜索支持
type ShapeSearchOption func(options *shapeSearchOptions)
// WithShapeSearchRectangleLowerLimit 通过矩形宽高下限的方式搜索
func WithShapeSearchRectangleLowerLimit(minWidth, minHeight int) ShapeSearchOption {
return func(options *shapeSearchOptions) {
options.rectangleMinWidth = minWidth
options.rectangleMinHeight = minHeight
options.rectangle = true
}
}
// WithShapeSearchRectangleUpperLimit 通过矩形宽高上限的方式搜索
func WithShapeSearchRectangleUpperLimit(maxWidth, maxHeight int) ShapeSearchOption {
return func(options *shapeSearchOptions) {
options.rectangleMaxWidth = maxWidth
options.rectangleMaxHeight = maxHeight
options.rectangle = true
}
}
// WithShapeSearchRightAngle 通过直角的方式进行搜索
func WithShapeSearchRightAngle() ShapeSearchOption {
return func(options *shapeSearchOptions) {

View File

@ -12,6 +12,7 @@ func TestShape_Search(t *testing.T) {
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, 1))
shape = append(shape, geometry.NewPoint(2, 2))
shape = append(shape, geometry.NewPoint(1, 3))
geometry.ShapeStringHasBorder = true
@ -21,9 +22,7 @@ func TestShape_Search(t *testing.T) {
shapes := shape.ShapeSearch(
geometry.WithShapeSearchDesc(),
geometry.WithShapeSearchDeduplication(),
geometry.WithShapeSearchPointCountLowerLimit(3),
geometry.WithShapeSearchRightAngle(),
geometry.WithShapeSearchRectangleLowerLimit(2, 2),
)
for _, shape := range shapes {