✨ 支持矩形搜索
This commit is contained in:
parent
016332b6be
commit
e5b092a75f
|
@ -231,6 +231,26 @@ func GenerateShapeOnRectangle[V generic.Number](xys ...Point[V]) (result []Point
|
||||||
return
|
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 获取一个宽高可表达的所有特定尺寸以上的矩形形状
|
// GetExpressibleRectangleBySize 获取一个宽高可表达的所有特定尺寸以上的矩形形状
|
||||||
// - 返回值表示了每一个矩形右下角的x,y位置(左上角始终为0, 0)
|
// - 返回值表示了每一个矩形右下角的x,y位置(左上角始终为0, 0)
|
||||||
// - 矩形尺寸由大到小
|
// - 矩形尺寸由大到小
|
||||||
|
|
|
@ -72,7 +72,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, rectangleMaxWidth: math.MaxInt, rectangleMaxHeight: math.MaxInt}
|
||||||
opt.directionCountUpper = map[Direction]int{}
|
opt.directionCountUpper = map[Direction]int{}
|
||||||
for _, d := range DirectionUDLR {
|
for _, d := range DirectionUDLR {
|
||||||
opt.directionCountUpper[d] = math.MaxInt
|
opt.directionCountUpper[d] = math.MaxInt
|
||||||
|
@ -147,6 +147,9 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
|
||||||
records := make(map[V]struct{})
|
records := make(map[V]struct{})
|
||||||
|
|
||||||
var match = func(links Shape[V], directionCount map[Direction]int, count int) bool {
|
var match = func(links Shape[V], directionCount map[Direction]int, count int) bool {
|
||||||
|
if opt.rectangle {
|
||||||
|
return false
|
||||||
|
}
|
||||||
match := true
|
match := true
|
||||||
for _, direction := range DirectionUDLR {
|
for _, direction := range DirectionUDLR {
|
||||||
c := directionCount[direction]
|
c := directionCount[direction]
|
||||||
|
@ -183,59 +186,99 @@ func (slf Shape[V]) getAllGraphicComposition(opt *shapeSearchOptions) (result []
|
||||||
return match
|
return match
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通过每个点扩散图形
|
if opt.rectangle {
|
||||||
for _, point := range slf.Points() {
|
l, r, t, b := GetShapeCoverageAreaWithCoordinateArray(slf.Points()...)
|
||||||
// 搜索四个方向
|
rs := GenerateShapeOnRectangleWithCoordinate(slf.Points()...)
|
||||||
var next = -1
|
w := r - l + 1
|
||||||
var directionPoint = point
|
h := b - t + 1
|
||||||
var links = Shape[V]{point}
|
shapes := GetExpressibleRectangleBySize(w, h, V(opt.rectangleMinWidth), V(opt.rectangleMinHeight))
|
||||||
var directionCount = map[Direction]int{}
|
for _, s := range shapes {
|
||||||
var count = 0
|
x, y := 0, 0
|
||||||
for i, directions := range [][]Direction{DirectionUDLR, DirectionLRUD} {
|
|
||||||
var direction Direction
|
|
||||||
next, direction = slice.NextLoop(directions, next)
|
|
||||||
for {
|
for {
|
||||||
directionPoint = GetDirectionNextWithCoordinateArray(direction, directionPoint)
|
if V(x)+s.GetX() >= w {
|
||||||
if px, py := directionPoint.GetXY(); px < 0 || px >= areaWidth || py < 0 || py >= areaHeight {
|
x = 0
|
||||||
|
y++
|
||||||
|
}
|
||||||
|
if V(y)+s.GetY() >= h {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
offset := directionPoint.GetOffset(-left, -top)
|
points := GetRectangleFullPoints(s[0]+1, s[1]+1)
|
||||||
if offset.Negative() {
|
find := 0
|
||||||
break
|
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 find == len(points) {
|
||||||
if offsetPos < 0 || offsetPos >= len(rectangleShape) || !rectangleShape[offsetPos].Data {
|
sw := s.GetX() + 1
|
||||||
break
|
sh := s.GetY() + 1
|
||||||
}
|
if !(sw < V(opt.rectangleMinWidth) || sw > V(opt.rectangleMaxWidth) || sh < V(opt.rectangleMinHeight) || sh > V(opt.rectangleMaxHeight)) {
|
||||||
links = append(links, directionPoint)
|
result = append(result, points)
|
||||||
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{}{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
return result
|
||||||
|
@ -262,107 +305,3 @@ func (slf Shape[V]) getAllGraphicCompositionWithDesc(opt *shapeSearchOptions) (r
|
||||||
})
|
})
|
||||||
return
|
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
|
directionCount int
|
||||||
oppositionDirection Direction
|
oppositionDirection Direction
|
||||||
ra bool
|
ra bool
|
||||||
|
rectangle bool
|
||||||
|
rectangleMinWidth int
|
||||||
|
rectangleMinHeight int
|
||||||
|
rectangleMaxWidth int
|
||||||
|
rectangleMaxHeight int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShapeSearchOption 图形搜索可选项,用于 Shape.ShapeSearch 搜索支持
|
// ShapeSearchOption 图形搜索可选项,用于 Shape.ShapeSearch 搜索支持
|
||||||
type ShapeSearchOption func(options *shapeSearchOptions)
|
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 通过直角的方式进行搜索
|
// WithShapeSearchRightAngle 通过直角的方式进行搜索
|
||||||
func WithShapeSearchRightAngle() ShapeSearchOption {
|
func WithShapeSearchRightAngle() ShapeSearchOption {
|
||||||
return func(options *shapeSearchOptions) {
|
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, 0))
|
||||||
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, 1))
|
||||||
shape = append(shape, geometry.NewPoint(2, 2))
|
shape = append(shape, geometry.NewPoint(2, 2))
|
||||||
shape = append(shape, geometry.NewPoint(1, 3))
|
shape = append(shape, geometry.NewPoint(1, 3))
|
||||||
geometry.ShapeStringHasBorder = true
|
geometry.ShapeStringHasBorder = true
|
||||||
|
@ -21,9 +22,7 @@ func TestShape_Search(t *testing.T) {
|
||||||
|
|
||||||
shapes := shape.ShapeSearch(
|
shapes := shape.ShapeSearch(
|
||||||
geometry.WithShapeSearchDesc(),
|
geometry.WithShapeSearchDesc(),
|
||||||
geometry.WithShapeSearchDeduplication(),
|
geometry.WithShapeSearchRectangleLowerLimit(2, 2),
|
||||||
geometry.WithShapeSearchPointCountLowerLimit(3),
|
|
||||||
geometry.WithShapeSearchRightAngle(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, shape := range shapes {
|
for _, shape := range shapes {
|
||||||
|
|
Loading…
Reference in New Issue