Added a shatter function to subtract rectangles from a rectangle

This commit is contained in:
Sasha Koshka 2023-02-11 21:07:35 -05:00
parent 7f1c3ae870
commit a893831a21
2 changed files with 121 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import "time"
import "image"
import "image/color"
import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/shatter"
import "git.tebibyte.media/sashakoshka/tomo/defaultfont"
import "git.tebibyte.media/sashakoshka/tomo/elements/core"
@ -118,6 +119,30 @@ func (element *Artist) draw () {
x, element.cellAt(x, 3))
}
// 4, 3
shatterPos := element.cellAt(4, 3).Min
rocks := []image.Rectangle {
image.Rect(3, 12, 13, 23).Add(shatterPos),
// image.Rect(30, 10, 40, 23).Add(shatterPos),
image.Rect(55, 40, 70, 49).Add(shatterPos),
image.Rect(30, -10, 40, 43).Add(shatterPos),
image.Rect(80, 30, 90, 45).Add(shatterPos),
}
tiles := shatter.Shatter(element.cellAt(4, 3), rocks...)
for index, tile := range tiles {
artist.FillRectangle (
element, []artist.Pattern {
uhex(0xFF0000FF),
uhex(0x00FF00FF),
uhex(0x0000FFFF),
uhex(0xFF00FFFF),
uhex(0xFFF00FFF),
uhex(0xAAAAAAFF),
uhex(0x699669FF),
uhex(0x077777FF),
} [index % 8], tile)
}
// 0, 4 - 3, 4
for x := 0; x < 4; x ++ {
artist.FillEllipse (

96
shatter/shatter.go Normal file
View File

@ -0,0 +1,96 @@
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:]...)
}