Got rectangles all sorted
This commit is contained in:
parent
48237f5687
commit
d167559830
@ -1,5 +1,2 @@
|
|||||||
// Package artist provides a simple 2D drawing library for canvas.Canvas.
|
// Package artist provides a simple 2D drawing library for canvas.Canvas.
|
||||||
// Artist's drawing functions take in things called patterns, which are sampled
|
|
||||||
// as a source in order to color and texture drawn shapes. Patterns can be
|
|
||||||
// mixed together and composited to create new, more complex patterns.
|
|
||||||
package artist
|
package artist
|
||||||
|
@ -1,130 +0,0 @@
|
|||||||
package artist
|
|
||||||
|
|
||||||
import "image"
|
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/shatter"
|
|
||||||
|
|
||||||
// Paste transfers one canvas onto another, offset by the specified point.
|
|
||||||
func Paste (
|
|
||||||
destination canvas.Canvas,
|
|
||||||
source canvas.Canvas,
|
|
||||||
offset image.Point,
|
|
||||||
) (
|
|
||||||
updatedRegion image.Rectangle,
|
|
||||||
) {
|
|
||||||
dstData, dstStride := destination.Buffer()
|
|
||||||
srcData, srcStride := source.Buffer()
|
|
||||||
|
|
||||||
sourceBounds :=
|
|
||||||
source.Bounds().Canon().
|
|
||||||
Intersect(destination.Bounds().Sub(offset))
|
|
||||||
if sourceBounds.Empty() { return }
|
|
||||||
|
|
||||||
updatedRegion = sourceBounds.Add(offset)
|
|
||||||
for y := sourceBounds.Min.Y; y < sourceBounds.Max.Y; y ++ {
|
|
||||||
for x := sourceBounds.Min.X; x < sourceBounds.Max.X; x ++ {
|
|
||||||
dstData[x + offset.X + (y + offset.Y) * dstStride] =
|
|
||||||
srcData[x + y * srcStride]
|
|
||||||
}}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillRectangle draws a filled rectangle with the specified pattern.
|
|
||||||
func FillRectangle (
|
|
||||||
destination canvas.Canvas,
|
|
||||||
source Pattern,
|
|
||||||
bounds image.Rectangle,
|
|
||||||
) (
|
|
||||||
updatedRegion image.Rectangle,
|
|
||||||
) {
|
|
||||||
return FillRectangleClip(destination, source, bounds, bounds)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillRectangleClip is similar to FillRectangle, but it clips the pattern to
|
|
||||||
// a specified rectangle mask. That is—the pattern will be queried as if it
|
|
||||||
// were drawn without the mask, but only the area specified by the intersection
|
|
||||||
// of bounds and mask will be drawn to.
|
|
||||||
func FillRectangleClip (
|
|
||||||
destination canvas.Canvas,
|
|
||||||
source Pattern,
|
|
||||||
bounds image.Rectangle,
|
|
||||||
mask image.Rectangle,
|
|
||||||
) (
|
|
||||||
updatedRegion image.Rectangle,
|
|
||||||
) {
|
|
||||||
data, stride := destination.Buffer()
|
|
||||||
realBounds := bounds
|
|
||||||
bounds =
|
|
||||||
bounds.Canon().
|
|
||||||
Intersect(mask.Canon()).
|
|
||||||
Intersect(destination.Bounds())
|
|
||||||
if bounds.Empty() { return }
|
|
||||||
updatedRegion = bounds
|
|
||||||
|
|
||||||
realWidth, realHeight := realBounds.Dx(), realBounds.Dy()
|
|
||||||
patternOffset := realBounds.Min.Sub(bounds.Min)
|
|
||||||
|
|
||||||
width, height := bounds.Dx(), bounds.Dy()
|
|
||||||
for y := 0; y < height; y ++ {
|
|
||||||
for x := 0; x < width; x ++ {
|
|
||||||
data[x + bounds.Min.X + (y + bounds.Min.Y) * stride] =
|
|
||||||
source.AtWhen (
|
|
||||||
x - patternOffset.X, y - patternOffset.Y,
|
|
||||||
realWidth, realHeight)
|
|
||||||
}}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FillRectangleShatter shatters a bounding rectangle and draws its tiles in one
|
|
||||||
// fell swoop.
|
|
||||||
func FillRectangleShatter (
|
|
||||||
destination canvas.Canvas,
|
|
||||||
source Pattern,
|
|
||||||
glass image.Rectangle,
|
|
||||||
rocks ...image.Rectangle,
|
|
||||||
) (
|
|
||||||
updatedRegions []image.Rectangle,
|
|
||||||
) {
|
|
||||||
tiles := shatter.Shatter(glass, rocks...)
|
|
||||||
for _, tile := range tiles {
|
|
||||||
FillRectangleClip(destination, source, glass, tile)
|
|
||||||
}
|
|
||||||
return tiles
|
|
||||||
}
|
|
||||||
|
|
||||||
// StrokeRectangle draws the outline of a rectangle with the specified line
|
|
||||||
// weight and pattern.
|
|
||||||
func StrokeRectangle (
|
|
||||||
destination canvas.Canvas,
|
|
||||||
source Pattern,
|
|
||||||
weight int,
|
|
||||||
bounds image.Rectangle,
|
|
||||||
) {
|
|
||||||
bounds = bounds.Canon()
|
|
||||||
insetBounds := bounds.Inset(weight)
|
|
||||||
if insetBounds.Empty() {
|
|
||||||
FillRectangle(destination, source, bounds)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// top
|
|
||||||
FillRectangle (destination, source, image.Rect (
|
|
||||||
bounds.Min.X, bounds.Min.Y,
|
|
||||||
bounds.Max.X, insetBounds.Min.Y))
|
|
||||||
|
|
||||||
// bottom
|
|
||||||
FillRectangle (destination, source, image.Rect (
|
|
||||||
bounds.Min.X, insetBounds.Max.Y,
|
|
||||||
bounds.Max.X, bounds.Max.Y))
|
|
||||||
|
|
||||||
// left
|
|
||||||
FillRectangle (destination, source, image.Rect (
|
|
||||||
bounds.Min.X, insetBounds.Min.Y,
|
|
||||||
insetBounds.Min.X, insetBounds.Max.Y))
|
|
||||||
|
|
||||||
// right
|
|
||||||
FillRectangle (destination, source, image.Rect (
|
|
||||||
insetBounds.Max.X, insetBounds.Min.Y,
|
|
||||||
bounds.Max.X, insetBounds.Max.Y))
|
|
||||||
}
|
|
11
artist/shapes/doc.go
Normal file
11
artist/shapes/doc.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Package shapes provides some basic shape drawing routines.
|
||||||
|
//
|
||||||
|
// A word about patterns:
|
||||||
|
//
|
||||||
|
// 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,14 +1,15 @@
|
|||||||
package artist
|
package shapes
|
||||||
|
|
||||||
import "math"
|
import "math"
|
||||||
import "image"
|
import "image"
|
||||||
import "image/color"
|
import "image/color"
|
||||||
|
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
||||||
import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
||||||
|
|
||||||
// FillEllipse draws a filled ellipse with the specified pattern.
|
// FillEllipse draws a filled ellipse with the specified pattern.
|
||||||
func FillEllipse (
|
func FillEllipse (
|
||||||
destination canvas.Canvas,
|
destination canvas.Canvas,
|
||||||
source Pattern,
|
source artist.Pattern,
|
||||||
bounds image.Rectangle,
|
bounds image.Rectangle,
|
||||||
) (
|
) (
|
||||||
updatedRegion image.Rectangle,
|
updatedRegion image.Rectangle,
|
||||||
@ -37,7 +38,7 @@ func FillEllipse (
|
|||||||
// and pattern.
|
// and pattern.
|
||||||
func StrokeEllipse (
|
func StrokeEllipse (
|
||||||
destination canvas.Canvas,
|
destination canvas.Canvas,
|
||||||
source Pattern,
|
source artist.Pattern,
|
||||||
weight int,
|
weight int,
|
||||||
bounds image.Rectangle,
|
bounds image.Rectangle,
|
||||||
) {
|
) {
|
||||||
@ -130,7 +131,7 @@ func StrokeEllipse (
|
|||||||
type ellipsePlottingContext struct {
|
type ellipsePlottingContext struct {
|
||||||
data []color.RGBA
|
data []color.RGBA
|
||||||
stride int
|
stride int
|
||||||
source Pattern
|
source artist.Pattern
|
||||||
width, height int
|
width, height int
|
||||||
weight int
|
weight int
|
||||||
bounds image.Rectangle
|
bounds image.Rectangle
|
@ -1,4 +1,4 @@
|
|||||||
package artist
|
package shapes
|
||||||
|
|
||||||
import "image"
|
import "image"
|
||||||
import "image/color"
|
import "image/color"
|
||||||
@ -10,7 +10,7 @@ import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
|||||||
// pattern.
|
// pattern.
|
||||||
func Line (
|
func Line (
|
||||||
destination canvas.Canvas,
|
destination canvas.Canvas,
|
||||||
source Pattern,
|
source canvas.Canvas,
|
||||||
weight int,
|
weight int,
|
||||||
min image.Point,
|
min image.Point,
|
||||||
max image.Point,
|
max image.Point,
|
84
artist/shapes/rectangle.go
Normal file
84
artist/shapes/rectangle.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package shapes
|
||||||
|
|
||||||
|
import "image"
|
||||||
|
import "git.tebibyte.media/sashakoshka/tomo/canvas"
|
||||||
|
import "git.tebibyte.media/sashakoshka/tomo/shatter"
|
||||||
|
|
||||||
|
// FillRectangle draws a rectangular subset of one canvas onto the other. 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 (
|
||||||
|
destination canvas.Canvas,
|
||||||
|
source canvas.Canvas,
|
||||||
|
offset image.Point,
|
||||||
|
) (
|
||||||
|
updatedRegion image.Rectangle,
|
||||||
|
) {
|
||||||
|
dstData, dstStride := destination.Buffer()
|
||||||
|
srcData, srcStride := source.Buffer()
|
||||||
|
|
||||||
|
sourceBounds :=
|
||||||
|
source.Bounds().Canon().
|
||||||
|
Intersect(destination.Bounds().Sub(offset))
|
||||||
|
if sourceBounds.Empty() { return }
|
||||||
|
|
||||||
|
updatedRegion = sourceBounds.Add(offset)
|
||||||
|
for y := sourceBounds.Min.Y; y < sourceBounds.Max.Y; y ++ {
|
||||||
|
for x := sourceBounds.Min.X; x < sourceBounds.Max.X; x ++ {
|
||||||
|
dstData[x + offset.X + (y + offset.Y) * dstStride] =
|
||||||
|
srcData[x + y * srcStride]
|
||||||
|
}}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
destination canvas.Canvas,
|
||||||
|
source canvas.Canvas,
|
||||||
|
offset image.Point,
|
||||||
|
weight int,
|
||||||
|
) {
|
||||||
|
bounds := source.Bounds()
|
||||||
|
insetBounds := bounds.Inset(weight)
|
||||||
|
if insetBounds.Empty() {
|
||||||
|
FillRectangle(destination, source, offset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
top := image.Rect (
|
||||||
|
bounds.Min.X, bounds.Min.Y,
|
||||||
|
bounds.Max.X, insetBounds.Min.Y)
|
||||||
|
bottom := image.Rect (
|
||||||
|
bounds.Min.X, insetBounds.Max.Y,
|
||||||
|
bounds.Max.X, bounds.Max.Y)
|
||||||
|
left := image.Rect (
|
||||||
|
bounds.Min.X, insetBounds.Min.Y,
|
||||||
|
insetBounds.Min.X, insetBounds.Max.Y)
|
||||||
|
right := image.Rect (
|
||||||
|
insetBounds.Max.X, insetBounds.Min.Y,
|
||||||
|
bounds.Max.X, insetBounds.Max.Y)
|
||||||
|
|
||||||
|
FillRectangle (destination, canvas.Cut(source, top), offset)
|
||||||
|
FillRectangle (destination, canvas.Cut(source, bottom), offset)
|
||||||
|
FillRectangle (destination, canvas.Cut(source, left), offset)
|
||||||
|
FillRectangle (destination, canvas.Cut(source, right), offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FillRectangleShatter is like FillRectangle, but it does not draw in areas
|
||||||
|
// specified in "rocks".
|
||||||
|
func FillRectangleShatter (
|
||||||
|
destination canvas.Canvas,
|
||||||
|
source canvas.Canvas,
|
||||||
|
offset image.Point,
|
||||||
|
rocks []image.Rectangle,
|
||||||
|
) {
|
||||||
|
tiles := shatter.Shatter(source.Bounds())
|
||||||
|
for _, tile := range tiles {
|
||||||
|
tile
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user