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:
parent
5d8c5510d8
commit
5a1ec156ca
10
README.md
10
README.md
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -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
|
||||||
ActorTool // drag and move actors
|
LineTool
|
||||||
|
RectTool
|
||||||
|
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",
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user