2023-03-15 23:10:59 -06:00
|
|
|
// Package shatter provides boolean operations for image.Rectangle.
|
2023-02-11 19:07:35 -07:00
|
|
|
package shatter
|
|
|
|
|
|
|
|
import "image"
|
|
|
|
|
|
|
|
// Shatter takes in a bounding rectangle, and several rectangles to be
|
|
|
|
// subtracted from it. It returns a slice of rectangles that tile together to
|
|
|
|
// make up the difference between them. This is intended to be used for figuring
|
|
|
|
// out which areas of a container element's background are covered by other
|
|
|
|
// elements so it doesn't waste CPU cycles drawing to those areas.
|
|
|
|
func Shatter (
|
|
|
|
glass image.Rectangle,
|
|
|
|
rocks ...image.Rectangle,
|
|
|
|
) (
|
|
|
|
tiles []image.Rectangle,
|
|
|
|
) {
|
|
|
|
// in this function, the metaphor of throwing several rocks at a sheet
|
|
|
|
// of glass is used to illustrate the concept.
|
|
|
|
|
|
|
|
tiles = []image.Rectangle { glass }
|
|
|
|
for _, rock := range rocks {
|
|
|
|
|
|
|
|
// check each tile to see if the rock has collided with it
|
|
|
|
tileLen := len(tiles)
|
|
|
|
for tileIndex := 0; tileIndex < tileLen; tileIndex ++ {
|
|
|
|
tile := tiles[tileIndex]
|
|
|
|
if !rock.Overlaps(tile) { continue }
|
|
|
|
newTiles, n := shatterOnce(tile, rock)
|
|
|
|
if n > 0 {
|
|
|
|
// the tile was shattered into one or more sub
|
|
|
|
// tiles
|
|
|
|
tiles[tileIndex] = newTiles[0]
|
|
|
|
tiles = append(tiles, newTiles[1:n]...)
|
|
|
|
} else {
|
|
|
|
// the tile was entirely obscured by the rock
|
|
|
|
// and must be wholly removed
|
|
|
|
tiles = remove(tiles, tileIndex)
|
|
|
|
tileIndex --
|
|
|
|
tileLen --
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func shatterOnce (glass, rock image.Rectangle) (tiles [4]image.Rectangle, n int) {
|
|
|
|
rock = rock.Intersect(glass)
|
|
|
|
|
|
|
|
// |'''''''''''|
|
|
|
|
// | |
|
|
|
|
// |###|'''| |
|
|
|
|
// |###|___| |
|
|
|
|
// | |
|
|
|
|
// |___________|
|
|
|
|
if rock.Min.X > glass.Min.X { tiles[n] = image.Rect (
|
|
|
|
glass.Min.X, rock.Min.Y,
|
|
|
|
rock.Min.X, rock.Max.Y,
|
|
|
|
); n ++ }
|
|
|
|
|
|
|
|
// |'''''''''''|
|
|
|
|
// | |
|
|
|
|
// | |'''|###|
|
|
|
|
// | |___|###|
|
|
|
|
// | |
|
|
|
|
// |___________|
|
|
|
|
if rock.Max.X < glass.Max.X { tiles[n] = image.Rect (
|
|
|
|
rock.Max.X, rock.Min.Y,
|
|
|
|
glass.Max.X, rock.Max.Y,
|
|
|
|
); n ++ }
|
|
|
|
|
|
|
|
// |###########|
|
|
|
|
// |###########|
|
|
|
|
// | |'''| |
|
|
|
|
// | |___| |
|
|
|
|
// | |
|
|
|
|
// |___________|
|
|
|
|
if rock.Min.Y > glass.Min.Y { tiles[n] = image.Rect (
|
|
|
|
glass.Min.X, glass.Min.Y,
|
|
|
|
glass.Max.X, rock.Min.Y,
|
|
|
|
); n ++ }
|
|
|
|
|
|
|
|
// |'''''''''''|
|
|
|
|
// | |
|
|
|
|
// | |'''| |
|
|
|
|
// | |___| |
|
|
|
|
// |###########|
|
|
|
|
// |###########|
|
|
|
|
if rock.Max.Y < glass.Max.Y { tiles[n] = image.Rect (
|
|
|
|
glass.Min.X, rock.Max.Y,
|
|
|
|
glass.Max.X, glass.Max.Y,
|
|
|
|
); n ++ }
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func remove[ELEMENT any] (slice []ELEMENT, s int) []ELEMENT {
|
|
|
|
return append(slice[:s], slice[s + 1:]...)
|
|
|
|
}
|