Editor Mode: Line Tool and Rectangle Tool

* Add support for the LineTool and RectTool while in the EditorMode to
  easily draw straight lines and rectangle outlines.
* Key bindings were added to toggle tools in lieu of a proper UI to
  select the tool from a toolbar.
  * "F" for Pencil (Freehand) Tool (since "P" is for "Playtest")
  * "L" for Line Tool
  * "R" for Rectangle Tool
This commit is contained in:
Noah 2019-07-03 17:19:25 -07:00
parent 5d8c5510d8
commit 5a1ec156ca
11 changed files with 151 additions and 14 deletions

View File

@ -69,6 +69,16 @@ Cursor Keys
Scroll the view of the map around. Scroll the view of the map around.
"P" Key "P" Key
Playtest the current map you're working on. Playtest the current map you're working on.
"F" Key
Switch to the Pencil (Freehand) Tool
"L" Key
Switch to the Line Tool
"R" Key
Switch to the Rectangle Tool
Ctrl-Z
Undo
Ctrl-Y
Redo
``` ```
# Built-In Doodads # Built-In Doodads

View File

@ -235,3 +235,44 @@ func IterLine(x1, y1, x2, y2 int32) chan Point {
func IterLine2(p1 Point, p2 Point) chan Point { func IterLine2(p1 Point, p2 Point) chan Point {
return IterLine(p1.X, p1.Y, p2.X, p2.Y) return IterLine(p1.X, p1.Y, p2.X, p2.Y)
} }
// IterRect loops through all the points forming a rectangle between the
// top-left point and the bottom-right point.
func IterRect(p1, p2 Point) chan Point {
generator := make(chan Point)
go func() {
var (
TopLeft = p1
BottomRight = p2
TopRight = Point{
X: BottomRight.X,
Y: TopLeft.Y,
}
BottomLeft = Point{
X: TopLeft.X,
Y: BottomRight.Y,
}
)
// Trace all four edges and yield it.
var edges = []struct {
A Point
B Point
}{
{TopLeft, TopRight},
{TopLeft, BottomLeft},
{BottomLeft, BottomRight},
{TopRight, BottomRight},
}
for _, edge := range edges {
for pt := range IterLine2(edge.A, edge.B) {
generator <- pt
}
}
close(generator)
}()
return generator
}

View File

@ -61,6 +61,10 @@ func (s *Stroke) IterPoints() chan render.Point {
for point := range render.IterLine2(s.PointA, s.PointB) { for point := range render.IterLine2(s.PointA, s.PointB) {
ch <- point ch <- point
} }
case Rectangle:
for point := range render.IterRect(s.PointA, s.PointB) {
ch <- point
}
} }
close(ch) close(ch)
}() }()

View File

@ -1,4 +1,4 @@
package uix package drawtool
// Tool is a draw mode for an editable Canvas. // Tool is a draw mode for an editable Canvas.
type Tool int type Tool int
@ -6,12 +6,16 @@ type Tool int
// Draw modes for editable Canvas. // Draw modes for editable Canvas.
const ( const (
PencilTool Tool = iota // draw pixels where the mouse clicks PencilTool Tool = iota // draw pixels where the mouse clicks
LineTool
RectTool
ActorTool // drag and move actors ActorTool // drag and move actors
LinkTool LinkTool
) )
var toolNames = []string{ var toolNames = []string{
"Pencil", "Pencil",
"Line",
"Rectangle",
"Doodad", // readable name for ActorTool "Doodad", // readable name for ActorTool
"Link", "Link",
} }

View File

