✨ 支持矩形搜索
This commit is contained in:
parent
016332b6be
commit
e5b092a75f
|
@ -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)
|
||||
// - 矩形尺寸由大到小
|
||||
|
|
|
@ -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
|
||||
//}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue