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
physics
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.
"P" Key
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

View File

@ -235,3 +235,44 @@ func IterLine(x1, y1, x2, y2 int32) chan Point {
func IterLine2(p1 Point, p2 Point) chan Point {
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) {
ch <- point
}
case Rectangle:
for point := range render.IterRect(s.PointA, s.PointB) {
ch <- point
}
}
close(ch)
}()

View File

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

View File

@ -9,6 +9,7 @@ import (
"git.kirsle.net/apps/doodle/lib/events"
"git.kirsle.net/apps/doodle/lib/render"
"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/level"
"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)
// Switching to Play Mode?
if ev.KeyName.Read() == "p" {
switch ev.KeyName.Read() {
case "p":
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

View File

@ -11,6 +11,7 @@ import (
"git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/branding"
"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/level"
"git.kirsle.net/apps/doodle/pkg/log"
@ -311,7 +312,7 @@ func (u *EditorUI) SetupCanvas(d *Doodle) *uix.Canvas {
b.Actor.AddLink(idA)
// 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)
}

View File

@ -4,9 +4,9 @@ import (
"git.kirsle.net/apps/doodle/lib/render"
"git.kirsle.net/apps/doodle/lib/ui"
"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/log"
"git.kirsle.net/apps/doodle/pkg/uix"
)
// 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) {
if u.paletteTab == "Palette" {
u.Canvas.Tool = uix.PencilTool
u.Canvas.Tool = drawtool.PencilTool
u.PaletteTab.Show()
u.DoodadTab.Hide()
} else {
u.Canvas.Tool = uix.ActorTool
u.Canvas.Tool = drawtool.ActorTool
u.PaletteTab.Hide()
u.DoodadTab.Show()
}

View File

@ -30,7 +30,7 @@ type Canvas struct {
Scrollable bool // Cursor keys will scroll the viewport of this canvas.
// 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
// 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 {
case PencilTool:
case drawtool.PencilTool:
// If no swatch is active, do nothing with mouse clicks.
if w.Palette.ActiveSwatch == nil {
return nil
@ -90,7 +90,73 @@ func (w *Canvas) loopEditable(ev *events.State) error {
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.
var WP = w.WorldIndexAt(cursor)
@ -134,7 +200,7 @@ func (w *Canvas) loopEditable(ev *events.State) error {
if len(deleteActors) > 0 && w.OnDeleteActors != nil {
w.OnDeleteActors(deleteActors)
}
case LinkTool:
case drawtool.LinkTool:
// See if any of the actors are below the mouse cursor.
var WP = w.WorldIndexAt(cursor)

View File

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

View File

@ -94,7 +94,7 @@ func (w *Canvas) presentStrokes(e render.Engine) {
w.drawStrokes(e, strokes)
// 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)
}
}