Compare commits

...

4 Commits

Author SHA1 Message Date
Sasha Koshka
0824aca4ba Added shatter algorithm to canvas 2023-07-05 03:14:34 -04:00
ec7596fa2d FuncBroadcaster actually broadcasts lol 2023-07-05 00:45:32 -04:00
66e1caeebf Reorganized the box interfaces somewhat 2023-07-02 02:46:12 -04:00
8b78d16506 Add window behavior to box 2023-07-02 00:31:05 -04:00
3 changed files with 136 additions and 20 deletions

96
canvas/shatter.go Normal file
View File

@@ -0,0 +1,96 @@
package canvas
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 box's background are covered by other boxes 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:]...)
}

View File

@@ -61,5 +61,7 @@ type FuncBroadcaster struct {
// Broadcast calls all connected listener funcs.
func (broadcaster *FuncBroadcaster) Broadcast () {
for _, listener := range broadcaster.Listeners() {
listener()
}
}

View File

@@ -98,12 +98,17 @@ type Align int; const (
AlignEven // similar to justified text
)
// Object is any obscreen object. Each object must be linked to a box, even if
// it is that box.
type Object interface {
Box () Box
}
// Box is a basic styled box.
type Box interface {
Object
Window () Window
Bounds () image.Rectangle
InnerBounds () image.Rectangle
SetBounds (image.Rectangle)
@@ -118,8 +123,8 @@ type Box interface {
SetFocusable (bool)
Focused () bool
Modifiers (func ()) input.Modifiers
MousePosition (func ()) image.Point
Modifiers () input.Modifiers
MousePosition () image.Point
OnFocusEnter (func ()) event.Cookie
OnFocusLeave (func ()) event.Cookie
@@ -134,28 +139,38 @@ type Box interface {
OnScroll (func (deltaX, deltaY float64)) event.Cookie
OnKeyDown (func (key input.Key, numberPad bool)) event.Cookie
OnKeyUp (func (key input.Key, numberPad bool)) event.Cookie
ContentBounds () image.Rectangle
ScrollTo (image.Point)
OnContentBoundsChange (func ()) event.Cookie
}
type TextBox interface {
Box
SetTextColor (color.Color)
SetFace (font.Face)
SetHAlign (Align)
SetVAlign (Align)
}
// CanvasBox is a box that can be drawn to.
type CanvasBox interface {
Box
SetDrawer (canvas.Drawer)
Invalidate ()
}
type ContainerBox interface {
// ContentBox is an abstract box that has some kind of content. Its only purpose
// is to be embedded into TextBox and ContainerBox.
type ContentBox interface {
Box
SetOverflow (horizontal, vertical bool)
ContentBounds () image.Rectangle
ScrollTo (image.Point)
OnContentBoundsChange (func ()) event.Cookie
}
// TextBox is a box that contains text content.
type TextBox interface {
ContentBox
SetTextColor (color.Color)
SetFace (font.Face)
SetHAlign (Align)
SetVAlign (Align)
}
// ContentBox is a box that can contain child objects. It arranges them
// according to a layout rule.
type ContainerBox interface {
ContentBox
SetGap (Gap)
Add (Object)
Delete (Object)
@@ -166,6 +181,12 @@ type ContainerBox interface {
SetLayout (Layout)
}
// Layout can be given to a ContainerBox to arrange child objects.
type Layout interface {
Arrange (image.Rectangle, Gap, []Box)
}
// Window is an operating system window. It can contain one object.
type Window interface {
SetRoot (Object)
SetTitle (string)
@@ -181,11 +202,8 @@ type Window interface {
OnClose (func ()) event.Cookie
}
// MainWindow is a top-level operating system window.
type MainWindow interface {
Window
NewChild (image.Rectangle) (Window, error)
}
type Layout interface {
Arrange (image.Rectangle, Gap, []Box)
}