whee back in busineess

This commit is contained in:
Sasha Koshka 2023-02-26 14:27:38 -05:00
parent 2859dc3313
commit 241c297626
9 changed files with 151 additions and 350 deletions

12
artist/color.go Normal file
View File

@ -0,0 +1,12 @@
package artist
import "image/color"
// 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
}

View File

@ -3,6 +3,7 @@ package patterns
import "image" import "image"
import "image/color" import "image/color"
import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/canvas"
import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/artist/shapes" import "git.tebibyte.media/sashakoshka/tomo/artist/shapes"
// Uniform is a pattern that draws a solid color. // Uniform is a pattern that draws a solid color.
@ -15,13 +16,5 @@ func (pattern Uniform) Draw (destination canvas.Canvas, clip image.Rectangle) {
// Uhex creates a new Uniform pattern from an RGBA integer value. // Uhex creates a new Uniform pattern from an RGBA integer value.
func Uhex (color uint32) (uniform Uniform) { func Uhex (color uint32) (uniform Uniform) {
return Uniform(hex(color)) return Uniform(artist.Hex(color))
}
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
} }

View File

@ -5,23 +5,23 @@ import "image"
import "image/color" import "image/color"
import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/canvas"
// FillEllipse draws the content of one canvas onto another, clipped by an // TODO: redo fill ellipse, stroke ellipse, etc. so that it only takes in
// ellipse stretched to the bounds of the source canvas. The offset point // destination and source, using the bounds of destination as the bounds of the
// defines where the origin point of the source canvas is positioned in relation // ellipse and the bounds of source as the "clipping rectangle". Line up the Min
// to the origin point of the destination canvas. To prevent the entire source // of both canvases.
// canvas's bounds from being used, it must be cut with canvas.Cut().
func FillEllipse ( func FillEllipse (
destination canvas.Canvas, destination canvas.Canvas,
source canvas.Canvas, source canvas.Canvas,
offset image.Point,
) ( ) (
updatedRegion image.Rectangle, updatedRegion image.Rectangle,
) { ) {
dstData, dstStride := destination.Buffer() dstData, dstStride := destination.Buffer()
srcData, srcStride := source.Buffer() srcData, srcStride := source.Buffer()
bounds := source.Bounds().Intersect(destination.Bounds()).Canon() offset := source.Bounds().Min.Sub(destination.Bounds().Min)
realBounds := source.Bounds() bounds := source.Bounds().Sub(offset).Intersect(destination.Bounds())
realBounds := destination.Bounds()
if bounds.Empty() { return } if bounds.Empty() { return }
updatedRegion = bounds updatedRegion = bounds
@ -30,22 +30,17 @@ func FillEllipse (
for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ { for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ {
if inEllipse(point, realBounds) { if inEllipse(point, realBounds) {
offsetPoint := point.Add(offset) offsetPoint := point.Add(offset)
dstIndex := offsetPoint.X + (offsetPoint.Y) * dstStride dstIndex := point.X + point.Y * dstStride
srcIndex := point.X + point.Y * srcStride srcIndex := offsetPoint.X + offsetPoint.Y * srcStride
dstData[dstIndex] = srcData[srcIndex] dstData[dstIndex] = srcData[srcIndex]
} }
}} }}
return return
} }
// StrokeEllipse is similar to FillEllipse, but it draws an elliptical inset
// outline of the source canvas onto the destination canvas. To prevent the
// entire source canvas's bounds from being used, it must be cut with
// canvas.Cut().
func StrokeEllipse ( func StrokeEllipse (
destination canvas.Canvas, destination canvas.Canvas,
source canvas.Canvas, source canvas.Canvas,
offset image.Point,
weight int, weight int,
) { ) {
if weight < 1 { return } if weight < 1 { return }
@ -53,7 +48,10 @@ func StrokeEllipse (
dstData, dstStride := destination.Buffer() dstData, dstStride := destination.Buffer()
srcData, srcStride := source.Buffer() srcData, srcStride := source.Buffer()
bounds := source.Bounds().Inset(weight - 1) bounds := destination.Bounds().Inset(weight - 1)
offset := source.Bounds().Min.Sub(destination.Bounds().Min)
realBounds := destination.Bounds()
if bounds.Empty() { return }
context := ellipsePlottingContext { context := ellipsePlottingContext {
plottingContext: plottingContext { plottingContext: plottingContext {
@ -63,9 +61,9 @@ func StrokeEllipse (
srcStride: srcStride, srcStride: srcStride,
weight: weight, weight: weight,
offset: offset, offset: offset,
bounds: bounds.Intersect(destination.Bounds()), bounds: realBounds,
}, },
radii: image.Pt(bounds.Dx() / 2 - 1, bounds.Dy() / 2 - 1), radii: image.Pt(bounds.Dx() / 2, bounds.Dy() / 2),
} }
context.center = bounds.Min.Add(context.radii) context.center = bounds.Min.Add(context.radii)
context.plotEllipse() context.plotEllipse()
@ -205,7 +203,7 @@ func StrokeColorEllipse (
if weight < 1 { return } if weight < 1 { return }
dstData, dstStride := destination.Buffer() dstData, dstStride := destination.Buffer()
bounds = bounds.Inset(weight - 1) insetBounds := bounds.Inset(weight - 1)
context := ellipsePlottingContext { context := ellipsePlottingContext {
plottingContext: plottingContext { plottingContext: plottingContext {
@ -215,9 +213,9 @@ func StrokeColorEllipse (
weight: weight, weight: weight,
bounds: bounds.Intersect(destination.Bounds()), bounds: bounds.Intersect(destination.Bounds()),
}, },
radii: image.Pt(bounds.Dx() / 2 - 1, bounds.Dy() / 2 - 1), radii: image.Pt(insetBounds.Dx() / 2, insetBounds.Dy() / 2),
} }
context.center = bounds.Min.Add(context.radii) context.center = insetBounds.Min.Add(context.radii)
context.plotEllipse() context.plotEllipse()
return return
} }

View File

@ -16,32 +16,31 @@ type plottingContext struct {
bounds image.Rectangle bounds image.Rectangle
} }
func (context plottingContext) square (center image.Point) image.Rectangle { func (context plottingContext) square (center image.Point) (square image.Rectangle) {
return image.Rect(0, 0, context.weight, context.weight). return image.Rect(0, 0, context.weight, context.weight).
Sub(image.Pt(context.weight / 2, context.weight / 2)). Sub(image.Pt(context.weight / 2, context.weight / 2)).
Add(center). Add(center).
Add(context.offset).
Intersect(context.bounds) Intersect(context.bounds)
} }
func (context plottingContext) plotColor (center image.Point) { func (context plottingContext) plotColor (center image.Point) {
square := context.square(center) square := context.square(center)
for y := square.Min.Y; y < square.Min.Y; y ++ { for y := square.Min.Y; y < square.Max.Y; y ++ {
for x := square.Min.X; x < square.Min.X; x ++ { for x := square.Min.X; x < square.Max.X; x ++ {
context.dstData[x + y * context.dstStride] = context.color context.dstData[x + y * context.dstStride] = context.color
}} }}
} }
func (context plottingContext) plotSource (center image.Point) { func (context plottingContext) plotSource (center image.Point) {
square := context.square(center) square := context.square(center)
for y := square.Min.Y; y < square.Min.Y; y ++ { for y := square.Min.Y; y < square.Max.Y; y ++ {
for x := square.Min.X; x < square.Min.X; x ++ { for x := square.Min.X; x < square.Max.X; x ++ {
// we offset srcIndex here because we have already applied the // we offset srcIndex here because we have already applied the
// offset to the square, and we need to reverse that to get the // offset to the square, and we need to reverse that to get the
// proper source coordinates. // proper source coordinates.
srcIndex := srcIndex :=
x - context.offset.X + x + context.offset.X +
(y - context.offset.Y) * context.dstStride (y + context.offset.Y) * context.dstStride
dstIndex := x + y * context.dstStride dstIndex := x + y * context.dstStride
context.dstData[dstIndex] = context.srcData [srcIndex] context.dstData[dstIndex] = context.srcData [srcIndex]
}} }}

View File

@ -7,51 +7,44 @@ import "git.tebibyte.media/sashakoshka/tomo/shatter"
// TODO: return updatedRegion for all routines in this package // TODO: return updatedRegion for all routines in this package
// FillRectangle draws the content of one canvas onto another. The offset point
// defines where the origin point of the source canvas is positioned in relation
// to the origin point of the destination canvas. To prevent the entire source
// canvas from being drawn, it must be cut with canvas.Cut().
func FillRectangle ( func FillRectangle (
destination canvas.Canvas, destination canvas.Canvas,
source canvas.Canvas, source canvas.Canvas,
offset image.Point,
) ( ) (
updatedRegion image.Rectangle, updatedRegion image.Rectangle,
) { ) {
dstData, dstStride := destination.Buffer() dstData, dstStride := destination.Buffer()
srcData, srcStride := source.Buffer() srcData, srcStride := source.Buffer()
sourceBounds := offset := source.Bounds().Min.Sub(destination.Bounds().Min)
source.Bounds().Canon(). bounds := source.Bounds().Sub(offset).Intersect(destination.Bounds())
Intersect(destination.Bounds().Sub(offset)) if bounds.Empty() { return }
if sourceBounds.Empty() { return } updatedRegion = bounds
updatedRegion = sourceBounds.Add(offset) point := image.Point { }
for y := sourceBounds.Min.Y; y < sourceBounds.Max.Y; y ++ { for point.Y = bounds.Min.Y; point.Y < bounds.Max.Y; point.Y ++ {
for x := sourceBounds.Min.X; x < sourceBounds.Max.X; x ++ { for point.X = bounds.Min.X; point.X < bounds.Max.X; point.X ++ {
dstData[x + offset.X + (y + offset.Y) * dstStride] = offsetPoint := point.Add(offset)
srcData[x + y * srcStride] dstIndex := point.X + point.Y * dstStride
srcIndex := offsetPoint.X + offsetPoint.Y * srcStride
dstData[dstIndex] = srcData[srcIndex]
}} }}
return return
} }
// StrokeRectangle is similar to FillRectangle, but it draws an inset outline of
// the source canvas onto the destination canvas. To prevent the entire source
// canvas's bounds from being used, it must be cut with canvas.Cut().
func StrokeRectangle ( func StrokeRectangle (
destination canvas.Canvas, destination canvas.Canvas,
source canvas.Canvas, source canvas.Canvas,
offset image.Point,
weight int, weight int,
) { ) {
bounds := source.Bounds() bounds := destination.Bounds()
insetBounds := bounds.Inset(weight) insetBounds := bounds.Inset(weight)
if insetBounds.Empty() { if insetBounds.Empty() {
FillRectangle(destination, source, offset) FillRectangle(destination, source)
return return
} }
FillRectangleShatter(destination, source, offset, insetBounds) FillRectangleShatter(destination, source, insetBounds)
} }
// FillRectangleShatter is like FillRectangle, but it does not draw in areas // FillRectangleShatter is like FillRectangle, but it does not draw in areas
@ -59,12 +52,14 @@ func StrokeRectangle (
func FillRectangleShatter ( func FillRectangleShatter (
destination canvas.Canvas, destination canvas.Canvas,
source canvas.Canvas, source canvas.Canvas,
offset image.Point,
rocks ...image.Rectangle, rocks ...image.Rectangle,
) { ) {
tiles := shatter.Shatter(source.Bounds().Sub(offset), rocks...) tiles := shatter.Shatter(destination.Bounds(), rocks...)
offset := source.Bounds().Min.Sub(destination.Bounds().Min)
for _, tile := range tiles { for _, tile := range tiles {
FillRectangle(destination, canvas.Cut(source, tile), offset) FillRectangle (
canvas.Cut(destination, tile),
canvas.Cut(source, tile.Add(offset)))
} }
} }

View File

@ -4,11 +4,14 @@ import "fmt"
import "time" import "time"
import "image" import "image"
import "image/color" import "image/color"
import "git.tebibyte.media/sashakoshka/tomo/canvas"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/shatter" import "git.tebibyte.media/sashakoshka/tomo/shatter"
import "git.tebibyte.media/sashakoshka/tomo/textdraw" import "git.tebibyte.media/sashakoshka/tomo/textdraw"
import "git.tebibyte.media/sashakoshka/tomo/defaultfont" import "git.tebibyte.media/sashakoshka/tomo/defaultfont"
import "git.tebibyte.media/sashakoshka/tomo/elements/core" import "git.tebibyte.media/sashakoshka/tomo/elements/core"
import "git.tebibyte.media/sashakoshka/tomo/artist/shapes"
import "git.tebibyte.media/sashakoshka/tomo/artist/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 artist
// package in order to test it. // package in order to test it.
@ -21,103 +24,52 @@ type Artist struct {
func NewArtist () (element *Artist) { func NewArtist () (element *Artist) {
element = &Artist { } element = &Artist { }
element.Core, element.core = core.NewCore(element.draw) element.Core, element.core = core.NewCore(element.draw)
element.core.SetMinimumSize(240, 360) element.core.SetMinimumSize(240, 240)
return return
} }
func (element *Artist) draw () { func (element *Artist) draw () {
bounds := element.Bounds() bounds := element.Bounds()
artist.FillRectangle(element.core, artist.NewUniform(hex(0)), bounds) patterns.Uhex(0x000000FF).Draw(element.core, bounds)
drawStart := time.Now() drawStart := time.Now()
// 0, 0 // 0, 0 - 3, 0
artist.FillRectangle ( for x := 0; x < 4; x ++ {
element.core, element.colorLines(x + 1, element.cellAt(x, 0).Bounds())
artist.Beveled { }
artist.NewUniform(hex(0xFF0000FF)),
artist.NewUniform(hex(0x0000FFFF)),
},
element.cellAt(0, 0))
// 1, 0
artist.StrokeRectangle (
element.core,
artist.NewUniform(hex(0x00FF00FF)), 3,
element.cellAt(1, 0))
// 2, 0
artist.FillRectangle (
element.core,
artist.NewMultiBordered (
artist.Stroke { Pattern: uhex(0xFF0000FF), Weight: 1 },
artist.Stroke { Pattern: uhex(0x888800FF), Weight: 2 },
artist.Stroke { Pattern: uhex(0x00FF00FF), Weight: 3 },
artist.Stroke { Pattern: uhex(0x008888FF), Weight: 4 },
artist.Stroke { Pattern: uhex(0x0000FFFF), Weight: 5 },
),
element.cellAt(2, 0))
// 3, 0
artist.FillRectangle (
element.core,
artist.Bordered {
Stroke: artist.Stroke { Pattern: uhex(0x0000FFFF), Weight: 5 },
Fill: uhex(0xFF0000FF),
},
element.cellAt(3, 0))
// 4, 0 // 4, 0
artist.FillRectangle ( c40 := element.cellAt(4, 0)
element.core, shapes.StrokeColorRectangle(c40, artist.Hex(0x888888FF), c40.Bounds(), 1)
artist.Padded { shapes.ColorLine (
Stroke: uhex(0xFFFFFFFF), c40, artist.Hex(0xFF0000FF), 1,
Fill: uhex(0x666666FF), c40.Bounds().Min, c40.Bounds().Max)
Sides: []int { 4, 13, 2, 0 },
},
element.cellAt(4, 0))
// 0, 1 - 3, 1 // 0, 1
for x := 0; x < 4; x ++ { c01 := element.cellAt(0, 1)
artist.FillRectangle ( shapes.StrokeColorRectangle(c01, artist.Hex(0x888888FF), c01.Bounds(), 1)
element.core, shapes.FillColorEllipse(element.core, artist.Hex(0x00FF00FF), c01.Bounds())
artist.Striped {
First: artist.Stroke { Pattern: uhex(0xFF8800FF), Weight: 7 },
Second: artist.Stroke { Pattern: uhex(0x0088FFFF), Weight: 2 },
Orientation: artist.Orientation(x),
},
element.cellAt(x, 1))
}
// 0, 2 - 3, 2 // 1, 1 - 3, 1
for x := 0; x < 4; x ++ {
element.lines(x + 1, element.cellAt(x, 2))
}
// 0, 3
artist.StrokeRectangle (
element.core, uhex(0x888888FF), 1,
element.cellAt(0, 3))
artist.FillEllipse(element.core, uhex(0x00FF00FF), element.cellAt(0, 3))
// 1, 3 - 3, 3
for x := 1; x < 4; x ++ { for x := 1; x < 4; x ++ {
artist.StrokeRectangle ( c := element.cellAt(x, 1)
element.core,uhex(0x888888FF), 1, shapes.StrokeColorRectangle (
element.cellAt(x, 3)) element.core, artist.Hex(0x888888FF),
artist.StrokeEllipse ( c.Bounds(), 1)
shapes.StrokeColorEllipse (
element.core, element.core,
[]artist.Pattern { []color.RGBA {
uhex(0xFF0000FF), artist.Hex(0xFF0000FF),
uhex(0x00FF00FF), artist.Hex(0x00FF00FF),
uhex(0xFF00FFFF), artist.Hex(0xFF00FFFF),
} [x - 1], } [x - 1],
x, element.cellAt(x, 3)) c.Bounds(), x)
} }
// 4, 3 // 4, 1
shatterPos := element.cellAt(4, 3).Min c41 := element.cellAt(4, 1)
shatterPos := c41.Bounds().Min
rocks := []image.Rectangle { rocks := []image.Rectangle {
image.Rect(3, 12, 13, 23).Add(shatterPos), image.Rect(3, 12, 13, 23).Add(shatterPos),
// image.Rect(30, 10, 40, 23).Add(shatterPos), // image.Rect(30, 10, 40, 23).Add(shatterPos),
@ -125,159 +77,36 @@ func (element *Artist) draw () {
image.Rect(30, -10, 40, 43).Add(shatterPos), image.Rect(30, -10, 40, 43).Add(shatterPos),
image.Rect(80, 30, 90, 45).Add(shatterPos), image.Rect(80, 30, 90, 45).Add(shatterPos),
} }
tiles := shatter.Shatter(element.cellAt(4, 3), rocks...) tiles := shatter.Shatter(c41.Bounds(), rocks...)
for _, tile := range tiles { for index, tile := range tiles {
artist.FillRectangle ( artist.DrawBounds (
element.core, element.core, tile,
artist.Bordered { []artist.Pattern {
Fill: uhex(0x888888FF), patterns.Uhex(0xFF0000FF),
Stroke: artist.Stroke { patterns.Uhex(0x00FF00FF),
Pattern: artist.Beveled { patterns.Uhex(0xFF00FFFF),
uhex(0xCCCCCCFF), patterns.Uhex(0xFFFF00FF),
uhex(0x444444FF), patterns.Uhex(0x00FFFFFF),
}, } [index % 5], tile)
Weight: 1,
},
},
tile)
} }
// 0, 4 - 3, 4 // 0, 2
for x := 0; x < 4; x ++ { c02 := element.cellAt(0, 2)
artist.FillEllipse ( shapes.StrokeColorRectangle(c02, artist.Hex(0x888888FF), c02.Bounds(), 1)
element.core, shapes.FillEllipse(c02, c41)
artist.Split {
First: uhex(0xFF0000FF), // 1, 2
Second: uhex(0x0000FFFF), c12 := element.cellAt(1, 2)
Orientation: artist.Orientation(x), shapes.StrokeColorRectangle(c12, artist.Hex(0x888888FF), c12.Bounds(), 1)
}, shapes.StrokeEllipse(c12, c41, 5)
element.cellAt(x, 4))
}
// 0, 5 // 2, 2
artist.FillRectangle ( c22 := element.cellAt(2, 2)
element.core, shapes.FillRectangle(c22, c41)
artist.QuadBeveled {
uhex(0x880000FF),
uhex(0x00FF00FF),
uhex(0x0000FFFF),
uhex(0xFF00FFFF),
},
element.cellAt(0, 5))
// 1, 5
artist.FillRectangle (
element.core,
artist.Checkered {
First: artist.QuadBeveled {
uhex(0x880000FF),
uhex(0x00FF00FF),
uhex(0x0000FFFF),
uhex(0xFF00FFFF),
},
Second: artist.Striped {
First: artist.Stroke { Pattern: uhex(0xFF8800FF), Weight: 1 },
Second: artist.Stroke { Pattern: uhex(0x0088FFFF), Weight: 1 },
Orientation: artist.OrientationVertical,
},
CellWidth: 32,
CellHeight: 16,
},
element.cellAt(1, 5))
// 2, 5
artist.FillRectangle (
element.core,
artist.Dotted {
Foreground: uhex(0x00FF00FF),
Background: artist.Checkered {
First: uhex(0x444444FF),
Second: uhex(0x888888FF),
CellWidth: 16,
CellHeight: 16,
},
Size: 8,
Spacing: 16,
},
element.cellAt(2, 5))
// 3, 5
artist.FillRectangle (
element.core,
artist.Tiled {
Pattern: artist.QuadBeveled {
uhex(0x880000FF),
uhex(0x00FF00FF),
uhex(0x0000FFFF),
uhex(0xFF00FFFF),
},
CellWidth: 17,
CellHeight: 23,
},
element.cellAt(3, 5))
// 0, 6 - 3, 6
for x := 0; x < 4; x ++ {
artist.FillRectangle (
element.core,
artist.Gradient {
First: uhex(0xFF0000FF),
Second: uhex(0x0000FFFF),
Orientation: artist.Orientation(x),
},
element.cellAt(x, 6))
}
// 0, 7 // 3, 2
artist.FillEllipse ( c32 := element.cellAt(3, 2)
element.core, shapes.StrokeRectangle(c32, c41, 5)
artist.EllipticallyBordered {
Fill: artist.Gradient {
First: uhex(0x00FF00FF),
Second: uhex(0x0000FFFF),
Orientation: artist.OrientationVertical,
},
Stroke: artist.Stroke { Pattern: uhex(0x00FF00), Weight: 5 },
},
element.cellAt(0, 7))
// 1, 7
artist.FillRectangle (
element.core,
artist.Noisy {
Low: uhex(0x000000FF),
High: uhex(0xFFFFFFFF),
Seed: 0,
},
element.cellAt(1, 7),
)
// 2, 7
artist.FillRectangle (
element.core,
artist.Noisy {
Low: uhex(0x000000FF),
High: artist.Gradient {
First: uhex(0x000000FF),
Second: uhex(0xFFFFFFFF),
Orientation: artist.OrientationVertical,
},
Seed: 0,
},
element.cellAt(2, 7),
)
// 3, 7
artist.FillRectangle (
element.core,
artist.Noisy {
Low: uhex(0x000000FF),
High: uhex(0xFFFFFFFF),
Seed: 0,
Harsh: true,
},
element.cellAt(3, 7),
)
// how long did that take to render? // how long did that take to render?
drawTime := time.Since(drawStart) drawTime := time.Since(drawStart)
@ -288,68 +117,51 @@ func (element *Artist) draw () {
drawTime.Milliseconds(), drawTime.Milliseconds(),
drawTime.Microseconds()))) drawTime.Microseconds())))
textDrawer.Draw ( textDrawer.Draw (
element.core, uhex(0xFFFFFFFF), element.core, artist.Hex(0xFFFFFFFF),
image.Pt(bounds.Min.X + 8, bounds.Max.Y - 24)) image.Pt(bounds.Min.X + 8, bounds.Max.Y - 24))
} }
func (element *Artist) lines (weight int, bounds image.Rectangle) { func (element *Artist) colorLines (weight int, bounds image.Rectangle) {
bounds = bounds.Inset(4) bounds = bounds.Inset(4)
c := uhex(0xFFFFFFFF) c := artist.Hex(0xFFFFFFFF)
artist.Line(element.core, c, weight, bounds.Min, bounds.Max) shapes.ColorLine(element.core, c, weight, bounds.Min, bounds.Max)
artist.Line ( shapes.ColorLine (
element.core, c, weight, element.core, c, weight,
image.Pt(bounds.Max.X, bounds.Min.Y), image.Pt(bounds.Max.X, bounds.Min.Y),
image.Pt(bounds.Min.X, bounds.Max.Y)) image.Pt(bounds.Min.X, bounds.Max.Y))
artist.Line ( shapes.ColorLine (
element.core, c, weight, element.core, c, weight,
image.Pt(bounds.Max.X, bounds.Min.Y + 16), image.Pt(bounds.Max.X, bounds.Min.Y + 16),
image.Pt(bounds.Min.X, bounds.Max.Y - 16)) image.Pt(bounds.Min.X, bounds.Max.Y - 16))
artist.Line ( shapes.ColorLine (
element.core, c, weight, element.core, c, weight,
image.Pt(bounds.Min.X, bounds.Min.Y + 16), image.Pt(bounds.Min.X, bounds.Min.Y + 16),
image.Pt(bounds.Max.X, bounds.Max.Y - 16)) image.Pt(bounds.Max.X, bounds.Max.Y - 16))
artist.Line ( shapes.ColorLine (
element.core, c, weight, element.core, c, weight,
image.Pt(bounds.Min.X + 20, bounds.Min.Y), image.Pt(bounds.Min.X + 20, bounds.Min.Y),
image.Pt(bounds.Max.X - 20, bounds.Max.Y)) image.Pt(bounds.Max.X - 20, bounds.Max.Y))
artist.Line ( shapes.ColorLine (
element.core, c, weight, element.core, c, weight,
image.Pt(bounds.Max.X - 20, bounds.Min.Y), image.Pt(bounds.Max.X - 20, bounds.Min.Y),
image.Pt(bounds.Min.X + 20, bounds.Max.Y)) image.Pt(bounds.Min.X + 20, bounds.Max.Y))
artist.Line ( shapes.ColorLine (
element.core, c, weight, element.core, c, weight,
image.Pt(bounds.Min.X, bounds.Min.Y + bounds.Dy() / 2), image.Pt(bounds.Min.X, bounds.Min.Y + bounds.Dy() / 2),
image.Pt(bounds.Max.X, bounds.Min.Y + bounds.Dy() / 2)) image.Pt(bounds.Max.X, bounds.Min.Y + bounds.Dy() / 2))
artist.Line ( shapes.ColorLine (
element.core, c, weight, element.core, c, weight,
image.Pt(bounds.Min.X + bounds.Dx() / 2, bounds.Min.Y), image.Pt(bounds.Min.X + bounds.Dx() / 2, bounds.Min.Y),
image.Pt(bounds.Min.X + bounds.Dx() / 2, bounds.Max.Y)) image.Pt(bounds.Min.X + bounds.Dx() / 2, bounds.Max.Y))
} }
func (element *Artist) cellAt (x, y int) (image.Rectangle) { func (element *Artist) cellAt (x, y int) (canvas.Canvas) {
bounds := element.Bounds() bounds := element.Bounds()
cellBounds := image.Rectangle { } cellBounds := image.Rectangle { }
cellBounds.Min = bounds.Min cellBounds.Min = bounds.Min
cellBounds.Max.X = bounds.Min.X + bounds.Dx() / 5 cellBounds.Max.X = bounds.Min.X + bounds.Dx() / 5
cellBounds.Max.Y = bounds.Min.Y + (bounds.Dy() - 48) / 8 cellBounds.Max.Y = bounds.Min.Y + (bounds.Dy() - 48) / 4
return cellBounds.Add (image.Pt ( return canvas.Cut (element.core, cellBounds.Add (image.Pt (
x * cellBounds.Dx(), x * cellBounds.Dx(),
y * cellBounds.Dy())) y * cellBounds.Dy())))
}
func hex (n uint32) (c color.RGBA) {
c.A = uint8(n)
c.B = uint8(n >> 8)
c.G = uint8(n >> 16)
c.R = uint8(n >> 24)
return
}
func uhex (n uint32) (artist.Pattern) {
return artist.NewUniform (color.RGBA {
A: uint8(n),
B: uint8(n >> 8),
G: uint8(n >> 16),
R: uint8(n >> 24),
})
} }

View File

@ -1,11 +1,11 @@
package testing package testing
import "image" import "image"
import "image/color"
import "git.tebibyte.media/sashakoshka/tomo/input" import "git.tebibyte.media/sashakoshka/tomo/input"
import "git.tebibyte.media/sashakoshka/tomo/config"
import "git.tebibyte.media/sashakoshka/tomo/theme" import "git.tebibyte.media/sashakoshka/tomo/theme"
import "git.tebibyte.media/sashakoshka/tomo/config"
import "git.tebibyte.media/sashakoshka/tomo/artist" import "git.tebibyte.media/sashakoshka/tomo/artist"
import "git.tebibyte.media/sashakoshka/tomo/artist/shapes"
import "git.tebibyte.media/sashakoshka/tomo/elements/core" import "git.tebibyte.media/sashakoshka/tomo/elements/core"
// Mouse is an element capable of testing mouse input. When the mouse is clicked // Mouse is an element capable of testing mouse input. When the mouse is clicked
@ -14,7 +14,6 @@ type Mouse struct {
*core.Core *core.Core
core core.CoreControl core core.CoreControl
drawing bool drawing bool
color artist.Pattern
lastMousePos image.Point lastMousePos image.Point
config config.Config config config.Config
@ -27,7 +26,6 @@ func NewMouse () (element *Mouse) {
element = &Mouse { c: theme.C("testing", "mouse") } element = &Mouse { c: theme.C("testing", "mouse") }
element.Core, element.core = core.NewCore(element.draw) element.Core, element.core = core.NewCore(element.draw)
element.core.SetMinimumSize(32, 32) element.core.SetMinimumSize(32, 32)
element.color = artist.NewUniform(color.Black)
return return
} }
@ -55,17 +53,17 @@ func (element *Mouse) draw () {
theme.PatternAccent, theme.PatternAccent,
theme.PatternState { }, theme.PatternState { },
element.c) element.c)
artist.FillRectangle(element.core, pattern, bounds) pattern.Draw(element.core, bounds)
artist.StrokeRectangle ( shapes.StrokeColorRectangle (
element.core, element.core,
artist.NewUniform(color.Black), 1, artist.Hex(0x000000FF),
bounds) bounds, 1)
artist.Line ( shapes.ColorLine (
element.core, artist.NewUniform(color.White), 1, element.core, artist.Hex(0xFFFFFFFF), 1,
bounds.Min.Add(image.Pt(1, 1)), bounds.Min.Add(image.Pt(1, 1)),
bounds.Min.Add(image.Pt(bounds.Dx() - 2, bounds.Dy() - 2))) bounds.Min.Add(image.Pt(bounds.Dx() - 2, bounds.Dy() - 2)))
artist.Line ( shapes.ColorLine (
element.core, artist.NewUniform(color.White), 1, element.core, artist.Hex(0xFFFFFFFF), 1,
bounds.Min.Add(image.Pt(1, bounds.Dy() - 2)), bounds.Min.Add(image.Pt(1, bounds.Dy() - 2)),
bounds.Min.Add(image.Pt(bounds.Dx() - 2, 1))) bounds.Min.Add(image.Pt(bounds.Dx() - 2, 1)))
} }
@ -78,8 +76,8 @@ func (element *Mouse) HandleMouseDown (x, y int, button input.Button) {
func (element *Mouse) HandleMouseUp (x, y int, button input.Button) { func (element *Mouse) HandleMouseUp (x, y int, button input.Button) {
element.drawing = false element.drawing = false
mousePos := image.Pt(x, y) mousePos := image.Pt(x, y)
element.core.DamageRegion (artist.Line ( element.core.DamageRegion (shapes.ColorLine (
element.core, element.color, 1, element.core, artist.Hex(0x000000FF), 1,
element.lastMousePos, mousePos)) element.lastMousePos, mousePos))
element.lastMousePos = mousePos element.lastMousePos = mousePos
} }
@ -87,8 +85,8 @@ func (element *Mouse) HandleMouseUp (x, y int, button input.Button) {
func (element *Mouse) HandleMouseMove (x, y int) { func (element *Mouse) HandleMouseMove (x, y int) {
if !element.drawing { return } if !element.drawing { return }
mousePos := image.Pt(x, y) mousePos := image.Pt(x, y)
element.core.DamageRegion (artist.Line ( element.core.DamageRegion (shapes.ColorLine (
element.core, element.color, 1, element.core, artist.Hex(0x000000FF), 1,
element.lastMousePos, mousePos)) element.lastMousePos, mousePos))
element.lastMousePos = mousePos element.lastMousePos = mousePos
} }

View File

@ -11,12 +11,12 @@ func main () {
} }
func run () { func run () {
window, _ := tomo.NewWindow(128, 128) window, _ := tomo.NewWindow(480, 360)
window.SetTitle("Draw Test") window.SetTitle("Draw Test")
window.Adopt(testing.NewArtist()) window.Adopt(testing.NewArtist())
window.OnClose(tomo.Stop) window.OnClose(tomo.Stop)
window.Show() window.Show()
go func () { go func () {
http.ListenAndServe("localhost:6060", nil) http.ListenAndServe("localhost:9090", nil)
} () } ()
} }

View File

@ -3,9 +3,9 @@ package textdraw
import "image" import "image"
import "unicode" import "unicode"
import "image/draw" import "image/draw"
import "image/color"
import "golang.org/x/image/math/fixed" import "golang.org/x/image/math/fixed"
import "git.tebibyte.media/sashakoshka/tomo/canvas" import "git.tebibyte.media/sashakoshka/tomo/canvas"
import "git.tebibyte.media/sashakoshka/tomo/artist"
// Drawer is an extended TypeSetter that is able to draw text. Much like // 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. // TypeSetter, It has no constructor and its zero value can be used safely.
@ -14,17 +14,11 @@ type Drawer struct { TypeSetter }
// Draw draws the drawer's text onto the specified canvas at the given offset. // Draw draws the drawer's text onto the specified canvas at the given offset.
func (drawer Drawer) Draw ( func (drawer Drawer) Draw (
destination canvas.Canvas, destination canvas.Canvas,
source artist.Pattern, color color.RGBA,
offset image.Point, offset image.Point,
) ( ) (
updatedRegion image.Rectangle, updatedRegion image.Rectangle,
) { ) {
wrappedSource := artist.WrappedPattern {
Pattern: source,
Width: 0,
Height: 0, // TODO: choose a better width and height
}
drawer.For (func ( drawer.For (func (
index int, index int,
char rune, char rune,
@ -46,7 +40,7 @@ func (drawer Drawer) Draw (
draw.DrawMask ( draw.DrawMask (
destination, destination,
destinationRectangle, destinationRectangle,
wrappedSource, image.Point { }, image.NewUniform(color), image.Point { },
mask, maskPoint, mask, maskPoint,
draw.Over) draw.Over)