diff --git a/utils/geometry/geometry.go b/utils/geometry/geometry.go index 63ceb23..df6422e 100644 --- a/utils/geometry/geometry.go +++ b/utils/geometry/geometry.go @@ -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 { diff --git a/utils/geometry/position.go b/utils/geometry/position.go index c380bfc..7e89d91 100644 --- a/utils/geometry/position.go +++ b/utils/geometry/position.go @@ -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() diff --git a/utils/geometry/shape.go b/utils/geometry/shape.go index ae39bcb..2b66909 100644 --- a/utils/geometry/shape.go +++ b/utils/geometry/shape.go @@ -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 diff --git a/utils/geometry/shape_search_options.go b/utils/geometry/shape_search_options.go index f04b52b..42a4ac7 100644 --- a/utils/geometry/shape_search_options.go +++ b/utils/geometry/shape_search_options.go @@ -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 { diff --git a/utils/geometry/shape_test.go b/utils/geometry/shape_test.go index dba349c..70cb9bc 100644 --- a/utils/geometry/shape_test.go +++ b/utils/geometry/shape_test.go @@ -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 {