@ -9,6 +9,7 @@ import (
"git.kirsle.net/apps/doodle/lib/events" "git.kirsle.net/apps/doodle/lib/events"
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/apps/doodle/lib/render"
"git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/drawtool"
"git.kirsle.net/apps/doodle/pkg/enum" "git.kirsle.net/apps/doodle/pkg/enum"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"
@ -167,9 +168,18 @@ func (s *EditorScene) Loop(d *Doodle, ev *events.State) error {
s.UI.Loop(ev) s.UI.Loop(ev)
// Switching to Play Mode? // Switching to Play Mode?
if ev.KeyName.Read() == "p" { switch ev.KeyName.Read() {
case "p":
s.Playtest() s.Playtest()
return nil case "l":
d.Flash("Line Tool selected.")
s.UI.Canvas.Tool = drawtool.LineTool
case "f":
d.Flash("Pencil Tool selected.")
s.UI.Canvas.Tool = drawtool.PencilTool
case "r":
d.Flash("Rectangle Tool selected.")
s.UI.Canvas.Tool = drawtool.RectTool
} }
return nil return nil

View File

@ -11,6 +11,7 @@ import (
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/branding" "git.kirsle.net/apps/doodle/pkg/branding"
"git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/drawtool"
"git.kirsle.net/apps/doodle/pkg/enum" "git.kirsle.net/apps/doodle/pkg/enum"
"git.kirsle.net/apps/doodle/pkg/level" "git.kirsle.net/apps/doodle/pkg/level"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"
@ -311,7 +312,7 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
b.Actor.AddLink(idA) b.Actor.AddLink(idA)
// Reset the Link tool. // Reset the Link tool.
drawing.Tool = uix.ActorTool drawing.Tool = drawtool.ActorTool
d.Flash("Linked '%s' and '%s' together", a.Doodad.Title, b.Doodad.Title) d.Flash("Linked '%s' and '%s' together", a.Doodad.Title, b.Doodad.Title)
} }

View File

@ -4,9 +4,9 @@ import (
"git.kirsle.net/apps/doodle/lib/render" "git.kirsle.net/apps/doodle/lib/render"
"git.kirsle.net/apps/doodle/lib/ui" "git.kirsle.net/apps/doodle/lib/ui"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/drawtool"
"git.kirsle.net/apps/doodle/pkg/enum" "git.kirsle.net/apps/doodle/pkg/enum"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"
"git.kirsle.net/apps/doodle/pkg/uix"
) )
// SetupPalette sets up the palette panel. // SetupPalette sets up the palette panel.
@ -31,11 +31,11 @@ func (u *EditorUI) SetupPalette(d *Doodle) *ui.Window {
})) }))
tab.Handle(ui.Click, func(p render.Point) { tab.Handle(ui.Click, func(p render.Point) {
if u.paletteTab == "Palette" { if u.paletteTab == "Palette" {
u.Canvas.Tool = uix.PencilTool u.Canvas.Tool = drawtool.PencilTool
u.PaletteTab.Show() u.PaletteTab.Show()
u.DoodadTab.Hide() u.DoodadTab.Hide()
} else { } else {
u.Canvas.Tool = uix.ActorTool u.Canvas.Tool = drawtool.ActorTool
u.PaletteTab.Hide() u.PaletteTab.Hide()
u.DoodadTab.Show() u.DoodadTab.Show()
} }

View File

@ -30,7 +30,7 @@ type Canvas struct {
Scrollable bool // Cursor keys will scroll the viewport of this canvas. Scrollable bool // Cursor keys will scroll the viewport of this canvas.
// Selected draw tool/mode, default Pencil, for editable canvases. // Selected draw tool/mode, default Pencil, for editable canvases.
Tool Tool Tool drawtool.Tool
// MaskColor will force every pixel to render as this color regardless of // MaskColor will force every pixel to render as this color regardless of
// the palette index of that pixel. Otherwise pixels behave the same and // the palette index of that pixel. Otherwise pixels behave the same and

View File

@ -21,7 +21,7 @@ func (w *Canvas) loopEditable(ev *events.State) error {
) )
switch w.Tool { switch w.Tool {
case PencilTool: case drawtool.PencilTool:
// If no swatch is active, do nothing with mouse clicks. // If no swatch is active, do nothing with mouse clicks.
if w.Palette.ActiveSwatch == nil { if w.Palette.ActiveSwatch == nil {
return nil return nil
@ -90,7 +90,73 @@ func (w *Canvas) loopEditable(ev *events.State) error {
w.lastPixel = nil w.lastPixel = nil
} }
case ActorTool: 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.
if ev.Button1.Now {
// Initialize a new Stroke for this atomic drawing operation?
if w.currentStroke == nil {
w.currentStroke = drawtool.NewStroke(drawtool.Line, w.Palette.ActiveSwatch.Color)
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 {
// Mouse released, commit the points to the drawing.
if w.currentStroke != nil {
for pt := range render.IterLine2(w.currentStroke.PointA, w.currentStroke.PointB) {
w.chunks.Set(pt, w.Palette.ActiveSwatch)
}
// Add the stroke to level history.
if w.level != nil {
w.level.UndoHistory.AddStroke(w.currentStroke)
}
w.RemoveStroke(w.currentStroke)
w.currentStroke = nil
}
}
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.
if ev.Button1.Now {
// Initialize a new Stroke for this atomic drawing operation?
if w.currentStroke == nil {
w.currentStroke = drawtool.NewStroke(drawtool.Rectangle, w.Palette.ActiveSwatch.Color)
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 {
// Mouse released, commit the points to the drawing.
if w.currentStroke != nil {
for pt := range render.IterRect(w.currentStroke.PointA, w.currentStroke.PointB) {
w.chunks.Set(pt, w.Palette.ActiveSwatch)
}
// Add the stroke to level history.
if w.level != nil {
w.level.UndoHistory.AddStroke(w.currentStroke)
}
w.RemoveStroke(w.currentStroke)
w.currentStroke = nil
}
}
case drawtool.ActorTool:
// See if any of the actors are below the mouse cursor. // See if any of the actors are below the mouse cursor.
var WP = w.WorldIndexAt(cursor) var WP = w.WorldIndexAt(cursor)
@ -134,7 +200,7 @@ func (w *Canvas) loopEditable(ev *events.State) error {
if len(deleteActors) > 0 && w.OnDeleteActors != nil { if len(deleteActors) > 0 && w.OnDeleteActors != nil {
w.OnDeleteActors(deleteActors) w.OnDeleteActors(deleteActors)
} }
case LinkTool: case drawtool.LinkTool:
// See if any of the actors are below the mouse cursor. // See if any of the actors are below the mouse cursor.
var WP = w.WorldIndexAt(cursor) var WP = w.WorldIndexAt(cursor)

View File

@ -3,12 +3,13 @@ package uix
import ( import (
"errors" "errors"
"git.kirsle.net/apps/doodle/pkg/drawtool"
"git.kirsle.net/apps/doodle/pkg/shmem" "git.kirsle.net/apps/doodle/pkg/shmem"
) )
// LinkStart initializes the Link tool. // LinkStart initializes the Link tool.
func (w *Canvas) LinkStart() { func (w *Canvas) LinkStart() {
w.Tool = LinkTool w.Tool = drawtool.LinkTool
w.linkFirst = nil w.linkFirst = nil
} }

View File

@ -94,7 +94,7 @@ func (w *Canvas) presentStrokes(e render.Engine) {
w.drawStrokes(e, strokes) w.drawStrokes(e, strokes)
// Dynamic actor links visible in the ActorTool and LinkTool. // Dynamic actor links visible in the ActorTool and LinkTool.
if w.Tool == ActorTool || w.Tool == LinkTool { if w.Tool == drawtool.ActorTool || w.Tool == drawtool.LinkTool {
w.presentActorLinks(e) w.presentActorLinks(e)
} }
} }