2023-02-23 18:55:19 -07:00
|
|
|
package shapes
|
|
|
|
|
|
|
|
import "image"
|
2023-02-24 14:31:42 -07:00
|
|
|
import "image/color"
|
2023-04-30 11:45:21 -06:00
|
|
|
import "git.tebibyte.media/sashakoshka/tomo/artist"
|
2023-02-23 18:55:19 -07:00
|
|
|
import "git.tebibyte.media/sashakoshka/tomo/shatter"
|
|
|
|
|
2023-02-24 14:31:42 -07:00
|
|
|
// TODO: return updatedRegion for all routines in this package
|
|
|
|
|
2023-02-23 18:55:19 -07:00
|
|
|
func FillRectangle (
|
2023-04-30 11:45:21 -06:00
|
|
|
destination artist.Canvas,
|
|
|
|
source artist.Canvas,
|
2023-03-11 23:04:06 -07:00
|
|
|
bounds image.Rectangle,
|
2023-02-23 18:55:19 -07:00
|
|
|
) (
|
|
|
|
updatedRegion image.Rectangle,
|
|
|
|
) {
|
|
|
|
dstData, dstStride := destination.Buffer()
|
|
|
|
srcData, srcStride := source.Buffer()
|
|
|
|
|
2023-03-11 23:04:06 -07:00
|
|
|
offset := source.Bounds().Min.Sub(destination.Bounds().Min)
|
|
|
|
drawBounds :=
|
|
|
|
source.Bounds().Sub(offset).
|
|
|
|
Intersect(destination.Bounds()).
|
|
|
|
Intersect(bounds)
|
|
|
|
if drawBounds.Empty() { return }
|
|
|
|
updatedRegion = drawBounds
|
2023-02-23 18:55:19 -07:00
|
|
|
|
2023-02-26 12:27:38 -07:00
|
|
|
point := image.Point { }
|
2023-03-11 23:04:06 -07:00
|
|
|
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 ++ {
|
2023-02-26 12:27:38 -07:00
|
|
|
offsetPoint := point.Add(offset)
|
|
|
|
dstIndex := point.X + point.Y * dstStride
|
|
|
|
srcIndex := offsetPoint.X + offsetPoint.Y * srcStride
|
|
|
|
dstData[dstIndex] = srcData[srcIndex]
|
2023-02-23 18:55:19 -07:00
|
|
|
}}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func StrokeRectangle (
|
2023-04-30 11:45:21 -06:00
|
|
|
destination artist.Canvas,
|
|
|
|
source artist.Canvas,
|
2023-03-11 23:04:06 -07:00
|
|
|
bounds image.Rectangle,
|
2023-02-23 18:55:19 -07:00
|
|
|
weight int,
|
2023-03-11 23:04:06 -07:00
|
|
|
) (
|
|
|
|
updatedRegion image.Rectangle,
|
2023-02-23 18:55:19 -07:00
|
|
|
) {
|
|
|
|
insetBounds := bounds.Inset(weight)
|
|
|
|
if insetBounds.Empty() {
|
2023-03-11 23:04:06 -07:00
|
|
|
return FillRectangle(destination, source, bounds)
|
2023-02-23 18:55:19 -07:00
|
|
|
}
|
2023-03-11 23:23:20 -07:00
|
|
|
return FillRectangleShatter(destination, source, bounds, insetBounds)
|
2023-02-23 18:55:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// FillRectangleShatter is like FillRectangle, but it does not draw in areas
|
|
|
|
// specified in "rocks".
|
|
|
|
func FillRectangleShatter (
|
2023-04-30 11:45:21 -06:00
|
|
|
destination artist.Canvas,
|
|
|
|
source artist.Canvas,
|
2023-03-11 23:04:06 -07:00
|
|
|
bounds image.Rectangle,
|
2023-02-24 00:26:34 -07:00
|
|
|
rocks ...image.Rectangle,
|
2023-03-11 23:04:06 -07:00
|
|
|
) (
|
|
|
|
updatedRegion image.Rectangle,
|
2023-02-23 18:55:19 -07:00
|
|
|
) {
|
2023-03-11 23:04:06 -07:00
|
|
|
tiles := shatter.Shatter(bounds, rocks...)
|
2023-02-23 18:55:19 -07:00
|
|
|
for _, tile := range tiles {
|
2023-02-26 12:27:38 -07:00
|
|
|
FillRectangle (
|
2023-04-30 11:45:21 -06:00
|
|
|
artist.Cut(destination, tile),
|
2023-03-11 23:04:06 -07:00
|
|
|
source, tile)
|
|
|
|
updatedRegion = updatedRegion.Union(tile)
|
2023-02-23 18:55:19 -07:00
|
|
|
}
|
2023-03-11 23:04:06 -07:00
|
|
|
return
|
2023-02-23 18:55:19 -07:00
|
|
|
}
|
2023-02-24 14:31:42 -07:00
|
|
|
|
|
|
|
// FillColorRectangle fills a rectangle within the destination canvas with a
|
|
|
|
// solid color.
|
|
|
|
func FillColorRectangle (
|
2023-04-30 11:45:21 -06:00
|
|
|
destination artist.Canvas,
|
2023-02-24 14:31:42 -07:00
|
|
|
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 (
|
2023-04-30 11:45:21 -06:00
|
|
|
destination artist.Canvas,
|
2023-02-24 14:31:42 -07:00
|
|
|
color color.RGBA,
|
|
|
|
bounds image.Rectangle,
|
|
|
|
rocks ...image.Rectangle,
|
2023-03-11 23:04:06 -07:00
|
|
|
) (
|
|
|
|
updatedRegion image.Rectangle,
|
2023-02-24 14:31:42 -07:00
|
|
|
) {
|
|
|
|
tiles := shatter.Shatter(bounds, rocks...)
|
|
|
|
for _, tile := range tiles {
|
|
|
|
FillColorRectangle(destination, color, tile)
|
2023-03-11 23:04:06 -07:00
|
|
|
updatedRegion = updatedRegion.Union(tile)
|
2023-02-24 14:31:42 -07:00
|
|
|
}
|
2023-03-11 23:04:06 -07:00
|
|
|
return
|
2023-02-24 14:31:42 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// StrokeColorRectangle is similar to FillColorRectangle, but it draws an inset
|
|
|
|
// outline of the given rectangle instead.
|
|
|
|
func StrokeColorRectangle (
|
2023-04-30 11:45:21 -06:00
|
|
|
destination artist.Canvas,
|
2023-02-24 14:31:42 -07:00
|
|
|
color color.RGBA,
|
|
|
|
bounds image.Rectangle,
|
|
|
|
weight int,
|
2023-03-11 23:04:06 -07:00
|
|
|
) (
|
|
|
|
updatedRegion image.Rectangle,
|
2023-02-24 14:31:42 -07:00
|
|
|
) {
|
|
|
|
insetBounds := bounds.Inset(weight)
|
|
|
|
if insetBounds.Empty() {
|
2023-03-11 23:04:06 -07:00
|
|
|
return FillColorRectangle(destination, color, bounds)
|
2023-02-24 14:31:42 -07:00
|
|
|
}
|
2023-03-11 23:04:06 -07:00
|
|
|
return FillColorRectangleShatter(destination, color, bounds, insetBounds)
|
2023-02-24 14:31:42 -07:00
|
|
|
}
|