Compare commits
2 Commits
Author | SHA1 | Date |
---|---|---|
Sasha Koshka | 501eb34922 | |
Sasha Koshka | 54ea1c283f |
|
@ -20,5 +20,5 @@ it. It will be placed in `~/.local/lib/nasin/plugins`.
|
|||
You can find out more about how to use Tomo and Nasin by visiting the examples
|
||||
directory, or pull up the documentation by running `godoc` within the
|
||||
repository. You can also view it on the web on
|
||||
[pkg.go.dev](https://pkg.go.dev/git.tebibyte.media/sashakoshka/tomo) (although
|
||||
[pkg.go.dev](https://pkg.go.dev/git.tebibyte.media/tomo/tomo) (although
|
||||
it may be slightly out of date).
|
||||
|
|
|
@ -4,7 +4,7 @@ package ability
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
// Layoutable represents an element that needs to perform layout calculations
|
||||
// before it can draw itself.
|
||||
|
@ -24,7 +24,7 @@ type Container interface {
|
|||
// the specified canvas. The bounds of this canvas specify the area that
|
||||
// is actually drawn to, while the Entity bounds specify the actual area
|
||||
// of the element.
|
||||
DrawBackground (artist.Canvas)
|
||||
DrawBackground (art.Canvas)
|
||||
|
||||
// HandleChildMinimumSizeChange is called when a child's minimum size is
|
||||
// changed.
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
// Package artutil provides utility functions for working with graphical types
|
||||
// defined in artist, canvas, and image.
|
||||
package artutil
|
||||
|
||||
import "image"
|
||||
import "image/color"
|
||||
import "tomo/artist"
|
||||
import "tomo/shatter"
|
||||
|
||||
// Fill fills the destination canvas with the given pattern.
|
||||
func Fill (destination artist.Canvas, source artist.Pattern) (updated image.Rectangle) {
|
||||
source.Draw(destination, destination.Bounds())
|
||||
return destination.Bounds()
|
||||
}
|
||||
|
||||
// DrawClip lets you draw several subsets of a pattern at once.
|
||||
func DrawClip (
|
||||
destination artist.Canvas,
|
||||
source artist.Pattern,
|
||||
bounds image.Rectangle,
|
||||
subsets ...image.Rectangle,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
for _, subset := range subsets {
|
||||
source.Draw(artist.Cut(destination, subset), bounds)
|
||||
updatedRegion = updatedRegion.Union(subset)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DrawShatter is like an inverse of DrawClip, drawing nothing in the areas
|
||||
// specified by "rocks".
|
||||
func DrawShatter (
|
||||
destination artist.Canvas,
|
||||
source artist.Pattern,
|
||||
bounds image.Rectangle,
|
||||
rocks ...image.Rectangle,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
tiles := shatter.Shatter(bounds, rocks...)
|
||||
return DrawClip(destination, source, bounds, tiles...)
|
||||
}
|
||||
|
||||
// AllocateSample returns a new canvas containing the result of a pattern. The
|
||||
// resulting canvas can be sourced from shape drawing functions. I beg of you
|
||||
// please do not call this every time you need to draw a shape with a pattern on
|
||||
// it because that is horrible and cruel to the computer.
|
||||
func AllocateSample (source artist.Pattern, width, height int) artist.Canvas {
|
||||
allocated := artist.NewBasicCanvas(width, height)
|
||||
Fill(allocated, source)
|
||||
return allocated
|
||||
}
|
||||
|
||||
// Hex creates a color.RGBA value from an RGBA integer value.
|
||||
func Hex (color uint32) (c color.RGBA) {
|
||||
c.A = uint8(color)
|
||||
c.B = uint8(color >> 8)
|
||||
c.G = uint8(color >> 16)
|
||||
c.R = uint8(color >> 24)
|
||||
return
|
||||
}
|
111
artist/canvas.go
111
artist/canvas.go
|
@ -1,111 +0,0 @@
|
|||
package artist
|
||||
|
||||
import "image"
|
||||
import "image/draw"
|
||||
import "image/color"
|
||||
|
||||
// Image represents an immutable canvas.
|
||||
type Image interface {
|
||||
image.Image
|
||||
RGBAAt (x, y int) color.RGBA
|
||||
}
|
||||
|
||||
// Canvas is like draw.Image but is also able to return a raw pixel buffer for
|
||||
// more efficient drawing. This interface can be easily satisfied using a
|
||||
// BasicCanvas struct.
|
||||
type Canvas interface {
|
||||
draw.Image
|
||||
Buffer () (data []color.RGBA, stride int)
|
||||
}
|
||||
|
||||
// BasicCanvas is a general purpose implementation of tomo.Canvas.
|
||||
type BasicCanvas struct {
|
||||
pix []color.RGBA
|
||||
stride int
|
||||
rect image.Rectangle
|
||||
}
|
||||
|
||||
// NewBasicCanvas creates a new basic canvas with the specified width and
|
||||
// height, allocating a buffer for it.
|
||||
func NewBasicCanvas (width, height int) (canvas BasicCanvas) {
|
||||
canvas.pix = make([]color.RGBA, height * width)
|
||||
canvas.stride = width
|
||||
canvas.rect = image.Rect(0, 0, width, height)
|
||||
return
|
||||
}
|
||||
|
||||
// FromImage creates a new BasicCanvas from an image.Image.
|
||||
func FromImage (img image.Image) (canvas BasicCanvas) {
|
||||
bounds := img.Bounds()
|
||||
canvas = NewBasicCanvas(bounds.Dx(), bounds.Dy())
|
||||
point := image.Point { }
|
||||
for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ {
|
||||
for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ {
|
||||
canvasPoint := point.Sub(bounds.Min)
|
||||
canvas.Set (
|
||||
canvasPoint.X, canvasPoint.Y,
|
||||
img.At(point.X, point.Y))
|
||||
}}
|
||||
return
|
||||
}
|
||||
|
||||
// you know what it do
|
||||
func (canvas BasicCanvas) Bounds () (bounds image.Rectangle) {
|
||||
return canvas.rect
|
||||
}
|
||||
|
||||
// you know what it do
|
||||
func (canvas BasicCanvas) At (x, y int) (color.Color) {
|
||||
if !image.Pt(x, y).In(canvas.rect) { return nil }
|
||||
return canvas.pix[x + y * canvas.stride]
|
||||
}
|
||||
|
||||
// you know what it do
|
||||
func (canvas BasicCanvas) ColorModel () (model color.Model) {
|
||||
return color.RGBAModel
|
||||
}
|
||||
|
||||
// you know what it do
|
||||
func (canvas BasicCanvas) Set (x, y int, c color.Color) {
|
||||
if !image.Pt(x, y).In(canvas.rect) { return }
|
||||
r, g, b, a := c.RGBA()
|
||||
canvas.pix[x + y * canvas.stride] = color.RGBA {
|
||||
R: uint8(r >> 8),
|
||||
G: uint8(g >> 8),
|
||||
B: uint8(b >> 8),
|
||||
A: uint8(a >> 8),
|
||||
}
|
||||
}
|
||||
|
||||
// you know what it do
|
||||
func (canvas BasicCanvas) Buffer () (data []color.RGBA, stride int) {
|
||||
return canvas.pix, canvas.stride
|
||||
}
|
||||
|
||||
// Reallocate efficiently reallocates the canvas. The data within will be
|
||||
// garbage. This method will do nothing if this is a cut image.
|
||||
func (canvas *BasicCanvas) Reallocate (width, height int) {
|
||||
if canvas.rect.Min != (image.Point { }) { return }
|
||||
|
||||
previousLen := len(canvas.pix)
|
||||
newLen := width * height
|
||||
bigger := newLen > previousLen
|
||||
smaller := newLen < previousLen / 2
|
||||
if bigger || smaller {
|
||||
canvas.pix = make (
|
||||
[]color.RGBA,
|
||||
((height * width) / 4096) * 4096 + 4096)
|
||||
}
|
||||
canvas.stride = width
|
||||
canvas.rect = image.Rect(0, 0, width, height)
|
||||
}
|
||||
|
||||
// Cut returns a sub-canvas of a given canvas.
|
||||
func Cut (canvas Canvas, bounds image.Rectangle) (reduced BasicCanvas) {
|
||||
// println(canvas.Bounds().String(), bounds.String())
|
||||
bounds = bounds.Intersect(canvas.Bounds())
|
||||
if bounds.Empty() { return }
|
||||
reduced.rect = bounds
|
||||
reduced.pix, reduced.stride = canvas.Buffer()
|
||||
return
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
// Package artist provides a simple 2D drawing library for canvas.Canvas.
|
||||
package artist
|
|
@ -1,13 +0,0 @@
|
|||
package artist
|
||||
|
||||
import "image"
|
||||
import "image/color"
|
||||
|
||||
type Icon interface {
|
||||
// Draw draws the icon to the destination canvas at the specified point,
|
||||
// using the specified color (if the icon is monochrome).
|
||||
Draw (destination Canvas, color color.RGBA, at image.Point)
|
||||
|
||||
// Bounds returns the bounds of the icon.
|
||||
Bounds () image.Rectangle
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package artist
|
||||
|
||||
import "image"
|
||||
|
||||
// Side represents one side of a rectangle.
|
||||
type Side int; const (
|
||||
SideTop Side = iota
|
||||
SideRight
|
||||
SideBottom
|
||||
SideLeft
|
||||
)
|
||||
|
||||
// Inset represents an inset amount for all four sides of a rectangle. The top
|
||||
// side is at index zero, the right at index one, the bottom at index two, and
|
||||
// the left at index three. These values may be negative.
|
||||
type Inset [4]int
|
||||
|
||||
// I allows you to create an inset in a CSS-ish way:
|
||||
//
|
||||
// - One argument: all sides are set to this value
|
||||
// - Two arguments: the top and bottom sides are set to the first value, and
|
||||
// the left and right sides are set to the second value.
|
||||
// - Three arguments: the top side is set by the first value, the left and
|
||||
// right sides are set by the second vaue, and the bottom side is set by the
|
||||
// third value.
|
||||
// - Four arguments: each value corresponds to a side.
|
||||
//
|
||||
// This function will panic if an argument count that isn't one of these is
|
||||
// given.
|
||||
func I (sides ...int) Inset {
|
||||
switch len(sides) {
|
||||
case 1: return Inset { sides[0], sides[0], sides[0], sides[0] }
|
||||
case 2: return Inset { sides[0], sides[1], sides[0], sides[1] }
|
||||
case 3: return Inset { sides[0], sides[1], sides[2], sides[1] }
|
||||
case 4: return Inset { sides[0], sides[1], sides[2], sides[3] }
|
||||
default: panic("I: illegal argument count.")
|
||||
}
|
||||
}
|
||||
|
||||
// Apply returns the given rectangle, shrunk on all four sides by the given
|
||||
// inset. If a measurment of the inset is negative, that side will instead be
|
||||
// expanded outward. If the rectangle's dimensions cannot be reduced any
|
||||
// further, an empty rectangle near its center will be returned.
|
||||
func (inset Inset) Apply (bigger image.Rectangle) (smaller image.Rectangle) {
|
||||
smaller = bigger
|
||||
if smaller.Dx() < inset[3] + inset[1] {
|
||||
smaller.Min.X = (smaller.Min.X + smaller.Max.X) / 2
|
||||
smaller.Max.X = smaller.Min.X
|
||||
} else {
|
||||
smaller.Min.X += inset[3]
|
||||
smaller.Max.X -= inset[1]
|
||||
}
|
||||
|
||||
if smaller.Dy() < inset[0] + inset[2] {
|
||||
smaller.Min.Y = (smaller.Min.Y + smaller.Max.Y) / 2
|
||||
smaller.Max.Y = smaller.Min.Y
|
||||
} else {
|
||||
smaller.Min.Y += inset[0]
|
||||
smaller.Max.Y -= inset[2]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Inverse returns a negated version of the inset.
|
||||
func (inset Inset) Inverse () (prime Inset) {
|
||||
return Inset {
|
||||
inset[0] * -1,
|
||||
inset[1] * -1,
|
||||
inset[2] * -1,
|
||||
inset[3] * -1,
|
||||
}
|
||||
}
|
||||
|
||||
// Horizontal returns the sum of SideRight and SideLeft.
|
||||
func (inset Inset) Horizontal () int {
|
||||
return inset[SideRight] + inset[SideLeft]
|
||||
}
|
||||
|
||||
// Vertical returns the sum of SideTop and SideBottom.
|
||||
func (inset Inset) Vertical () int {
|
||||
return inset[SideTop] + inset[SideBottom]
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package artist
|
||||
|
||||
import "image"
|
||||
|
||||
// Pattern is capable of drawing to a canvas within the bounds of a given
|
||||
// clipping rectangle.
|
||||
type Pattern interface {
|
||||
// Draw draws the pattern onto the destination canvas, using the
|
||||
// specified bounds. The given bounds can be smaller or larger than the
|
||||
// bounds of the destination canvas. The destination canvas can be cut
|
||||
// using canvas.Cut() to draw only a specific subset of a pattern.
|
||||
Draw (destination Canvas, bounds image.Rectangle)
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
package patterns
|
||||
|
||||
import "image"
|
||||
import "tomo/artist"
|
||||
|
||||
// Border is a pattern that behaves similarly to border-image in CSS. It divides
|
||||
// a source canvas into nine sections...
|
||||
//
|
||||
// Inset[1]
|
||||
// ┌──┴──┐
|
||||
// ┌─┌─────┬─────┬─────┐
|
||||
// Inset[0]─┤ │ 0 │ 1 │ 2 │
|
||||
// └─├─────┼─────┼─────┤
|
||||
// │ 3 │ 4 │ 5 │
|
||||
// ├─────┼─────┼─────┤─┐
|
||||
// │ 6 │ 7 │ 8 │ ├─Inset[2]
|
||||
// └─────┴─────┴─────┘─┘
|
||||
// └──┬──┘
|
||||
// Inset[3]
|
||||
//
|
||||
// ... Where the bounds of section 4 are defined as the application of the
|
||||
// pattern's inset to the canvas's bounds. The bounds of the other eight
|
||||
// sections are automatically sized around it.
|
||||
//
|
||||
// When drawn to a destination canvas, the bounds of sections 1, 3, 4, 5, and 7
|
||||
// are expanded or contracted to fit the given drawing bounds. All sections are
|
||||
// rendered as if they are Texture patterns, meaning these flexible sections
|
||||
// will repeat to fill in any empty space.
|
||||
//
|
||||
// This pattern can be used to make a static image texture into something that
|
||||
// responds well to being resized.
|
||||
type Border struct {
|
||||
artist.Canvas
|
||||
artist.Inset
|
||||
}
|
||||
|
||||
// Draw draws the border pattern onto the destination canvas within the given
|
||||
// bounds.
|
||||
func (pattern Border) Draw (destination artist.Canvas, bounds image.Rectangle) {
|
||||
drawBounds := bounds.Canon().Intersect(destination.Bounds())
|
||||
if drawBounds.Empty() { return }
|
||||
|
||||
srcSections := nonasect(pattern.Bounds(), pattern.Inset)
|
||||
srcTextures := [9]Texture { }
|
||||
for index, section := range srcSections {
|
||||
srcTextures[index].Canvas = artist.Cut(pattern, section)
|
||||
}
|
||||
|
||||
dstSections := nonasect(bounds, pattern.Inset)
|
||||
for index, section := range dstSections {
|
||||
srcTextures[index].Draw(destination, section)
|
||||
}
|
||||
}
|
||||
|
||||
func nonasect (bounds image.Rectangle, inset artist.Inset) [9]image.Rectangle {
|
||||
center := inset.Apply(bounds)
|
||||
return [9]image.Rectangle {
|
||||
// top
|
||||
image.Rectangle {
|
||||
bounds.Min,
|
||||
center.Min },
|
||||
image.Rect (
|
||||
center.Min.X, bounds.Min.Y,
|
||||
center.Max.X, center.Min.Y),
|
||||
image.Rect (
|
||||
center.Max.X, bounds.Min.Y,
|
||||
bounds.Max.X, center.Min.Y),
|
||||
|
||||
// center
|
||||
image.Rect (
|
||||
bounds.Min.X, center.Min.Y,
|
||||
center.Min.X, center.Max.Y),
|
||||
center,
|
||||
image.Rect (
|
||||
center.Max.X, center.Min.Y,
|
||||
bounds.Max.X, center.Max.Y),
|
||||
|
||||
// bottom
|
||||
image.Rect (
|
||||
bounds.Min.X, center.Max.Y,
|
||||
center.Min.X, bounds.Max.Y),
|
||||
image.Rect (
|
||||
center.Min.X, center.Max.Y,
|
||||
center.Max.X, bounds.Max.Y),
|
||||
image.Rect (
|
||||
center.Max.X, center.Max.Y,
|
||||
bounds.Max.X, bounds.Max.Y),
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
// Package patterns provides a basic set of types that satisfy the
|
||||
// artist.Pattern interface.
|
||||
package patterns
|
|
@ -1,77 +0,0 @@
|
|||
package patterns
|
||||
|
||||
import "image"
|
||||
import "tomo/artist"
|
||||
|
||||
// Texture is a pattern that tiles the content of a canvas both horizontally and
|
||||
// vertically.
|
||||
type Texture struct {
|
||||
artist.Canvas
|
||||
}
|
||||
|
||||
// Draw tiles the pattern's canvas within the given bounds. The minimum
|
||||
// point of the pattern's canvas will be lined up with the minimum point of the
|
||||
// bounding rectangle.
|
||||
func (pattern Texture) Draw (destination artist.Canvas, bounds image.Rectangle) {
|
||||
dstBounds := bounds.Canon().Intersect(destination.Bounds())
|
||||
if dstBounds.Empty() { return }
|
||||
|
||||
dstData, dstStride := destination.Buffer()
|
||||
srcData, srcStride := pattern.Buffer()
|
||||
srcBounds := pattern.Bounds()
|
||||
|
||||
// offset is a vector that is added to points in destination space to
|
||||
// convert them to points in source space
|
||||
offset := srcBounds.Min.Sub(bounds.Min)
|
||||
|
||||
// calculate the starting position in source space
|
||||
srcPoint := dstBounds.Min.Add(offset)
|
||||
srcPoint.X = wrap(srcPoint.X, srcBounds.Min.X, srcBounds.Max.X)
|
||||
srcPoint.Y = wrap(srcPoint.Y, srcBounds.Min.Y, srcBounds.Max.Y)
|
||||
srcStartPoint := srcPoint
|
||||
|
||||
// for each row
|
||||
dstPoint := image.Point { }
|
||||
for dstPoint.Y = dstBounds.Min.Y; dstPoint.Y < dstBounds.Max.Y; dstPoint.Y ++ {
|
||||
srcPoint.X = srcStartPoint.X
|
||||
dstPoint.X = dstBounds.Min.X
|
||||
dstYComponent := dstPoint.Y * dstStride
|
||||
srcYComponent := srcPoint.Y * srcStride
|
||||
|
||||
// for each pixel in the row
|
||||
for {
|
||||
// draw pixel
|
||||
dstIndex := dstYComponent + dstPoint.X
|
||||
srcIndex := srcYComponent + srcPoint.X
|
||||
dstData[dstIndex] = srcData[srcIndex]
|
||||
|
||||
// increment X in source space. wrap to start if out of
|
||||
// bounds.
|
||||
srcPoint.X ++
|
||||
if srcPoint.X >= srcBounds.Max.X {
|
||||
srcPoint.X = srcBounds.Min.X
|
||||
}
|
||||
|
||||
// increment X in destination space. stop drawing this
|
||||
// row if out of bounds.
|
||||
dstPoint.X ++
|
||||
if dstPoint.X >= dstBounds.Max.X {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// increment row in source space. wrap to start if out of
|
||||
// bounds.
|
||||
srcPoint.Y ++
|
||||
if srcPoint.Y >= srcBounds.Max.Y {
|
||||
srcPoint.Y = srcBounds.Min.Y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func wrap (value, min, max int) int {
|
||||
difference := max - min
|
||||
value = (value - min) % difference
|
||||
if value < 0 { value += difference }
|
||||
return value + min
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package patterns
|
||||
|
||||
import "image"
|
||||
import "image/color"
|
||||
import "tomo/artist"
|
||||
import "tomo/artist/shapes"
|
||||
import "tomo/artist/artutil"
|
||||
|
||||
// Uniform is a pattern that draws a solid color.
|
||||
type Uniform color.RGBA
|
||||
|
||||
// Draw fills the bounding rectangle with the pattern's color.
|
||||
func (pattern Uniform) Draw (destination artist.Canvas, bounds image.Rectangle) {
|
||||
shapes.FillColorRectangle(destination, color.RGBA(pattern), bounds)
|
||||
}
|
||||
|
||||
// Uhex creates a new Uniform pattern from an RGBA integer value.
|
||||
func Uhex (color uint32) (uniform Uniform) {
|
||||
return Uniform(artutil.Hex(color))
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
// Package shapes provides some basic shape drawing routines.
|
||||
//
|
||||
// A word about using patterns with shape routines:
|
||||
//
|
||||
// Most drawing routines have a version that samples from other canvases, and a
|
||||
// version that samples from a solid color. None of these routines can use
|
||||
// patterns directly, but it is entirely possible to have a pattern draw to an
|
||||
// off-screen canvas and then draw a shape based on that canvas. As a little
|
||||
// bonus, you can save the canvas for later so you don't have to render the
|
||||
// pattern again when you need to redraw the shape.
|
||||
package shapes
|
|
@ -1,231 +0,0 @@
|
|||
package shapes
|
||||
|
||||
import "math"
|
||||
import "image"
|
||||
import "image/color"
|
||||
import "tomo/artist"
|
||||
|
||||
// TODO: redo fill ellipse, stroke ellipse, etc. so that it only takes in
|
||||
// destination and source, using the bounds of destination as the bounds of the
|
||||
// ellipse and the bounds of source as the "clipping rectangle". Line up the Min
|
||||
// of both canvases.
|
||||
|
||||
func FillEllipse (
|
||||
destination artist.Canvas,
|
||||
source artist.Canvas,
|
||||
bounds image.Rectangle,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
dstData, dstStride := destination.Buffer()
|
||||
srcData, srcStride := source.Buffer()
|
||||
|
||||
offset := source.Bounds().Min.Sub(destination.Bounds().Min)
|
||||
drawBounds :=
|
||||
source.Bounds().Sub(offset).
|
||||
Intersect(destination.Bounds()).
|
||||
Intersect(bounds)
|
||||
if bounds.Empty() { return }
|
||||
updatedRegion = bounds
|
||||
|
||||
point := image.Point { }
|
||||
for point.Y = drawBounds.Min.Y; point.Y < drawBounds.Max.Y; point.Y ++ {
|
||||
for point.X = drawBounds.Min.X; point.X < drawBounds.Max.X; point.X ++ {
|
||||
if inEllipse(point, bounds) {
|
||||
offsetPoint := point.Add(offset)
|
||||
dstIndex := point.X + point.Y * dstStride
|
||||
srcIndex := offsetPoint.X + offsetPoint.Y * srcStride
|
||||
dstData[dstIndex] = srcData[srcIndex]
|
||||
}
|
||||
}}
|
||||
return
|
||||
}
|
||||
|
||||
func StrokeEllipse (
|
||||
destination artist.Canvas,
|
||||
source artist.Canvas,
|
||||
bounds image.Rectangle,
|
||||
weight int,
|
||||
) {
|
||||
if weight < 1 { return }
|
||||
|
||||
dstData, dstStride := destination.Buffer()
|
||||
srcData, srcStride := source.Buffer()
|
||||
|
||||
drawBounds := destination.Bounds().Inset(weight - 1)
|
||||
offset := source.Bounds().Min.Sub(destination.Bounds().Min)
|
||||
if drawBounds.Empty() { return }
|
||||
|
||||
context := ellipsePlottingContext {
|
||||
plottingContext: plottingContext {
|
||||
dstData: dstData,
|
||||
dstStride: dstStride,
|
||||
srcData: srcData,
|
||||
srcStride: srcStride,
|
||||
weight: weight,
|
||||
offset: offset,
|
||||
bounds: bounds,
|
||||
},
|
||||
radii: image.Pt(drawBounds.Dx() / 2, drawBounds.Dy() / 2),
|
||||
}
|
||||
context.center = drawBounds.Min.Add(context.radii)
|
||||
context.plotEllipse()
|
||||
}
|
||||
|
||||
type ellipsePlottingContext struct {
|
||||
plottingContext
|
||||
radii image.Point
|
||||
center image.Point
|
||||
}
|
||||
|
||||
func (context ellipsePlottingContext) plotEllipse () {
|
||||
x := float64(0)
|
||||
y := float64(context.radii.Y)
|
||||
|
||||
// region 1 decision parameter
|
||||
decision1 :=
|
||||
float64(context.radii.Y * context.radii.Y) -
|
||||
float64(context.radii.X * context.radii.X * context.radii.Y) +
|
||||
(0.25 * float64(context.radii.X) * float64(context.radii.X))
|
||||
decisionX := float64(2 * context.radii.Y * context.radii.Y * int(x))
|
||||
decisionY := float64(2 * context.radii.X * context.radii.X * int(y))
|
||||
|
||||
// draw region 1
|
||||
for decisionX < decisionY {
|
||||
points := []image.Point {
|
||||
image.Pt(-int(x) + context.center.X, -int(y) + context.center.Y),
|
||||
image.Pt( int(x) + context.center.X, -int(y) + context.center.Y),
|
||||
image.Pt(-int(x) + context.center.X, int(y) + context.center.Y),
|
||||
image.Pt( int(x) + context.center.X, int(y) + context.center.Y),
|
||||
}
|
||||
if context.srcData == nil {
|
||||
context.plotColor(points[0])
|
||||
context.plotColor(points[1])
|
||||
context.plotColor(points[2])
|
||||
context.plotColor(points[3])
|
||||
} else {
|
||||
context.plotSource(points[0])
|
||||
context.plotSource(points[1])
|
||||
context.plotSource(points[2])
|
||||
context.plotSource(points[3])
|
||||
}
|
||||
|
||||
if (decision1 < 0) {
|
||||
x ++
|
||||
decisionX += float64(2 * context.radii.Y * context.radii.Y)
|
||||
decision1 += decisionX + float64(context.radii.Y * context.radii.Y)
|
||||
} else {
|
||||
x ++
|
||||
y --
|
||||
decisionX += float64(2 * context.radii.Y * context.radii.Y)
|
||||
decisionY -= float64(2 * context.radii.X * context.radii.X)
|
||||
decision1 +=
|
||||
decisionX - decisionY +
|
||||
float64(context.radii.Y * context.radii.Y)
|
||||
}
|
||||
}
|
||||
|
||||
// region 2 decision parameter
|
||||
decision2 :=
|
||||
float64(context.radii.Y * context.radii.Y) * (x + 0.5) * (x + 0.5) +
|
||||
float64(context.radii.X * context.radii.X) * (y - 1) * (y - 1) -
|
||||
float64(context.radii.X * context.radii.X * context.radii.Y * context.radii.Y)
|
||||
|
||||
// draw region 2
|
||||
for y >= 0 {
|
||||
points := []image.Point {
|
||||
image.Pt( int(x) + context.center.X, int(y) + context.center.Y),
|
||||
image.Pt(-int(x) + context.center.X, int(y) + context.center.Y),
|
||||
image.Pt( int(x) + context.center.X, -int(y) + context.center.Y),
|
||||
image.Pt(-int(x) + context.center.X, -int(y) + context.center.Y),
|
||||
}
|
||||
if context.srcData == nil {
|
||||
context.plotColor(points[0])
|
||||
context.plotColor(points[1])
|
||||
context.plotColor(points[2])
|
||||
context.plotColor(points[3])
|
||||
} else {
|
||||
context.plotSource(points[0])
|
||||
context.plotSource(points[1])
|
||||
context.plotSource(points[2])
|
||||
context.plotSource(points[3])
|
||||
}
|
||||
|
||||
if decision2 > 0 {
|
||||
y --
|
||||
decisionY -= float64(2 * context.radii.X * context.radii.X)
|
||||
decision2 += float64(context.radii.X * context.radii.X) - decisionY
|
||||
} else {
|
||||
y --
|
||||
x ++
|
||||
decisionX += float64(2 * context.radii.Y * context.radii.Y)
|
||||
decisionY -= float64(2 * context.radii.X * context.radii.X)
|
||||
decision2 +=
|
||||
decisionX - decisionY +
|
||||
float64(context.radii.X * context.radii.X)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FillColorEllipse fills an ellipse within the destination canvas with a solid
|
||||
// color.
|
||||
func FillColorEllipse (
|
||||
destination artist.Canvas,
|
||||
color color.RGBA,
|
||||
bounds image.Rectangle,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
dstData, dstStride := destination.Buffer()
|
||||
|
||||
realBounds := bounds
|
||||
bounds = bounds.Intersect(destination.Bounds()).Canon()
|
||||
if bounds.Empty() { return }
|
||||
updatedRegion = bounds
|
||||
|
||||
point := image.Point { }
|
||||
for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ {
|
||||
for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ {
|
||||
if inEllipse(point, realBounds) {
|
||||
dstData[point.X + point.Y * dstStride] = color
|
||||
}
|
||||
}}
|
||||
return
|
||||
}
|
||||
|
||||
// StrokeColorEllipse is similar to FillColorEllipse, but it draws an inset
|
||||
// outline of an ellipse instead.
|
||||
func StrokeColorEllipse (
|
||||
destination artist.Canvas,
|
||||
color color.RGBA,
|
||||
bounds image.Rectangle,
|
||||
weight int,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
if weight < 1 { return }
|
||||
|
||||
dstData, dstStride := destination.Buffer()
|
||||
insetBounds := bounds.Inset(weight - 1)
|
||||
|
||||
context := ellipsePlottingContext {
|
||||
plottingContext: plottingContext {
|
||||
dstData: dstData,
|
||||
dstStride: dstStride,
|
||||
color: color,
|
||||
weight: weight,
|
||||
bounds: bounds.Intersect(destination.Bounds()),
|
||||
},
|
||||
radii: image.Pt(insetBounds.Dx() / 2, insetBounds.Dy() / 2),
|
||||
}
|
||||
context.center = insetBounds.Min.Add(context.radii)
|
||||
context.plotEllipse()
|
||||
return
|
||||
}
|
||||
|
||||
func inEllipse (point image.Point, bounds image.Rectangle) bool {
|
||||
point = point.Sub(bounds.Min)
|
||||
x := (float64(point.X) + 0.5) / float64(bounds.Dx()) - 0.5
|
||||
y := (float64(point.Y) + 0.5) / float64(bounds.Dy()) - 0.5
|
||||
return math.Hypot(x, y) <= 0.5
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
package shapes
|
||||
|
||||
import "image"
|
||||
import "image/color"
|
||||
import "tomo/artist"
|
||||
|
||||
// ColorLine draws a line from one point to another with the specified weight
|
||||
// and color.
|
||||
func ColorLine (
|
||||
destination artist.Canvas,
|
||||
color color.RGBA,
|
||||
weight int,
|
||||
min image.Point,
|
||||
max image.Point,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
updatedRegion = image.Rectangle { Min: min, Max: max }.Canon()
|
||||
updatedRegion.Max.X ++
|
||||
updatedRegion.Max.Y ++
|
||||
|
||||
data, stride := destination.Buffer()
|
||||
bounds := destination.Bounds()
|
||||
context := linePlottingContext {
|
||||
plottingContext: plottingContext {
|
||||
dstData: data,
|
||||
dstStride: stride,
|
||||
color: color,
|
||||
weight: weight,
|
||||
bounds: bounds,
|
||||
},
|
||||
min: min,
|
||||
max: max,
|
||||
}
|
||||
|
||||
if abs(max.Y - min.Y) < abs(max.X - min.X) {
|
||||
if max.X < min.X { context.swap() }
|
||||
context.lineLow()
|
||||
|
||||
} else {
|
||||
if max.Y < min.Y { context.swap() }
|
||||
context.lineHigh()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type linePlottingContext struct {
|
||||
plottingContext
|
||||
min image.Point
|
||||
max image.Point
|
||||
}
|
||||
|
||||
func (context *linePlottingContext) swap () {
|
||||
temp := context.max
|
||||
context.max = context.min
|
||||
context.min = temp
|
||||
}
|
||||
|
||||
func (context linePlottingContext) lineLow () {
|
||||
deltaX := context.max.X - context.min.X
|
||||
deltaY := context.max.Y - context.min.Y
|
||||
yi := 1
|
||||
|
||||
if deltaY < 0 {
|
||||
yi = -1
|
||||
deltaY *= -1
|
||||
}
|
||||
|
||||
D := (2 * deltaY) - deltaX
|
||||
point := context.min
|
||||
|
||||
for ; point.X < context.max.X; point.X ++ {
|
||||
context.plotColor(point)
|
||||
if D > 0 {
|
||||
D += 2 * (deltaY - deltaX)
|
||||
point.Y += yi
|
||||
} else {
|
||||
D += 2 * deltaY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (context linePlottingContext) lineHigh () {
|
||||
deltaX := context.max.X - context.min.X
|
||||
deltaY := context.max.Y - context.min.Y
|
||||
xi := 1
|
||||
|
||||
if deltaX < 0 {
|
||||
xi = -1
|
||||
deltaX *= -1
|
||||
}
|
||||
|
||||
D := (2 * deltaX) - deltaY
|
||||
point := context.min
|
||||
|
||||
for ; point.Y < context.max.Y; point.Y ++ {
|
||||
context.plotColor(point)
|
||||
if D > 0 {
|
||||
point.X += xi
|
||||
D += 2 * (deltaX - deltaY)
|
||||
} else {
|
||||
D += 2 * deltaX
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func abs (n int) int {
|
||||
if n < 0 { n *= -1}
|
||||
return n
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package shapes
|
||||
|
||||
import "image"
|
||||
import "image/color"
|
||||
|
||||
// FIXME? drawing a ton of overlapping squares might be a bit wasteful.
|
||||
|
||||
type plottingContext struct {
|
||||
dstData []color.RGBA
|
||||
dstStride int
|
||||
srcData []color.RGBA
|
||||
srcStride int
|
||||
color color.RGBA
|
||||
weight int
|
||||
offset image.Point
|
||||
bounds image.Rectangle
|
||||
}
|
||||
|
||||
func (context plottingContext) square (center image.Point) (square image.Rectangle) {
|
||||
return image.Rect(0, 0, context.weight, context.weight).
|
||||
Sub(image.Pt(context.weight / 2, context.weight / 2)).
|
||||
Add(center).
|
||||
Intersect(context.bounds)
|
||||
}
|
||||
|
||||
func (context plottingContext) plotColor (center image.Point) {
|
||||
square := context.square(center)
|
||||
for y := square.Min.Y; y < square.Max.Y; y ++ {
|
||||
for x := square.Min.X; x < square.Max.X; x ++ {
|
||||
context.dstData[x + y * context.dstStride] = context.color
|
||||
}}
|
||||
}
|
||||
|
||||
func (context plottingContext) plotSource (center image.Point) {
|
||||
square := context.square(center)
|
||||
for y := square.Min.Y; y < square.Max.Y; y ++ {
|
||||
for x := square.Min.X; x < square.Max.X; x ++ {
|
||||
// we offset srcIndex here because we have already applied the
|
||||
// offset to the square, and we need to reverse that to get the
|
||||
// proper source coordinates.
|
||||
srcIndex :=
|
||||
x + context.offset.X +
|
||||
(y + context.offset.Y) * context.dstStride
|
||||
dstIndex := x + y * context.dstStride
|
||||
context.dstData[dstIndex] = context.srcData [srcIndex]
|
||||
}}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
package shapes
|
||||
|
||||
import "image"
|
||||
import "image/color"
|
||||
import "tomo/artist"
|
||||
import "tomo/shatter"
|
||||
|
||||
// TODO: return updatedRegion for all routines in this package
|
||||
|
||||
func FillRectangle (
|
||||
destination artist.Canvas,
|
||||
source artist.Canvas,
|
||||
bounds image.Rectangle,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
dstData, dstStride := destination.Buffer()
|
||||
srcData, srcStride := source.Buffer()
|
||||
|
||||
offset := source.Bounds().Min.Sub(destination.Bounds().Min)
|
||||
drawBounds :=
|
||||
source.Bounds().Sub(offset).
|
||||
Intersect(destination.Bounds()).
|
||||
Intersect(bounds)
|
||||
if drawBounds.Empty() { return }
|
||||
updatedRegion = drawBounds
|
||||
|
||||
point := image.Point { }
|
||||
for point.Y = drawBounds.Min.Y; point.Y < drawBounds.Max.Y; point.Y ++ {
|
||||
for point.X = drawBounds.Min.X; point.X < drawBounds.Max.X; point.X ++ {
|
||||
offsetPoint := point.Add(offset)
|
||||
dstIndex := point.X + point.Y * dstStride
|
||||
srcIndex := offsetPoint.X + offsetPoint.Y * srcStride
|
||||
dstData[dstIndex] = srcData[srcIndex]
|
||||
}}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func StrokeRectangle (
|
||||
destination artist.Canvas,
|
||||
source artist.Canvas,
|
||||
bounds image.Rectangle,
|
||||
weight int,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
insetBounds := bounds.Inset(weight)
|
||||
if insetBounds.Empty() {
|
||||
return FillRectangle(destination, source, bounds)
|
||||
}
|
||||
return FillRectangleShatter(destination, source, bounds, insetBounds)
|
||||
}
|
||||
|
||||
// FillRectangleShatter is like FillRectangle, but it does not draw in areas
|
||||
// specified in "rocks".
|
||||
func FillRectangleShatter (
|
||||
destination artist.Canvas,
|
||||
source artist.Canvas,
|
||||
bounds image.Rectangle,
|
||||
rocks ...image.Rectangle,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
tiles := shatter.Shatter(bounds, rocks...)
|
||||
for _, tile := range tiles {
|
||||
FillRectangle (
|
||||
artist.Cut(destination, tile),
|
||||
source, tile)
|
||||
updatedRegion = updatedRegion.Union(tile)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FillColorRectangle fills a rectangle within the destination canvas with a
|
||||
// solid color.
|
||||
func FillColorRectangle (
|
||||
destination artist.Canvas,
|
||||
color color.RGBA,
|
||||
bounds image.Rectangle,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
dstData, dstStride := destination.Buffer()
|
||||
bounds = bounds.Canon().Intersect(destination.Bounds())
|
||||
if bounds.Empty() { return }
|
||||
|
||||
updatedRegion = bounds
|
||||
for y := bounds.Min.Y; y < bounds.Max.Y; y ++ {
|
||||
for x := bounds.Min.X; x < bounds.Max.X; x ++ {
|
||||
dstData[x + y * dstStride] = color
|
||||
}}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// FillColorRectangleShatter is like FillColorRectangle, but it does not draw in
|
||||
// areas specified in "rocks".
|
||||
func FillColorRectangleShatter (
|
||||
destination artist.Canvas,
|
||||
color color.RGBA,
|
||||
bounds image.Rectangle,
|
||||
rocks ...image.Rectangle,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
tiles := shatter.Shatter(bounds, rocks...)
|
||||
for _, tile := range tiles {
|
||||
FillColorRectangle(destination, color, tile)
|
||||
updatedRegion = updatedRegion.Union(tile)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// StrokeColorRectangle is similar to FillColorRectangle, but it draws an inset
|
||||
// outline of the given rectangle instead.
|
||||
func StrokeColorRectangle (
|
||||
destination artist.Canvas,
|
||||
color color.RGBA,
|
||||
bounds image.Rectangle,
|
||||
weight int,
|
||||
) (
|
||||
updatedRegion image.Rectangle,
|
||||
) {
|
||||
insetBounds := bounds.Inset(weight)
|
||||
if insetBounds.Empty() {
|
||||
return FillColorRectangle(destination, color, bounds)
|
||||
}
|
||||
return FillColorRectangleShatter(destination, color, bounds, insetBounds)
|
||||
}
|
|
@ -9,14 +9,14 @@ import "golang.org/x/image/font"
|
|||
import "golang.org/x/image/font/basicfont"
|
||||
import "tomo"
|
||||
import "tomo/data"
|
||||
import "tomo/artist"
|
||||
import "tomo/artist/artutil"
|
||||
import "tomo/artist/patterns"
|
||||
import "art"
|
||||
import "art/artutil"
|
||||
import "art/patterns"
|
||||
|
||||
//go:embed assets/default.png
|
||||
var defaultAtlasBytes []byte
|
||||
var defaultAtlas artist.Canvas
|
||||
var defaultTextures [7][7]artist.Pattern
|
||||
var defaultAtlas art.Canvas
|
||||
var defaultTextures [7][7]art.Pattern
|
||||
//go:embed assets/wintergreen-icons-small.png
|
||||
var defaultIconsSmallAtlasBytes []byte
|
||||
var defaultIconsSmall [640]binaryIcon
|
||||
|
@ -24,15 +24,15 @@ var defaultIconsSmall [640]binaryIcon
|
|||
var defaultIconsLargeAtlasBytes []byte
|
||||
var defaultIconsLarge [640]binaryIcon
|
||||
|
||||
func atlasCell (col, row int, border artist.Inset) {
|
||||
func atlasCell (col, row int, border art.Inset) {
|
||||
bounds := image.Rect(0, 0, 8, 8).Add(image.Pt(col, row).Mul(8))
|
||||
defaultTextures[col][row] = patterns.Border {
|
||||
Canvas: artist.Cut(defaultAtlas, bounds),
|
||||
Canvas: art.Cut(defaultAtlas, bounds),
|
||||
Inset: border,
|
||||
}
|
||||
}
|
||||
|
||||
func atlasCol (col int, border artist.Inset) {
|
||||
func atlasCol (col int, border art.Inset) {
|
||||
for index, _ := range defaultTextures[col] {
|
||||
atlasCell(col, index, border)
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ type binaryIcon struct {
|
|||
stride int
|
||||
}
|
||||
|
||||
func (icon binaryIcon) Draw (destination artist.Canvas, color color.RGBA, at image.Point) {
|
||||
func (icon binaryIcon) Draw (destination art.Canvas, color color.RGBA, at image.Point) {
|
||||
bounds := icon.Bounds().Add(at).Intersect(destination.Bounds())
|
||||
point := image.Point { }
|
||||
data, stride := destination.Buffer()
|
||||
|
@ -85,15 +85,15 @@ func binaryIconFrom (source image.Image, clip image.Rectangle) (icon binaryIcon)
|
|||
|
||||
func init () {
|
||||
defaultAtlasImage, _, _ := image.Decode(bytes.NewReader(defaultAtlasBytes))
|
||||
defaultAtlas = artist.FromImage(defaultAtlasImage)
|
||||
defaultAtlas = art.FromImage(defaultAtlasImage)
|
||||
|
||||
atlasCol(0, artist.I(0))
|
||||
atlasCol(1, artist.I(3))
|
||||
atlasCol(2, artist.I(1))
|
||||
atlasCol(3, artist.I(1))
|
||||
atlasCol(4, artist.I(1))
|
||||
atlasCol(5, artist.I(3))
|
||||
atlasCol(6, artist.I(1))
|
||||
atlasCol(0, art.I(0))
|
||||
atlasCol(1, art.I(3))
|
||||
atlasCol(2, art.I(1))
|
||||
atlasCol(3, art.I(1))
|
||||
atlasCol(4, art.I(1))
|
||||
atlasCol(5, art.I(3))
|
||||
atlasCol(6, art.I(1))
|
||||
|
||||
// set up small icons
|
||||
defaultIconsSmallAtlasImage, _, _ := image.Decode (
|
||||
|
@ -139,7 +139,7 @@ func (Default) FontFace (style tomo.FontStyle, size tomo.FontSize, c tomo.Case)
|
|||
}
|
||||
|
||||
// Icon returns an icon from the default set corresponding to the given name.
|
||||
func (Default) Icon (id tomo.Icon, size tomo.IconSize, c tomo.Case) artist.Icon {
|
||||
func (Default) Icon (id tomo.Icon, size tomo.IconSize, c tomo.Case) art.Icon {
|
||||
if size == tomo.IconSizeLarge {
|
||||
if id < 0 || int(id) >= len(defaultIconsLarge) {
|
||||
return nil
|
||||
|
@ -157,14 +157,14 @@ func (Default) Icon (id tomo.Icon, size tomo.IconSize, c tomo.Case) artist.Icon
|
|||
|
||||
// MimeIcon returns an icon from the default set corresponding to the given mime.
|
||||
// type.
|
||||
func (Default) MimeIcon (data.Mime, tomo.IconSize, tomo.Case) artist.Icon {
|
||||
func (Default) MimeIcon (data.Mime, tomo.IconSize, tomo.Case) art.Icon {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pattern returns a pattern from the default theme corresponding to the given
|
||||
// pattern ID.
|
||||
func (Default) Pattern (id tomo.Pattern, state tomo.State, c tomo.Case) artist.Pattern {
|
||||
func (Default) Pattern (id tomo.Pattern, state tomo.State, c tomo.Case) art.Pattern {
|
||||
offset := 0; switch {
|
||||
case state.Disabled: offset = 1
|
||||
case state.Pressed && state.On: offset = 4
|
||||
|
@ -224,11 +224,11 @@ func (Default) Color (id tomo.Color, state tomo.State, c tomo.Case) color.RGBA {
|
|||
}
|
||||
|
||||
// Padding returns the default padding value for the given pattern.
|
||||
func (Default) Padding (id tomo.Pattern, c tomo.Case) artist.Inset {
|
||||
func (Default) Padding (id tomo.Pattern, c tomo.Case) art.Inset {
|
||||
switch id {
|
||||
case tomo.PatternGutter: return artist.I(0)
|
||||
case tomo.PatternLine: return artist.I(1)
|
||||
default: return artist.I(6)
|
||||
case tomo.PatternGutter: return art.I(0)
|
||||
case tomo.PatternLine: return art.I(1)
|
||||
default: return art.I(6)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package tomo
|
||||
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
// Element represents a basic on-screen object. Extended element interfaces are
|
||||
// defined in the ability module.
|
||||
|
@ -8,7 +8,7 @@ type Element interface {
|
|||
// Draw causes the element to draw to the specified canvas. The bounds
|
||||
// of this canvas specify the area that is actually drawn to, while the
|
||||
// Entity bounds specify the actual area of the element.
|
||||
Draw (artist.Canvas)
|
||||
Draw (art.Canvas)
|
||||
|
||||
// Entity returns this element's entity.
|
||||
Entity () Entity
|
||||
|
|
|
@ -2,8 +2,8 @@ package elements
|
|||
|
||||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/artist"
|
||||
import "tomo/shatter"
|
||||
import "art"
|
||||
import "art/shatter"
|
||||
|
||||
var boxCase = tomo.C("tomo", "box")
|
||||
|
||||
|
@ -61,7 +61,7 @@ func NewVBox (space Space, children ...tomo.Element) (element *Box) {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Box) Draw (destination artist.Canvas) {
|
||||
func (element *Box) Draw (destination art.Canvas) {
|
||||
rocks := make([]image.Rectangle, element.entity.CountChildren())
|
||||
for index := 0; index < element.entity.CountChildren(); index ++ {
|
||||
rocks[index] = element.entity.Child(index).Entity().Bounds()
|
||||
|
@ -69,7 +69,7 @@ func (element *Box) Draw (destination artist.Canvas) {
|
|||
|
||||
tiles := shatter.Shatter(element.entity.Bounds(), rocks...)
|
||||
for _, tile := range tiles {
|
||||
element.entity.DrawBackground(artist.Cut(destination, tile))
|
||||
element.entity.DrawBackground(art.Cut(destination, tile))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ func (element *Box) AdoptExpand (children ...tomo.Element) {
|
|||
|
||||
// DrawBackground draws this element's background pattern to the specified
|
||||
// destination canvas.
|
||||
func (element *Box) DrawBackground (destination artist.Canvas) {
|
||||
func (element *Box) DrawBackground (destination art.Canvas) {
|
||||
element.entity.DrawBackground(destination)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package elements
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/textdraw"
|
||||
|
||||
var buttonCase = tomo.C("tomo", "button")
|
||||
|
@ -42,7 +42,7 @@ func (element *Button) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Button) Draw (destination artist.Canvas) {
|
||||
func (element *Button) Draw (destination art.Canvas) {
|
||||
state := element.state()
|
||||
bounds := element.entity.Bounds()
|
||||
pattern := element.entity.Theme().Pattern(tomo.PatternButton, state, buttonCase)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package elements
|
||||
|
||||
import "tomo"
|
||||
import "tomo/artist"
|
||||
import "tomo/artist/artutil"
|
||||
import "art"
|
||||
import "art/artutil"
|
||||
|
||||
var cellCase = tomo.C("tomo", "cell")
|
||||
|
||||
|
@ -32,7 +32,7 @@ func (element *Cell) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Cell) Draw (destination artist.Canvas) {
|
||||
func (element *Cell) Draw (destination art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
pattern := element.entity.Theme().Pattern(tomo.PatternTableCell, element.state(), cellCase)
|
||||
if element.child == nil {
|
||||
|
@ -56,7 +56,7 @@ func (element *Cell) Layout () {
|
|||
|
||||
// DrawBackground draws this element's background pattern to the specified
|
||||
// destination canvas.
|
||||
func (element *Cell) DrawBackground (destination artist.Canvas) {
|
||||
func (element *Cell) DrawBackground (destination art.Canvas) {
|
||||
element.entity.Theme().Pattern(tomo.PatternTableCell, element.state(), cellCase).
|
||||
Draw(destination, element.entity.Bounds())
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package elements
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/textdraw"
|
||||
|
||||
var checkboxCase = tomo.C("tomo", "checkbox")
|
||||
|
@ -39,7 +39,7 @@ func (element *Checkbox) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Checkbox) Draw (destination artist.Canvas) {
|
||||
func (element *Checkbox) Draw (destination art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
boxBounds := image.Rect(0, 0, bounds.Dy(), bounds.Dy()).Add(bounds.Min)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package elements
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/ability"
|
||||
import "tomo/textdraw"
|
||||
|
||||
|
@ -53,7 +53,7 @@ func (element *ComboBox) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *ComboBox) Draw (destination artist.Canvas) {
|
||||
func (element *ComboBox) Draw (destination art.Canvas) {
|
||||
state := element.state()
|
||||
bounds := element.entity.Bounds()
|
||||
pattern := element.entity.Theme().Pattern(tomo.PatternButton, state, comboBoxCase)
|
||||
|
|
|
@ -4,9 +4,9 @@ import "image"
|
|||
import "path/filepath"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/ability"
|
||||
import "tomo/shatter"
|
||||
import "art/shatter"
|
||||
|
||||
// TODO: base on flow implementation of list. also be able to switch to a table
|
||||
// variant for a more information dense view.
|
||||
|
@ -52,7 +52,7 @@ func NewDirectory (
|
|||
return
|
||||
}
|
||||
|
||||
func (element *Directory) Draw (destination artist.Canvas) {
|
||||
func (element *Directory) Draw (destination art.Canvas) {
|
||||
rocks := make([]image.Rectangle, element.entity.CountChildren())
|
||||
for index := 0; index < element.entity.CountChildren(); index ++ {
|
||||
rocks[index] = element.entity.Child(index).Entity().Bounds()
|
||||
|
@ -60,7 +60,7 @@ func (element *Directory) Draw (destination artist.Canvas) {
|
|||
|
||||
tiles := shatter.Shatter(element.entity.Bounds(), rocks...)
|
||||
for _, tile := range tiles {
|
||||
element.DrawBackground(artist.Cut(destination, tile))
|
||||
element.DrawBackground(art.Cut(destination, tile))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,7 +199,7 @@ func (element *Directory) ScrollAxes () (horizontal, vertical bool) {
|
|||
return false, true
|
||||
}
|
||||
|
||||
func (element *Directory) DrawBackground (destination artist.Canvas) {
|
||||
func (element *Directory) DrawBackground (destination art.Canvas) {
|
||||
element.entity.Theme().Pattern(tomo.PatternPinboard, tomo.State { }, directoryCase).
|
||||
Draw(destination, element.entity.Bounds())
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ package elements
|
|||
|
||||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/ability"
|
||||
import "tomo/shatter"
|
||||
import "art/shatter"
|
||||
|
||||
var documentCase = tomo.C("tomo", "document")
|
||||
|
||||
|
@ -33,7 +33,7 @@ func NewDocument (children ...tomo.Element) (element *Document) {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Document) Draw (destination artist.Canvas) {
|
||||
func (element *Document) Draw (destination art.Canvas) {
|
||||
rocks := make([]image.Rectangle, element.entity.CountChildren())
|
||||
for index := 0; index < element.entity.CountChildren(); index ++ {
|
||||
rocks[index] = element.entity.Child(index).Entity().Bounds()
|
||||
|
@ -41,7 +41,7 @@ func (element *Document) Draw (destination artist.Canvas) {
|
|||
|
||||
tiles := shatter.Shatter(element.entity.Bounds(), rocks...)
|
||||
for _, tile := range tiles {
|
||||
element.entity.DrawBackground(artist.Cut(destination, tile))
|
||||
element.entity.DrawBackground(art.Cut(destination, tile))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,7 @@ func (element *Document) HandleChildFlexibleHeightChange (child ability.Flexible
|
|||
|
||||
// DrawBackground draws this element's background pattern to the specified
|
||||
// destination canvas.
|
||||
func (element *Document) DrawBackground (destination artist.Canvas) {
|
||||
func (element *Document) DrawBackground (destination art.Canvas) {
|
||||
element.entity.DrawBackground(destination)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import "io/fs"
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
var fileCase = tomo.C("files", "file")
|
||||
|
||||
|
@ -45,7 +45,7 @@ func (element *File) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *File) Draw (destination artist.Canvas) {
|
||||
func (element *File) Draw (destination art.Canvas) {
|
||||
// background
|
||||
state := element.state()
|
||||
bounds := element.entity.Bounds()
|
||||
|
@ -199,7 +199,7 @@ func (element *File) state () tomo.State {
|
|||
}
|
||||
}
|
||||
|
||||
func (element *File) icon () artist.Icon {
|
||||
func (element *File) icon () art.Icon {
|
||||
return element.entity.Theme().Icon(element.iconID, tomo.IconSizeLarge, fileCase)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ import "math"
|
|||
import "image"
|
||||
import "image/color"
|
||||
import "tomo"
|
||||
import "tomo/artist"
|
||||
import "tomo/artist/shapes"
|
||||
import "art"
|
||||
import "art/shapes"
|
||||
|
||||
var clockCase = tomo.C("tomo", "clock")
|
||||
|
||||
|
@ -30,7 +30,7 @@ func (element *AnalogClock) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *AnalogClock) Draw (destination artist.Canvas) {
|
||||
func (element *AnalogClock) Draw (destination art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
|
||||
state := tomo.State { }
|
||||
|
@ -71,7 +71,7 @@ func (element *AnalogClock) HandleThemeChange () {
|
|||
}
|
||||
|
||||
func (element *AnalogClock) radialLine (
|
||||
destination artist.Canvas,
|
||||
destination art.Canvas,
|
||||
source color.RGBA,
|
||||
inner float64,
|
||||
outer float64,
|
||||
|
|
|
@ -3,8 +3,8 @@ package fun
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "tomo/artist/artutil"
|
||||
import "art"
|
||||
import "art/artutil"
|
||||
import "tomo/elements/fun/music"
|
||||
|
||||
var pianoCase = tomo.C("tomo", "piano")
|
||||
|
@ -57,7 +57,7 @@ func (element *Piano) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Piano) Draw (destination artist.Canvas) {
|
||||
func (element *Piano) Draw (destination art.Canvas) {
|
||||
element.recalculate()
|
||||
|
||||
state := tomo.State {
|
||||
|
@ -304,7 +304,7 @@ func (element *Piano) recalculate () {
|
|||
}
|
||||
|
||||
func (element *Piano) drawFlat (
|
||||
destination artist.Canvas,
|
||||
destination art.Canvas,
|
||||
bounds image.Rectangle,
|
||||
pressed bool,
|
||||
state tomo.State,
|
||||
|
@ -315,7 +315,7 @@ func (element *Piano) drawFlat (
|
|||
}
|
||||
|
||||
func (element *Piano) drawSharp (
|
||||
destination artist.Canvas,
|
||||
destination art.Canvas,
|
||||
bounds image.Rectangle,
|
||||
pressed bool,
|
||||
state tomo.State,
|
||||
|
|
|
@ -2,7 +2,7 @@ package elements
|
|||
|
||||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
var iconCase = tomo.C("tomo", "icon")
|
||||
|
||||
|
@ -44,7 +44,7 @@ func (element *Icon) HandleThemeChange () {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Icon) Draw (destination artist.Canvas) {
|
||||
func (element *Icon) Draw (destination art.Canvas) {
|
||||
if element.entity == nil { return }
|
||||
|
||||
bounds := element.entity.Bounds()
|
||||
|
@ -65,7 +65,7 @@ func (element *Icon) Draw (destination artist.Canvas) {
|
|||
}
|
||||
}
|
||||
|
||||
func (element *Icon) icon () artist.Icon {
|
||||
func (element *Icon) icon () art.Icon {
|
||||
return element.entity.Theme().Icon(element.id, element.size, iconCase)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,20 +2,20 @@ package elements
|
|||
|
||||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/artist"
|
||||
import "tomo/artist/patterns"
|
||||
import "art"
|
||||
import "art/patterns"
|
||||
|
||||
// TODO: this element is lame need to make it better
|
||||
|
||||
// Image is an element capable of displaying an image.
|
||||
type Image struct {
|
||||
entity tomo.Entity
|
||||
buffer artist.Canvas
|
||||
buffer art.Canvas
|
||||
}
|
||||
|
||||
// NewImage creates a new image element.
|
||||
func NewImage (image image.Image) (element *Image) {
|
||||
element = &Image { buffer: artist.FromImage(image) }
|
||||
element = &Image { buffer: art.FromImage(image) }
|
||||
element.entity = tomo.GetBackend().NewEntity(element)
|
||||
bounds := element.buffer.Bounds()
|
||||
element.entity.SetMinimumSize(bounds.Dx(), bounds.Dy())
|
||||
|
@ -28,7 +28,7 @@ func (element *Image) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Image) Draw (destination artist.Canvas) {
|
||||
func (element *Image) Draw (destination art.Canvas) {
|
||||
if element.entity == nil { return }
|
||||
(patterns.Texture { Canvas: element.buffer }).
|
||||
Draw(destination, element.entity.Bounds())
|
||||
|
|
|
@ -5,7 +5,7 @@ import "golang.org/x/image/math/fixed"
|
|||
import "tomo"
|
||||
import "tomo/data"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/textdraw"
|
||||
|
||||
var labelCase = tomo.C("tomo", "label")
|
||||
|
@ -48,7 +48,7 @@ func (element *Label) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Label) Draw (destination artist.Canvas) {
|
||||
func (element *Label) Draw (destination art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
|
||||
if element.wrap {
|
||||
|
|
|
@ -3,9 +3,9 @@ package elements
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/ability"
|
||||
import "tomo/artist/artutil"
|
||||
import "art/artutil"
|
||||
|
||||
type list struct {
|
||||
container
|
||||
|
@ -61,7 +61,7 @@ func (element *list) init (children ...tomo.Element) {
|
|||
element.Adopt(children...)
|
||||
}
|
||||
|
||||
func (element *list) Draw (destination artist.Canvas) {
|
||||
func (element *list) Draw (destination art.Canvas) {
|
||||
rocks := make([]image.Rectangle, element.entity.CountChildren())
|
||||
for index := 0; index < element.entity.CountChildren(); index ++ {
|
||||
rocks[index] = element.entity.Child(index).Entity().Bounds()
|
||||
|
@ -274,7 +274,7 @@ func (element *list) HandleKeyDown (key input.Key, modifiers input.Modifiers) {
|
|||
|
||||
func (element *list) HandleKeyUp(key input.Key, modifiers input.Modifiers) { }
|
||||
|
||||
func (element *list) DrawBackground (destination artist.Canvas) {
|
||||
func (element *list) DrawBackground (destination art.Canvas) {
|
||||
element.entity.DrawBackground(destination)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package elements
|
|||
|
||||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
var progressBarCase = tomo.C("tomo", "progressBar")
|
||||
|
||||
|
@ -29,7 +29,7 @@ func (element *ProgressBar) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *ProgressBar) Draw (destination artist.Canvas) {
|
||||
func (element *ProgressBar) Draw (destination art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
|
||||
pattern := element.entity.Theme().Pattern(tomo.PatternSunken, tomo.State { }, progressBarCase)
|
||||
|
|
|
@ -3,7 +3,7 @@ package elements
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/ability"
|
||||
|
||||
var scrollCase = tomo.C("tomo", "scroll")
|
||||
|
@ -76,7 +76,7 @@ func (element *Scroll) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Scroll) Draw (destination artist.Canvas) {
|
||||
func (element *Scroll) Draw (destination art.Canvas) {
|
||||
if element.horizontal != nil && element.vertical != nil {
|
||||
bounds := element.entity.Bounds()
|
||||
bounds.Min = image.Pt (
|
||||
|
@ -84,7 +84,7 @@ func (element *Scroll) Draw (destination artist.Canvas) {
|
|||
bounds.Max.Y - element.horizontal.Entity().Bounds().Dy())
|
||||
state := tomo.State { }
|
||||
deadArea := element.entity.Theme().Pattern(tomo.PatternDead, state, scrollCase)
|
||||
deadArea.Draw(artist.Cut(destination, bounds), bounds)
|
||||
deadArea.Draw(art.Cut(destination, bounds), bounds)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ func (element *Scroll) Layout () {
|
|||
|
||||
// DrawBackground draws this element's background pattern to the specified
|
||||
// destination canvas.
|
||||
func (element *Scroll) DrawBackground (destination artist.Canvas) {
|
||||
func (element *Scroll) DrawBackground (destination art.Canvas) {
|
||||
element.entity.DrawBackground(destination)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ package elements
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
// ScrollBar is an element similar to Slider, but it has special behavior that
|
||||
// makes it well suited for controlling the viewport position on one axis of a
|
||||
|
@ -63,7 +63,7 @@ func (element *ScrollBar) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *ScrollBar) Draw (destination artist.Canvas) {
|
||||
func (element *ScrollBar) Draw (destination art.Canvas) {
|
||||
element.recalculate()
|
||||
|
||||
bounds := element.entity.Bounds()
|
||||
|
|
|
@ -3,7 +3,7 @@ package elements
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
// Slider is a slider control with a floating point value between zero and one.
|
||||
type Slider struct {
|
||||
|
@ -59,7 +59,7 @@ func (element *slider) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *slider) Draw (destination artist.Canvas) {
|
||||
func (element *slider) Draw (destination art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
element.track = element.entity.Theme().Padding(tomo.PatternGutter, element.c).Apply(bounds)
|
||||
if element.vertical {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package elements
|
||||
|
||||
import "tomo"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
var spacerCase = tomo.C("tomo", "spacer")
|
||||
|
||||
|
@ -32,7 +32,7 @@ func (element *Spacer) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Spacer) Draw (destination artist.Canvas) {
|
||||
func (element *Spacer) Draw (destination art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
|
||||
if element.line {
|
||||
|
|
|
@ -3,7 +3,7 @@ package elements
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/textdraw"
|
||||
|
||||
var switchCase = tomo.C("tomo", "switch")
|
||||
|
@ -44,7 +44,7 @@ func (element *Switch) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *Switch) Draw (destination artist.Canvas) {
|
||||
func (element *Switch) Draw (destination art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
handleBounds := image.Rect(0, 0, bounds.Dy(), bounds.Dy()).Add(bounds.Min)
|
||||
gutterBounds := image.Rect(0, 0, bounds.Dy() * 2, bounds.Dy()).Add(bounds.Min)
|
||||
|
|
|
@ -5,20 +5,20 @@ import "time"
|
|||
import "image"
|
||||
import "image/color"
|
||||
import "tomo"
|
||||
import "tomo/artist"
|
||||
import "tomo/shatter"
|
||||
import "art"
|
||||
import "art/shatter"
|
||||
import "tomo/textdraw"
|
||||
import "tomo/artist/shapes"
|
||||
import "tomo/artist/artutil"
|
||||
import "tomo/artist/patterns"
|
||||
import "art/shapes"
|
||||
import "art/artutil"
|
||||
import "art/patterns"
|
||||
|
||||
// Artist is an element that displays shapes and patterns drawn by the artist
|
||||
// Artist is an element that displays shapes and patterns drawn by the art
|
||||
// package in order to test it.
|
||||
type Artist struct {
|
||||
entity tomo.Entity
|
||||
}
|
||||
|
||||
// NewArtist creates a new artist test element.
|
||||
// NewArtist creates a new art test element.
|
||||
func NewArtist () (element *Artist) {
|
||||
element = &Artist { }
|
||||
element.entity = tomo.GetBackend().NewEntity(element)
|
||||
|
@ -30,7 +30,7 @@ func (element *Artist) Entity () tomo.Entity {
|
|||
return element.entity
|
||||
}
|
||||
|
||||
func (element *Artist) Draw (destination artist.Canvas) {
|
||||
func (element *Artist) Draw (destination art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
patterns.Uhex(0x000000FF).Draw(destination, bounds)
|
||||
|
||||
|
@ -81,7 +81,7 @@ func (element *Artist) Draw (destination artist.Canvas) {
|
|||
}
|
||||
tiles := shatter.Shatter(c41.Bounds(), rocks...)
|
||||
for index, tile := range tiles {
|
||||
[]artist.Pattern {
|
||||
[]art.Pattern {
|
||||
patterns.Uhex(0xFF0000FF),
|
||||
patterns.Uhex(0x00FF00FF),
|
||||
patterns.Uhex(0xFF00FFFF),
|
||||
|
@ -115,26 +115,26 @@ func (element *Artist) Draw (destination artist.Canvas) {
|
|||
c03 := element.cellAt(destination, 0, 3)
|
||||
patterns.Border {
|
||||
Canvas: element.thingy(c42),
|
||||
Inset: artist.Inset { 8, 8, 8, 8 },
|
||||
Inset: art.Inset { 8, 8, 8, 8 },
|
||||
}.Draw(c03, c03.Bounds())
|
||||
|
||||
// 1, 3
|
||||
c13 := element.cellAt(destination, 1, 3)
|
||||
patterns.Border {
|
||||
Canvas: element.thingy(c42),
|
||||
Inset: artist.Inset { 8, 8, 8, 8 },
|
||||
Inset: art.Inset { 8, 8, 8, 8 },
|
||||
}.Draw(c13, c13.Bounds().Inset(10))
|
||||
|
||||
// 2, 3
|
||||
c23 := element.cellAt(destination, 2, 3)
|
||||
patterns.Border {
|
||||
Canvas: element.thingy(c42),
|
||||
Inset: artist.Inset { 8, 8, 8, 8 },
|
||||
Inset: art.Inset { 8, 8, 8, 8 },
|
||||
}.Draw(c23, c23.Bounds())
|
||||
patterns.Border {
|
||||
Canvas: element.thingy(c42),
|
||||
Inset: artist.Inset { 8, 8, 8, 8 },
|
||||
}.Draw(artist.Cut(c23, c23.Bounds().Inset(16)), c23.Bounds())
|
||||
Inset: art.Inset { 8, 8, 8, 8 },
|
||||
}.Draw(art.Cut(c23, c23.Bounds().Inset(16)), c23.Bounds())
|
||||
|
||||
// how long did that take to render?
|
||||
drawTime := time.Since(drawStart)
|
||||
|
@ -142,7 +142,7 @@ func (element *Artist) Draw (destination artist.Canvas) {
|
|||
textDrawer.SetFace(element.entity.Theme().FontFace (
|
||||
tomo.FontStyleRegular,
|
||||
tomo.FontSizeNormal,
|
||||
tomo.C("tomo", "artist")))
|
||||
tomo.C("tomo", "art")))
|
||||
textDrawer.SetText ([]rune (fmt.Sprintf (
|
||||
"%dms\n%dus",
|
||||
drawTime.Milliseconds(),
|
||||
|
@ -152,7 +152,7 @@ func (element *Artist) Draw (destination artist.Canvas) {
|
|||
image.Pt(bounds.Min.X + 8, bounds.Max.Y - 24))
|
||||
}
|
||||
|
||||
func (element *Artist) colorLines (destination artist.Canvas, weight int, bounds image.Rectangle) {
|
||||
func (element *Artist) colorLines (destination art.Canvas, weight int, bounds image.Rectangle) {
|
||||
bounds = bounds.Inset(4)
|
||||
c := artutil.Hex(0xFFFFFFFF)
|
||||
shapes.ColorLine(destination, c, weight, bounds.Min, bounds.Max)
|
||||
|
@ -186,18 +186,18 @@ func (element *Artist) colorLines (destination artist.Canvas, weight int, bounds
|
|||
image.Pt(bounds.Min.X + bounds.Dx() / 2, bounds.Max.Y))
|
||||
}
|
||||
|
||||
func (element *Artist) cellAt (destination artist.Canvas, x, y int) (artist.Canvas) {
|
||||
func (element *Artist) cellAt (destination art.Canvas, x, y int) (art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
cellBounds := image.Rectangle { }
|
||||
cellBounds.Min = bounds.Min
|
||||
cellBounds.Max.X = bounds.Min.X + bounds.Dx() / 5
|
||||
cellBounds.Max.Y = bounds.Min.Y + (bounds.Dy() - 48) / 4
|
||||
return artist.Cut (destination, cellBounds.Add (image.Pt (
|
||||
return art.Cut (destination, cellBounds.Add (image.Pt (
|
||||
x * cellBounds.Dx(),
|
||||
y * cellBounds.Dy())))
|
||||
}
|
||||
|
||||
func (element *Artist) thingy (destination artist.Canvas) (result artist.Canvas) {
|
||||
func (element *Artist) thingy (destination art.Canvas) (result art.Canvas) {
|
||||
bounds := destination.Bounds()
|
||||
bounds = image.Rect(0, 0, 32, 32).Add(bounds.Min)
|
||||
shapes.FillColorRectangle(destination, artutil.Hex(0x440000FF), bounds)
|
||||
|
@ -205,5 +205,5 @@ func (element *Artist) thingy (destination artist.Canvas) (result artist.Canvas)
|
|||
shapes.StrokeColorRectangle(destination, artutil.Hex(0x004400FF), bounds.Inset(4), 1)
|
||||
shapes.FillColorRectangle(destination, artutil.Hex(0x004444FF), bounds.Inset(12))
|
||||
shapes.StrokeColorRectangle(destination, artutil.Hex(0x888888FF), bounds.Inset(8), 1)
|
||||
return artist.Cut(destination, bounds)
|
||||
return art.Cut(destination, bounds)
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@ package testing
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "tomo/artist/shapes"
|
||||
import "tomo/artist/artutil"
|
||||
import "art"
|
||||
import "art/shapes"
|
||||
import "art/artutil"
|
||||
|
||||
var mouseCase = tomo.C("tomo", "mouse")
|
||||
|
||||
|
@ -29,7 +29,7 @@ func (element *Mouse) Entity () tomo.Entity {
|
|||
return element.entity
|
||||
}
|
||||
|
||||
func (element *Mouse) Draw (destination artist.Canvas) {
|
||||
func (element *Mouse) Draw (destination art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
accent := element.entity.Theme().Color (
|
||||
tomo.ColorAccent,
|
||||
|
|
|
@ -6,11 +6,11 @@ import "image"
|
|||
import "tomo"
|
||||
import "tomo/data"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/textdraw"
|
||||
import "tomo/textmanip"
|
||||
import "tomo/fixedutil"
|
||||
import "tomo/artist/shapes"
|
||||
import "art/shapes"
|
||||
|
||||
var textBoxCase = tomo.C("tomo", "textBox")
|
||||
|
||||
|
@ -60,13 +60,13 @@ func (element *TextBox) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *TextBox) Draw (destination artist.Canvas) {
|
||||
func (element *TextBox) Draw (destination art.Canvas) {
|
||||
bounds := element.entity.Bounds()
|
||||
|
||||
state := element.state()
|
||||
pattern := element.entity.Theme().Pattern(tomo.PatternInput, state, textBoxCase)
|
||||
padding := element.entity.Theme().Padding(tomo.PatternInput, textBoxCase)
|
||||
innerCanvas := artist.Cut(destination, padding.Apply(bounds))
|
||||
innerCanvas := art.Cut(destination, padding.Apply(bounds))
|
||||
pattern.Draw(destination, bounds)
|
||||
offset := element.textOffset()
|
||||
|
||||
|
@ -208,8 +208,8 @@ func (element *TextBox) textOffset () image.Point {
|
|||
innerBounds := padding.Apply(bounds)
|
||||
textHeight := element.valueDrawer.LineHeight().Round()
|
||||
return bounds.Min.Add (image.Pt (
|
||||
padding[artist.SideLeft] - element.scroll,
|
||||
padding[artist.SideTop] + (innerBounds.Dy() - textHeight) / 2))
|
||||
padding[art.SideLeft] - element.scroll,
|
||||
padding[art.SideTop] + (innerBounds.Dy() - textHeight) / 2))
|
||||
}
|
||||
|
||||
func (element *TextBox) atPosition (position image.Point) int {
|
||||
|
|
|
@ -3,7 +3,7 @@ package elements
|
|||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/input"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/textdraw"
|
||||
|
||||
var toggleButtonCase = tomo.C("tomo", "toggleButton")
|
||||
|
@ -47,7 +47,7 @@ func (element *ToggleButton) Entity () tomo.Entity {
|
|||
}
|
||||
|
||||
// Draw causes the element to draw to the specified destination canvas.
|
||||
func (element *ToggleButton) Draw (destination artist.Canvas) {
|
||||
func (element *ToggleButton) Draw (destination art.Canvas) {
|
||||
state := element.state()
|
||||
bounds := element.entity.Bounds()
|
||||
pattern := element.entity.Theme().Pattern(tomo.PatternButton, state, toggleButtonCase)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package tomo
|
||||
|
||||
import "image"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
// Entity is a handle given to elements by the backend. Extended entity
|
||||
// interfaces are defined in the ability module.
|
||||
|
@ -33,7 +33,7 @@ type Entity interface {
|
|||
// labels. If there is no parent element (that is, the element is
|
||||
// directly inside of the window), the backend will draw a default
|
||||
// background pattern.
|
||||
DrawBackground (artist.Canvas)
|
||||
DrawBackground (art.Canvas)
|
||||
|
||||
// --- Behaviors relating to parenting ---
|
||||
|
||||
|
|
3
go.mod
3
go.mod
|
@ -2,7 +2,10 @@ module tomo
|
|||
|
||||
go 1.19
|
||||
|
||||
replace art => git.tebibyte.media/tomo/art v1.0.0
|
||||
|
||||
require (
|
||||
art v1.0.0
|
||||
git.tebibyte.media/sashakoshka/ezprof v0.0.0-20230309044548-401cba83602b
|
||||
github.com/jezek/xgbutil v0.0.0-20230403164920-e2f86723ca07
|
||||
golang.org/x/image v0.7.0
|
||||
|
|
2
go.sum
2
go.sum
|
@ -1,5 +1,7 @@
|
|||
git.tebibyte.media/sashakoshka/ezprof v0.0.0-20230309044548-401cba83602b h1:vPFKR7vjN1VrMdMtpATMrKQobz/cqbPiRrA1EbtG6PM=
|
||||
git.tebibyte.media/sashakoshka/ezprof v0.0.0-20230309044548-401cba83602b/go.mod h1:cpXX8SAUDEvZX5m7scoyruavUhEqQ1SByfWzPFHkTbg=
|
||||
git.tebibyte.media/tomo/art v1.0.0 h1:dnP19Irgiho5WH0Im0l5vFYj5sDh4nqVk8hvG+6kW4M=
|
||||
git.tebibyte.media/tomo/art v1.0.0/go.mod h1:pDQKkKWv/81CLTyH743zCzRWiri05+/4H6eNugarzvE=
|
||||
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298 h1:1qlsVAQJXZHsaM8b6OLVo6muQUQd4CwkH/D3fnnbHXA=
|
||||
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ=
|
||||
github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966 h1:lTG4HQym5oPKjL7nGs+csTgiDna685ZXjxijkne828g=
|
||||
|
|
|
@ -9,14 +9,14 @@ import "golang.org/x/image/font"
|
|||
import "golang.org/x/image/font/basicfont"
|
||||
import "tomo"
|
||||
import "tomo/data"
|
||||
import "tomo/artist"
|
||||
import "tomo/artist/artutil"
|
||||
import "tomo/artist/patterns"
|
||||
import "art"
|
||||
import "art/artutil"
|
||||
import "art/patterns"
|
||||
|
||||
//go:embed assets/wintergreen.png
|
||||
var defaultAtlasBytes []byte
|
||||
var defaultAtlas artist.Canvas
|
||||
var defaultTextures [17][9]artist.Pattern
|
||||
var defaultAtlas art.Canvas
|
||||
var defaultTextures [17][9]art.Pattern
|
||||
//go:embed assets/wintergreen-icons-small.png
|
||||
var defaultIconsSmallAtlasBytes []byte
|
||||
var defaultIconsSmall [640]binaryIcon
|
||||
|
@ -24,15 +24,15 @@ var defaultIconsSmall [640]binaryIcon
|
|||
var defaultIconsLargeAtlasBytes []byte
|
||||
var defaultIconsLarge [640]binaryIcon
|
||||
|
||||
func atlasCell (col, row int, border artist.Inset) {
|
||||
func atlasCell (col, row int, border art.Inset) {
|
||||
bounds := image.Rect(0, 0, 16, 16).Add(image.Pt(col, row).Mul(16))
|
||||
defaultTextures[col][row] = patterns.Border {
|
||||
Canvas: artist.Cut(defaultAtlas, bounds),
|
||||
Canvas: art.Cut(defaultAtlas, bounds),
|
||||
Inset: border,
|
||||
}
|
||||
}
|
||||
|
||||
func atlasCol (col int, border artist.Inset) {
|
||||
func atlasCol (col int, border art.Inset) {
|
||||
for index, _ := range defaultTextures[col] {
|
||||
atlasCell(col, index, border)
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ type binaryIcon struct {
|
|||
stride int
|
||||
}
|
||||
|
||||
func (icon binaryIcon) Draw (destination artist.Canvas, color color.RGBA, at image.Point) {
|
||||
func (icon binaryIcon) Draw (destination art.Canvas, color color.RGBA, at image.Point) {
|
||||
bounds := icon.Bounds().Add(at).Intersect(destination.Bounds())
|
||||
point := image.Point { }
|
||||
data, stride := destination.Buffer()
|
||||
|
@ -85,43 +85,43 @@ func binaryIconFrom (source image.Image, clip image.Rectangle) (icon binaryIcon)
|
|||
|
||||
func init () {
|
||||
defaultAtlasImage, _, _ := image.Decode(bytes.NewReader(defaultAtlasBytes))
|
||||
defaultAtlas = artist.FromImage(defaultAtlasImage)
|
||||
defaultAtlas = art.FromImage(defaultAtlasImage)
|
||||
|
||||
// PatternDead
|
||||
atlasCol(0, artist.Inset { })
|
||||
atlasCol(0, art.Inset { })
|
||||
// PatternRaised
|
||||
atlasCol(1, artist.Inset { 6, 6, 6, 6 })
|
||||
atlasCol(1, art.Inset { 6, 6, 6, 6 })
|
||||
// PatternSunken
|
||||
atlasCol(2, artist.Inset { 4, 4, 4, 4 })
|
||||
atlasCol(2, art.Inset { 4, 4, 4, 4 })
|
||||
// PatternPinboard
|
||||
atlasCol(3, artist.Inset { 2, 2, 2, 2 })
|
||||
atlasCol(3, art.Inset { 2, 2, 2, 2 })
|
||||
// PatternButton
|
||||
atlasCol(4, artist.Inset { 6, 6, 6, 6 })
|
||||
atlasCol(4, art.Inset { 6, 6, 6, 6 })
|
||||
// PatternInput
|
||||
atlasCol(5, artist.Inset { 4, 4, 4, 4 })
|
||||
atlasCol(5, art.Inset { 4, 4, 4, 4 })
|
||||
// PatternGutter
|
||||
atlasCol(6, artist.Inset { 7, 7, 7, 7 })
|
||||
atlasCol(6, art.Inset { 7, 7, 7, 7 })
|
||||
// PatternHandle
|
||||
atlasCol(7, artist.Inset { 3, 3, 3, 3 })
|
||||
atlasCol(7, art.Inset { 3, 3, 3, 3 })
|
||||
// PatternLine
|
||||
atlasCol(8, artist.Inset { 1, 1, 1, 1 })
|
||||
atlasCol(8, art.Inset { 1, 1, 1, 1 })
|
||||
// PatternMercury
|
||||
atlasCol(13, artist.Inset { 2, 2, 2, 2 })
|
||||
atlasCol(13, art.Inset { 2, 2, 2, 2 })
|
||||
// PatternTableHead:
|
||||
atlasCol(14, artist.Inset { 4, 4, 4, 4 })
|
||||
atlasCol(14, art.Inset { 4, 4, 4, 4 })
|
||||
// PatternTableCell:
|
||||
atlasCol(15, artist.Inset { 4, 4, 4, 4 })
|
||||
atlasCol(15, art.Inset { 4, 4, 4, 4 })
|
||||
// PatternLamp:
|
||||
atlasCol(16, artist.Inset { 4, 3, 4, 3 })
|
||||
atlasCol(16, art.Inset { 4, 3, 4, 3 })
|
||||
|
||||
// PatternButton: basic.checkbox
|
||||
atlasCol(9, artist.Inset { 3, 3, 3, 3 })
|
||||
atlasCol(9, art.Inset { 3, 3, 3, 3 })
|
||||
// PatternRaised: basic.listEntry
|
||||
atlasCol(10, artist.Inset { 3, 3, 3, 3 })
|
||||
atlasCol(10, art.Inset { 3, 3, 3, 3 })
|
||||
// PatternRaised: fun.flatKey
|
||||
atlasCol(11, artist.Inset { 3, 3, 5, 3 })
|
||||
atlasCol(11, art.Inset { 3, 3, 5, 3 })
|
||||
// PatternRaised: fun.sharpKey
|
||||
atlasCol(12, artist.Inset { 3, 3, 4, 3 })
|
||||
atlasCol(12, art.Inset { 3, 3, 4, 3 })
|
||||
|
||||
// set up small icons
|
||||
defaultIconsSmallAtlasImage, _, _ := image.Decode (
|
||||
|
@ -164,7 +164,7 @@ func (Theme) FontFace (style tomo.FontStyle, size tomo.FontSize, c tomo.Case) fo
|
|||
return basicfont.Face7x13
|
||||
}
|
||||
|
||||
func (Theme) Icon (id tomo.Icon, size tomo.IconSize, c tomo.Case) artist.Icon {
|
||||
func (Theme) Icon (id tomo.Icon, size tomo.IconSize, c tomo.Case) art.Icon {
|
||||
if size == tomo.IconSizeLarge {
|
||||
if id < 0 || int(id) >= len(defaultIconsLarge) {
|
||||
return nil
|
||||
|
@ -180,12 +180,12 @@ func (Theme) Icon (id tomo.Icon, size tomo.IconSize, c tomo.Case) artist.Icon {
|
|||
}
|
||||
}
|
||||
|
||||
func (Theme) MimeIcon (data.Mime, tomo.IconSize, tomo.Case) artist.Icon {
|
||||
func (Theme) MimeIcon (data.Mime, tomo.IconSize, tomo.Case) art.Icon {
|
||||
// TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
func (Theme) Pattern (id tomo.Pattern, state tomo.State, c tomo.Case) artist.Pattern {
|
||||
func (Theme) Pattern (id tomo.Pattern, state tomo.State, c tomo.Case) art.Pattern {
|
||||
offset := 0; switch {
|
||||
case state.Disabled: offset = 1
|
||||
case state.Pressed && state.On: offset = 4
|
||||
|
@ -254,31 +254,31 @@ func (Theme) Color (id tomo.Color, state tomo.State, c tomo.Case) color.RGBA {
|
|||
} [id])
|
||||
}
|
||||
|
||||
func (Theme) Padding (id tomo.Pattern, c tomo.Case) artist.Inset {
|
||||
func (Theme) Padding (id tomo.Pattern, c tomo.Case) art.Inset {
|
||||
switch id {
|
||||
case tomo.PatternSunken:
|
||||
if c.Match("tomo", "progressBar", "") {
|
||||
return artist.I(2, 1, 1, 2)
|
||||
return art.I(2, 1, 1, 2)
|
||||
} else if c.Match("tomo", "list", "") {
|
||||
return artist.I(2)
|
||||
return art.I(2)
|
||||
} else if c.Match("tomo", "flowList", "") {
|
||||
return artist.I(2)
|
||||
return art.I(2)
|
||||
} else {
|
||||
return artist.I(8)
|
||||
return art.I(8)
|
||||
}
|
||||
case tomo.PatternPinboard:
|
||||
if c.Match("tomo", "piano", "") {
|
||||
return artist.I(2)
|
||||
return art.I(2)
|
||||
} else {
|
||||
return artist.I(8)
|
||||
return art.I(8)
|
||||
}
|
||||
case tomo.PatternTableCell: return artist.I(5)
|
||||
case tomo.PatternTableHead: return artist.I(5)
|
||||
case tomo.PatternGutter: return artist.I(0)
|
||||
case tomo.PatternLine: return artist.I(1)
|
||||
case tomo.PatternMercury: return artist.I(5)
|
||||
case tomo.PatternLamp: return artist.I(5, 5, 5, 6)
|
||||
default: return artist.I(8)
|
||||
case tomo.PatternTableCell: return art.I(5)
|
||||
case tomo.PatternTableHead: return art.I(5)
|
||||
case tomo.PatternGutter: return art.I(0)
|
||||
case tomo.PatternLine: return art.I(1)
|
||||
case tomo.PatternMercury: return art.I(5)
|
||||
case tomo.PatternLamp: return art.I(5, 5, 5, 6)
|
||||
default: return art.I(8)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package x
|
|||
|
||||
import "image"
|
||||
import "tomo"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/ability"
|
||||
|
||||
type entity struct {
|
||||
|
@ -159,7 +159,7 @@ func (entity *entity) SetMinimumSize (width, height int) {
|
|||
}
|
||||
}
|
||||
|
||||
func (entity *entity) DrawBackground (destination artist.Canvas) {
|
||||
func (entity *entity) DrawBackground (destination art.Canvas) {
|
||||
if entity.parent != nil {
|
||||
entity.parent.element.(ability.Container).DrawBackground(destination)
|
||||
} else if entity.window != nil {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package x
|
||||
|
||||
import "image"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
import "tomo/ability"
|
||||
|
||||
type entitySet map[*entity] struct { }
|
||||
|
@ -22,7 +22,7 @@ func (set entitySet) Add (entity *entity) {
|
|||
type system struct {
|
||||
child *entity
|
||||
focused *entity
|
||||
canvas artist.BasicCanvas
|
||||
canvas art.BasicCanvas
|
||||
|
||||
invalidateIgnore bool
|
||||
drawingInvalid entitySet
|
||||
|
@ -167,7 +167,7 @@ func (system *system) draw () {
|
|||
|
||||
for entity := range system.drawingInvalid {
|
||||
if entity.clippedBounds.Empty() { continue }
|
||||
entity.element.Draw (artist.Cut (
|
||||
entity.element.Draw (art.Cut (
|
||||
system.canvas,
|
||||
entity.clippedBounds))
|
||||
finalBounds = finalBounds.Union(entity.clippedBounds)
|
||||
|
|
|
@ -13,7 +13,7 @@ import "github.com/jezek/xgbutil/mousebind"
|
|||
import "github.com/jezek/xgbutil/xgraphics"
|
||||
import "tomo"
|
||||
import "tomo/data"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
type mainWindow struct { *window }
|
||||
type menuWindow struct { *window }
|
||||
|
@ -414,7 +414,7 @@ func (window *window) pasteAndPush (region image.Rectangle) {
|
|||
}
|
||||
|
||||
func (window *window) paste (region image.Rectangle) {
|
||||
canvas := artist.Cut(window.canvas, region)
|
||||
canvas := art.Cut(window.canvas, region)
|
||||
data, stride := canvas.Buffer()
|
||||
bounds := canvas.Bounds().Intersect(window.xCanvas.Bounds())
|
||||
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
// Package shatter provides boolean operations for image.Rectangle.
|
||||
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:]...)
|
||||
}
|
|
@ -5,7 +5,7 @@ import "unicode"
|
|||
import "image/draw"
|
||||
import "image/color"
|
||||
import "golang.org/x/image/math/fixed"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
// Drawer is an extended TypeSetter that is able to draw text. Much like
|
||||
// TypeSetter, It has no constructor and its zero value can be used safely.
|
||||
|
@ -13,7 +13,7 @@ type Drawer struct { TypeSetter }
|
|||
|
||||
// Draw draws the drawer's text onto the specified canvas at the given offset.
|
||||
func (drawer Drawer) Draw (
|
||||
destination artist.Canvas,
|
||||
destination art.Canvas,
|
||||
color color.RGBA,
|
||||
offset image.Point,
|
||||
) (
|
||||
|
|
12
theme.go
12
theme.go
|
@ -4,7 +4,7 @@ import "image"
|
|||
import "image/color"
|
||||
import "golang.org/x/image/font"
|
||||
import "tomo/data"
|
||||
import "tomo/artist"
|
||||
import "art"
|
||||
|
||||
// Color lits a number of cannonical colors, each with its own ID.
|
||||
type Color int; const (
|
||||
|
@ -309,7 +309,7 @@ type Hints struct {
|
|||
// StaticInset defines an inset rectangular area in the middle of the
|
||||
// pattern that does not change between PatternStates. If the inset is
|
||||
// zero on all sides, this hint does not apply.
|
||||
StaticInset artist.Inset
|
||||
StaticInset art.Inset
|
||||
|
||||
// Uniform specifies a singular color for the entire pattern. If the
|
||||
// alpha channel is zero, this hint does not apply.
|
||||
|
@ -322,15 +322,15 @@ type Theme interface {
|
|||
FontFace (FontStyle, FontSize, Case) font.Face
|
||||
|
||||
// Icon returns an appropriate icon given an icon name, size, and case.
|
||||
Icon (Icon, IconSize, Case) artist.Icon
|
||||
Icon (Icon, IconSize, Case) art.Icon
|
||||
|
||||
// Icon returns an appropriate icon given a file mime type, size, and,
|
||||
// case.
|
||||
MimeIcon (data.Mime, IconSize, Case) artist.Icon
|
||||
MimeIcon (data.Mime, IconSize, Case) art.Icon
|
||||
|
||||
// Pattern returns an appropriate pattern given a pattern name, case,
|
||||
// and state.
|
||||
Pattern (Pattern, State, Case) artist.Pattern
|
||||
Pattern (Pattern, State, Case) art.Pattern
|
||||
|
||||
// Color returns an appropriate pattern given a color name, case, and
|
||||
// state.
|
||||
|
@ -338,7 +338,7 @@ type Theme interface {
|
|||
|
||||
// Padding returns how much space should be between the bounds of a
|
||||
// pattern whatever an element draws inside of it.
|
||||
Padding (Pattern, Case) artist.Inset
|
||||
Padding (Pattern, Case) art.Inset
|
||||
|
||||
// Margin returns the left/right (x) and top/bottom (y) margins that
|
||||
// should be put between any self-contained objects drawn within this
|
||||
|
|
Reference in New Issue