2018-10-21 00:08:20 +00:00
|
|
|
package uix
|
|
|
|
|
|
|
|
import (
|
2019-12-28 03:16:34 +00:00
|
|
|
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/level"
|
2019-12-23 02:21:58 +00:00
|
|
|
"git.kirsle.net/go/render"
|
|
|
|
"git.kirsle.net/go/render/event"
|
2019-12-28 00:31:58 +00:00
|
|
|
"git.kirsle.net/go/ui"
|
2018-10-21 00:08:20 +00:00
|
|
|
)
|
|
|
|
|
2020-11-16 02:02:35 +00:00
|
|
|
// Modified returns whether the canvas has been modified since it was last
|
|
|
|
// loaded. Methods like Load and LoadFile will set modified to false, and
|
|
|
|
// commitStroke sets it to true.
|
|
|
|
func (w *Canvas) Modified() bool {
|
|
|
|
return w.modified
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetModified sets the modified bit on the canvas.
|
|
|
|
func (w *Canvas) SetModified(v bool) {
|
|
|
|
w.modified = v
|
|
|
|
}
|
|
|
|
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
// commitStroke is the common function that applies a stroke the user is
|
|
|
|
// actively drawing onto the canvas. This is for Edit Mode.
|
|
|
|
func (w *Canvas) commitStroke(tool drawtool.Tool, addHistory bool) {
|
|
|
|
if w.currentStroke == nil {
|
|
|
|
// nothing to commit
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-11-20 04:08:38 +00:00
|
|
|
// Zoom the stroke coordinates (this modifies the pointer)
|
|
|
|
zStroke := w.ZoomStroke(w.currentStroke)
|
|
|
|
_ = zStroke
|
|
|
|
|
2020-11-16 02:02:35 +00:00
|
|
|
// Mark the canvas as modified.
|
|
|
|
w.modified = true
|
|
|
|
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
var (
|
|
|
|
deleting = w.currentStroke.Shape == drawtool.Eraser
|
|
|
|
dedupe = map[render.Point]interface{}{} // don't revisit the same point twice
|
|
|
|
|
|
|
|
// Helper functions to set pixels on the level while storing the original
|
|
|
|
// value of any pixel being replaced.
|
|
|
|
set = func(pt render.Point, sw *level.Swatch) {
|
|
|
|
// Take note of what pixel was originally here before we change it.
|
|
|
|
if swatch, err := w.chunks.Get(pt); err == nil {
|
|
|
|
if _, ok := dedupe[pt]; !ok {
|
|
|
|
w.currentStroke.OriginalPoints[pt] = swatch
|
|
|
|
dedupe[pt] = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if deleting {
|
|
|
|
w.chunks.Delete(pt)
|
|
|
|
} else if sw != nil {
|
|
|
|
w.chunks.Set(pt, sw)
|
|
|
|
} else {
|
|
|
|
panic("Canvas.commitStroke.set: current stroke has no level.Swatch in ExtraData")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rects: read existing pixels first, then write new pixels
|
|
|
|
readRect = func(rect render.Rect) {
|
|
|
|
for pt := range w.chunks.IterViewport(rect) {
|
|
|
|
point := pt.Point()
|
|
|
|
if _, ok := dedupe[point]; !ok {
|
|
|
|
w.currentStroke.OriginalPoints[pt.Point()] = pt.Swatch
|
|
|
|
dedupe[point] = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setRect = func(rect render.Rect, sw *level.Swatch) {
|
|
|
|
if deleting {
|
|
|
|
w.chunks.DeleteRect(rect)
|
|
|
|
} else if sw != nil {
|
|
|
|
w.chunks.SetRect(rect, sw)
|
|
|
|
} else {
|
|
|
|
panic("Canvas.commitStroke.setRect: current stroke has no level.Swatch in ExtraData")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
var swatch *level.Swatch
|
|
|
|
if v, ok := w.currentStroke.ExtraData.(*level.Swatch); ok {
|
|
|
|
swatch = v
|
|
|
|
}
|
|
|
|
|
|
|
|
if w.currentStroke.Thickness > 0 {
|
|
|
|
// Eraser Tool only: record which pixels will be blown away by this.
|
|
|
|
// This is SLOW for thick (rect-based) lines, but eraser tool must have it.
|
|
|
|
if deleting {
|
|
|
|
for rect := range w.currentStroke.IterThickPoints() {
|
|
|
|
readRect(rect)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for rect := range w.currentStroke.IterThickPoints() {
|
|
|
|
setRect(rect, swatch)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for pt := range w.currentStroke.IterPoints() {
|
|
|
|
// note: set already records the original pixel if changing it.
|
|
|
|
set(pt, swatch)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the stroke to level history.
|
|
|
|
if w.level != nil && addHistory {
|
|
|
|
w.level.UndoHistory.AddStroke(w.currentStroke)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.RemoveStroke(w.currentStroke)
|
|
|
|
w.currentStroke = nil
|
|
|
|
|
|
|
|
w.lastPixel = nil
|
|
|
|
}
|
|
|
|
|
2018-10-21 00:08:20 +00:00
|
|
|
// loopEditable handles the Loop() part for editable canvases.
|
2019-12-22 22:11:01 +00:00
|
|
|
func (w *Canvas) loopEditable(ev *event.State) error {
|
2018-10-21 00:08:20 +00:00
|
|
|
// Get the absolute position of the canvas on screen to accurately match
|
|
|
|
// it up to mouse clicks.
|
|
|
|
var (
|
|
|
|
P = ui.AbsolutePosition(w)
|
|
|
|
cursor = render.Point{
|
2019-12-28 03:16:34 +00:00
|
|
|
X: ev.CursorX - P.X - w.Scroll.X,
|
|
|
|
Y: ev.CursorY - P.Y - w.Scroll.Y,
|
2018-10-21 00:08:20 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
// If the actual cursor is not over the actual Canvas UI element, don't
|
|
|
|
// pay any attention to clicks. I added this when I saw you were able to
|
|
|
|
// accidentally draw (with large brush size) when clicking on the Palette
|
|
|
|
// panel and not the drawing itself.
|
|
|
|
if !w.IsCursorOver() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-10-21 00:08:20 +00:00
|
|
|
switch w.Tool {
|
2019-07-04 00:19:25 +00:00
|
|
|
case drawtool.PencilTool:
|
2018-10-21 00:08:20 +00:00
|
|
|
// If no swatch is active, do nothing with mouse clicks.
|
|
|
|
if w.Palette.ActiveSwatch == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clicking? Log all the pixels while doing so.
|
2019-12-22 22:11:01 +00:00
|
|
|
if ev.Button1 {
|
2019-07-03 23:22:30 +00:00
|
|
|
// Initialize a new Stroke for this atomic drawing operation?
|
|
|
|
if w.currentStroke == nil {
|
|
|
|
w.currentStroke = drawtool.NewStroke(drawtool.Freehand, w.Palette.ActiveSwatch.Color)
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
w.currentStroke.Thickness = w.BrushSize
|
2019-07-03 23:22:30 +00:00
|
|
|
w.currentStroke.ExtraData = w.Palette.ActiveSwatch
|
|
|
|
w.AddStroke(w.currentStroke)
|
|
|
|
}
|
|
|
|
|
2018-10-21 00:08:20 +00:00
|
|
|
lastPixel := w.lastPixel
|
|
|
|
pixel := &level.Pixel{
|
|
|
|
X: cursor.X,
|
|
|
|
Y: cursor.Y,
|
|
|
|
Swatch: w.Palette.ActiveSwatch,
|
|
|
|
}
|
|
|
|
|
2019-06-27 05:44:08 +00:00
|
|
|
// If the user is holding the mouse down over one spot and not
|
|
|
|
// moving, don't do anything. The pixel has already been set and
|
|
|
|
// needless writes to the map cause needless cache rewrites etc.
|
|
|
|
if lastPixel != nil {
|
|
|
|
if pixel.X == lastPixel.X && pixel.Y == lastPixel.Y {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-21 00:08:20 +00:00
|
|
|
// Append unique new pixels.
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
if lastPixel != nil || lastPixel != pixel {
|
|
|
|
// Draw the pixels in between.
|
|
|
|
if lastPixel != nil && lastPixel != pixel {
|
2019-07-14 21:18:44 +00:00
|
|
|
for point := range render.IterLine(lastPixel.Point(), pixel.Point()) {
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
w.currentStroke.AddPoint(point)
|
2018-10-21 00:08:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
w.lastPixel = pixel
|
|
|
|
|
2019-07-03 23:22:30 +00:00
|
|
|
// Save the pixel in the current stroke.
|
|
|
|
w.currentStroke.AddPoint(render.Point{
|
|
|
|
X: cursor.X,
|
|
|
|
Y: cursor.Y,
|
|
|
|
})
|
2018-10-21 00:08:20 +00:00
|
|
|
}
|
|
|
|
} else {
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
w.commitStroke(w.Tool, true)
|
2018-10-21 00:08:20 +00:00
|
|
|
}
|
2019-07-04 00:19:25 +00:00
|
|
|
case drawtool.LineTool:
|
|
|
|
// If no swatch is active, do nothing with mouse clicks.
|
|
|
|
if w.Palette.ActiveSwatch == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clicking? Log all the pixels while doing so.
|
2019-12-22 22:11:01 +00:00
|
|
|
if ev.Button1 {
|
2019-07-04 00:19:25 +00:00
|
|
|
// Initialize a new Stroke for this atomic drawing operation?
|
|
|
|
if w.currentStroke == nil {
|
|
|
|
w.currentStroke = drawtool.NewStroke(drawtool.Line, w.Palette.ActiveSwatch.Color)
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
w.currentStroke.Thickness = w.BrushSize
|
2019-07-04 00:19:25 +00:00
|
|
|
w.currentStroke.ExtraData = w.Palette.ActiveSwatch
|
|
|
|
w.currentStroke.PointA = render.NewPoint(cursor.X, cursor.Y)
|
|
|
|
w.AddStroke(w.currentStroke)
|
|
|
|
}
|
|
|
|
|
|
|
|
w.currentStroke.PointB = render.NewPoint(cursor.X, cursor.Y)
|
|
|
|
} else {
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
w.commitStroke(w.Tool, true)
|
2019-07-04 00:19:25 +00:00
|
|
|
}
|
|
|
|
case drawtool.RectTool:
|
|
|
|
// If no swatch is active, do nothing with mouse clicks.
|
|
|
|
if w.Palette.ActiveSwatch == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clicking? Log all the pixels while doing so.
|
2019-12-22 22:11:01 +00:00
|
|
|
if ev.Button1 {
|
2019-07-04 00:19:25 +00:00
|
|
|
// Initialize a new Stroke for this atomic drawing operation?
|
|
|
|
if w.currentStroke == nil {
|
|
|
|
w.currentStroke = drawtool.NewStroke(drawtool.Rectangle, w.Palette.ActiveSwatch.Color)
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
w.currentStroke.Thickness = w.BrushSize
|
2019-07-04 00:19:25 +00:00
|
|
|
w.currentStroke.ExtraData = w.Palette.ActiveSwatch
|
|
|
|
w.currentStroke.PointA = render.NewPoint(cursor.X, cursor.Y)
|
|
|
|
w.AddStroke(w.currentStroke)
|
|
|
|
}
|
|
|
|
|
2019-07-14 21:18:44 +00:00
|
|
|
w.currentStroke.PointB = render.NewPoint(cursor.X, cursor.Y)
|
|
|
|
} else {
|
|
|
|
w.commitStroke(w.Tool, true)
|
|
|
|
}
|
|
|
|
case drawtool.EllipseTool:
|
|
|
|
if w.Palette.ActiveSwatch == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-12-22 22:11:01 +00:00
|
|
|
if ev.Button1 {
|
2019-07-14 21:18:44 +00:00
|
|
|
if w.currentStroke == nil {
|
|
|
|
w.currentStroke = drawtool.NewStroke(drawtool.Ellipse, w.Palette.ActiveSwatch.Color)
|
|
|
|
w.currentStroke.Thickness = w.BrushSize
|
|
|
|
w.currentStroke.ExtraData = w.Palette.ActiveSwatch
|
|
|
|
w.currentStroke.PointA = render.NewPoint(cursor.X, cursor.Y)
|
|
|
|
w.AddStroke(w.currentStroke)
|
|
|
|
}
|
|
|
|
|
2019-07-04 00:19:25 +00:00
|
|
|
w.currentStroke.PointB = render.NewPoint(cursor.X, cursor.Y)
|
|
|
|
} else {
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
w.commitStroke(w.Tool, true)
|
|
|
|
}
|
|
|
|
case drawtool.EraserTool:
|
|
|
|
// Clicking? Log all the pixels while doing so.
|
2019-12-22 22:11:01 +00:00
|
|
|
if ev.Button1 {
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
// Initialize a new Stroke for this atomic drawing operation?
|
|
|
|
if w.currentStroke == nil {
|
|
|
|
// The color is white, will look like white-out that covers the
|
|
|
|
// wallpaper during the stroke.
|
|
|
|
w.currentStroke = drawtool.NewStroke(drawtool.Eraser, render.White)
|
|
|
|
w.currentStroke.Thickness = w.BrushSize
|
|
|
|
w.AddStroke(w.currentStroke)
|
|
|
|
}
|
|
|
|
|
|
|
|
lastPixel := w.lastPixel
|
|
|
|
pixel := &level.Pixel{
|
|
|
|
X: cursor.X,
|
|
|
|
Y: cursor.Y,
|
|
|
|
Swatch: w.Palette.ActiveSwatch,
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the user is holding the mouse down over one spot and not
|
|
|
|
// moving, don't do anything. The pixel has already been set and
|
|
|
|
// needless writes to the map cause needless cache rewrites etc.
|
|
|
|
if lastPixel != nil {
|
|
|
|
if pixel.X == lastPixel.X && pixel.Y == lastPixel.Y {
|
|
|
|
break
|
2019-07-04 00:19:25 +00:00
|
|
|
}
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
}
|
2019-07-04 00:19:25 +00:00
|
|
|
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
// Append unique new pixels.
|
|
|
|
if lastPixel == nil || lastPixel != pixel {
|
|
|
|
if lastPixel != nil && lastPixel != pixel {
|
2019-07-14 21:18:44 +00:00
|
|
|
for point := range render.IterLine(lastPixel.Point(), pixel.Point()) {
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
w.currentStroke.AddPoint(point)
|
|
|
|
}
|
2019-07-04 00:19:25 +00:00
|
|
|
}
|
|
|
|
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
w.lastPixel = pixel
|
|
|
|
w.currentStroke.AddPoint(render.Point{
|
|
|
|
X: cursor.X,
|
|
|
|
Y: cursor.Y,
|
|
|
|
})
|
2019-07-04 00:19:25 +00:00
|
|
|
}
|
Eraser Tool, Brush Sizes
* Implement Brush Sizes for drawtool.Stroke and add a UI to the tools panel
to control the brush size.
* Brush sizes: 1, 2, 4, 8, 16, 24, 32, 48, 64
* Add the Eraser Tool to editor mode. It uses a default brush size of 16
and a max size of 32 due to some performance issues.
* The Undo/Redo system now remembers the original color of pixels when
you change them, so that Undo will set them back how they were instead
of deleting the pixel entirely. Due to performance issues, this only
happens when your Brush Size is 0 (drawing single-pixel shapes).
* UI: Add an IntVariable option to ui.Label to bind showing the value of
an int reference.
Aforementioned performance issues:
* When we try to remember whole rects of pixels for drawing thick
shapes, it requires a ton of scanning for each step of the shape. Even
de-duplicating pixel checks, tons of extra reads are constantly
checked.
* The Eraser is the only tool that absolutely needs to be able to
remember wiped pixels AND have large brush sizes. The performance
sucks and lags a bit if you erase a lot all at once, but it's a
trade-off for now.
* So pixels aren't remembered when drawing lines in your level with
thick brushes, so the Undo action will simply delete your pixels and not
reset them. Only the Eraser can bring back pixels.
2019-07-12 02:07:46 +00:00
|
|
|
} else {
|
|
|
|
w.commitStroke(w.Tool, true)
|
2019-07-04 00:19:25 +00:00
|
|
|
}
|
|
|
|
case drawtool.ActorTool:
|
2018-10-21 00:08:20 +00:00
|
|
|
// See if any of the actors are below the mouse cursor.
|
|
|
|
var WP = w.WorldIndexAt(cursor)
|
|
|
|
|
|
|
|
var deleteActors = []*level.Actor{}
|
|
|
|
for _, actor := range w.actors {
|
|
|
|
box := render.Rect{
|
|
|
|
X: actor.Actor.Point.X - P.X - w.Scroll.X,
|
|
|
|
Y: actor.Actor.Point.Y - P.Y - w.Scroll.Y,
|
|
|
|
W: actor.Canvas.Size().W,
|
|
|
|
H: actor.Canvas.Size().H,
|
|
|
|
}
|
|
|
|
|
|
|
|
if WP.Inside(box) {
|
|
|
|
actor.Canvas.Configure(ui.Config{
|
|
|
|
BorderSize: 1,
|
|
|
|
BorderColor: render.RGBA(255, 153, 0, 255),
|
|
|
|
BorderStyle: ui.BorderSolid,
|
|
|
|
Background: render.White, // TODO: cuz the border draws a bgcolor
|
|
|
|
})
|
|
|
|
|
|
|
|
// Check for a mouse down event to begin dragging this
|
|
|
|
// canvas around.
|
2019-12-22 22:11:01 +00:00
|
|
|
if ev.Button1 {
|
2018-10-21 00:08:20 +00:00
|
|
|
// Pop this canvas out for the drag/drop.
|
|
|
|
if w.OnDragStart != nil {
|
|
|
|
deleteActors = append(deleteActors, actor.Actor)
|
2019-07-05 23:04:36 +00:00
|
|
|
w.OnDragStart(actor.Actor)
|
2018-10-21 00:08:20 +00:00
|
|
|
}
|
|
|
|
break
|
2019-12-31 02:13:28 +00:00
|
|
|
} else if ev.Button3 {
|
2018-10-21 00:08:20 +00:00
|
|
|
// Right click to delete an actor.
|
|
|
|
deleteActors = append(deleteActors, actor.Actor)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
actor.Canvas.SetBorderSize(0)
|
|
|
|
actor.Canvas.SetBackground(render.RGBA(0, 0, 1, 0)) // TODO
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Change in actor count?
|
|
|
|
if len(deleteActors) > 0 && w.OnDeleteActors != nil {
|
|
|
|
w.OnDeleteActors(deleteActors)
|
|
|
|
}
|
2019-07-04 00:19:25 +00:00
|
|
|
case drawtool.LinkTool:
|
2019-06-23 23:15:09 +00:00
|
|
|
// See if any of the actors are below the mouse cursor.
|
|
|
|
var WP = w.WorldIndexAt(cursor)
|
|
|
|
|
|
|
|
for _, actor := range w.actors {
|
|
|
|
// Permanently color the actor if it's the current subject of the
|
|
|
|
// Link Tool (after 1st click, until 2nd click of other actor)
|
|
|
|
if w.linkFirst == actor {
|
|
|
|
actor.Canvas.Configure(ui.Config{
|
|
|
|
Background: render.RGBA(255, 153, 255, 153),
|
|
|
|
})
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
box := render.Rect{
|
|
|
|
X: actor.Actor.Point.X - P.X - w.Scroll.X,
|
|
|
|
Y: actor.Actor.Point.Y - P.Y - w.Scroll.Y,
|
|
|
|
W: actor.Canvas.Size().W,
|
|
|
|
H: actor.Canvas.Size().H,
|
|
|
|
}
|
|
|
|
|
|
|
|
if WP.Inside(box) {
|
|
|
|
actor.Canvas.Configure(ui.Config{
|
|
|
|
BorderSize: 1,
|
|
|
|
BorderColor: render.RGBA(255, 153, 255, 255),
|
|
|
|
BorderStyle: ui.BorderSolid,
|
|
|
|
Background: render.White, // TODO: cuz the border draws a bgcolor
|
|
|
|
})
|
|
|
|
|
|
|
|
// Click handler to start linking this actor.
|
2019-12-22 22:11:01 +00:00
|
|
|
if ev.Button1 {
|
2019-06-23 23:15:09 +00:00
|
|
|
if err := w.LinkAdd(actor); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-07-10 02:38:37 +00:00
|
|
|
|
|
|
|
// TODO: reset the Button1 state so we don't finish a
|
|
|
|
// link and then LinkAdd the clicked doodad immediately
|
|
|
|
// (causing link chaining)
|
|
|
|
ev.Button1 = false
|
2019-06-23 23:15:09 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
actor.Canvas.SetBorderSize(0)
|
|
|
|
actor.Canvas.SetBackground(render.RGBA(0, 0, 1, 0)) // TODO
|
|
|
|
}
|
|
|
|
}
|
2018-10-21 00:08:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|