WIP Zoom Tool
* Added Feature Flag support, run doodle with --experimental to enable all flags. Eraser Tool is behind a feature flag now. * + and - on the top row of keyboard keys will zoom the drawing in and out in Edit Mode. The wallpaper zooms nicely enough, but level chunkers need work. * A View menu is added with Zoom in/out, reset zoom, and scroll to origin options. The whole menu is behind the Zoom feature flag. * Update README with lots of details for fun debug mode options to play around with.
This commit is contained in:
parent
24aef28a0d
commit
6e40d58010
91
README.md
91
README.md
|
@ -142,7 +142,9 @@ A brief introduction to the built-in doodads available so far:
|
|||
|
||||
# Developer Console
|
||||
|
||||
Press `Enter` at any time to open the developer console.
|
||||
Press `Enter` at any time to open the developer console. The console
|
||||
provides commands and advanced functionality, and is also where cheat
|
||||
codes can be entered.
|
||||
|
||||
Commands supported:
|
||||
|
||||
|
@ -154,8 +156,8 @@ new
|
|||
Show the "New Level" screen to start editing a new map.
|
||||
|
||||
save [filename]
|
||||
Save the current map in Edit Mode. The filename is required if the map has
|
||||
not been saved yet.
|
||||
Save the current map in Edit Mode. The filename is required
|
||||
if the map has not been saved yet.
|
||||
|
||||
edit [filename]
|
||||
Open a map or doodad in Edit Mode.
|
||||
|
@ -166,14 +168,39 @@ play [filename]
|
|||
echo <text>
|
||||
Flash a message to the console.
|
||||
|
||||
alert <text>
|
||||
Test an alert box modal with a custom message.
|
||||
|
||||
clear
|
||||
Clear the console output history.
|
||||
|
||||
exit
|
||||
quit
|
||||
Close the developer console.
|
||||
|
||||
boolProp <property> <true/false>
|
||||
Toggle certain boolean settings in the game. Most of these
|
||||
are debugging related. `boolProp list` shows the available
|
||||
props.
|
||||
|
||||
eval <expression>
|
||||
$ <expression>
|
||||
Execute a line of JavaScript code in the console. Several
|
||||
of the game's core data types are available here; `d` is
|
||||
the master game struct; d.Scene is the pointer to the
|
||||
current scene. d.Scene.UI.Canvas may point to the level edit
|
||||
canvas in Editor Mode. Object.keys() can enumerate public
|
||||
functions and variables.
|
||||
|
||||
repl
|
||||
Enters an interactive JavaScript shell, where the console
|
||||
stays open and pre-fills a $ prompt for subsequent commands.
|
||||
```
|
||||
|
||||
The JavaScript console is a feature for advanced users and was
|
||||
used while developing the game. Cool things you can do with it
|
||||
may be documented elsewhere.
|
||||
|
||||
## Cheat Codes
|
||||
|
||||
The following cheats can be entered into the developer console.
|
||||
|
@ -205,6 +232,64 @@ Experimental:
|
|||
The player character must always remain on screen though so you can't
|
||||
scroll too far away.
|
||||
|
||||
Unsupported shell commands (here be dragons):
|
||||
|
||||
* `reload`: reloads the current 'scene' within the game engine, using the
|
||||
existing scene's data. If playing a level this will start the level over.
|
||||
If editing a level this will reload the editor, but your recent unsaved
|
||||
changes _should_ be left intact.
|
||||
* `guitest`: loads the GUI Test scene within the game. This was where I
|
||||
was testing UI widgets early on; not well maintained; the `close`
|
||||
command can get you out of it.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
To enable certain debug features or customize some aspects of the game,
|
||||
run it with environment variables like the following:
|
||||
|
||||
```bash
|
||||
# Draw a semi-transparent yellow background over all level chunks
|
||||
$ DEBUG_CHUNK_COLOR=FFFF0066 ./doodle
|
||||
|
||||
# Set a window size for the application
|
||||
# (equivalent to: doodle --window 1024x768)
|
||||
$ DOODLE_W=1024 DOODLE_H=768 ./doodle
|
||||
|
||||
# Turn on lots of fun debug features.
|
||||
$ DEBUG_CANVAS_LABEL=1 DEBUG_CHUNK_COLOR=FFFF00AA \
|
||||
DEBUG_CANVAS_BORDER=FF0 ./doodle
|
||||
```
|
||||
|
||||
Supported variables include:
|
||||
|
||||
* `DOODLE_W` and `DOODLE_H` set the width and height of the application
|
||||
window. Equivalent to the `--window` command-line option.
|
||||
* `D_SCROLL_SPEED` (int): tune the canvas scrolling speed. Default might
|
||||
be around 8 or so.
|
||||
* `D_DOODAD_SIZE` (int): default size for newly created doodads
|
||||
* `D_SHELL_BG` (color): set the background color of the developer console
|
||||
* `D_SHELL_FG` (color): text color for the developer console
|
||||
* `D_SHELL_PC` (color): color for the shell prompt text
|
||||
* `D_SHELL_LN` (int): set the number of lines of output history the
|
||||
console will show. This dictates how 'tall' it rises from the bottom
|
||||
of the screen. Large values will cover the entire screen with console
|
||||
whenever the shell is open.
|
||||
* `D_SHELL_FS` (int): set the font size for the developer shell. Default
|
||||
is about 16. This also affects the size of "flashed" text that appears
|
||||
at the bottom of the screen.
|
||||
* `DEBUG_CHUNK_COLOR` (color): set a background color over each chunk
|
||||
of drawing (level or doodad). A solid color will completely block out
|
||||
the wallpaper; semitransparent is best.
|
||||
* `DEBUG_CANVAS_BORDER` (color): the game will draw an insert colored
|
||||
border around every "Canvas" widget (drawing) on the screen. The level
|
||||
itself is a Canvas and every individual Doodad or actor in the level is
|
||||
its own Canvas.
|
||||
* `DEBUG_CANVAS_LABEL` (bool): draws a text label over every Canvas
|
||||
widget on the screen, showing its name or Actor ID and some properties,
|
||||
such as Level Position (LP) and World Position (WP) of actors within
|
||||
a level. LP is their placement in the level file and WP is their
|
||||
actual position now (in case it moves).
|
||||
|
||||
# Author
|
||||
|
||||
Copyright (C) 2020 Noah Petherbridge. All rights reserved.
|
||||
|
|
|
@ -77,6 +77,10 @@ func main() {
|
|||
Name: "guitest",
|
||||
Usage: "enter the GUI Test scene on startup",
|
||||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "experimental",
|
||||
Usage: "enable experimental Feature Flags",
|
||||
},
|
||||
}
|
||||
|
||||
app.Action = func(c *cli.Context) error {
|
||||
|
@ -92,6 +96,11 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
// Enable feature flags?
|
||||
if c.Bool("experimental") {
|
||||
balance.FeaturesOn()
|
||||
}
|
||||
|
||||
// SDL engine.
|
||||
engine := sdl.New(
|
||||
fmt.Sprintf("%s v%s", branding.AppName, branding.Version),
|
||||
|
|
15
pkg/balance/feature_flags.go
Normal file
15
pkg/balance/feature_flags.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package balance
|
||||
|
||||
// Feature Flags to turn on/off experimental content.
|
||||
var Feature = feature{
|
||||
Zoom: false,
|
||||
}
|
||||
|
||||
// FeaturesOn turns on all feature flags, from CLI --experimental option.
|
||||
func FeaturesOn() {
|
||||
Feature.Zoom = true
|
||||
}
|
||||
|
||||
type feature struct {
|
||||
Zoom bool
|
||||
}
|
|
@ -137,7 +137,11 @@ func (d *Doodle) Run() error {
|
|||
} else {
|
||||
// Global event handlers.
|
||||
if keybind.Shutdown(ev) {
|
||||
if d.Debug { // fast exit in -debug mode.
|
||||
d.running = false
|
||||
} else {
|
||||
d.ConfirmExit()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -202,6 +202,23 @@ func (s *EditorScene) Loop(d *Doodle, ev *event.State) error {
|
|||
s.UI.Canvas.RedoStroke()
|
||||
}
|
||||
|
||||
// Zoom in/out.
|
||||
if balance.Feature.Zoom {
|
||||
if keybind.ZoomIn(ev) {
|
||||
d.Flash("Zoom in")
|
||||
s.UI.Canvas.Zoom++
|
||||
} else if keybind.ZoomOut(ev) {
|
||||
d.Flash("Zoom out")
|
||||
s.UI.Canvas.Zoom--
|
||||
} else if keybind.ZoomReset(ev) {
|
||||
d.Flash("Reset zoom")
|
||||
s.UI.Canvas.Zoom = 0
|
||||
} else if keybind.Origin(ev) {
|
||||
d.Flash("Scrolled back to level origin (0,0)")
|
||||
s.UI.Canvas.ScrollTo(render.Origin)
|
||||
}
|
||||
}
|
||||
|
||||
s.UI.Loop(ev)
|
||||
|
||||
// Switching to Play Mode?
|
||||
|
|
|
@ -547,8 +547,12 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
editMenu.AddItemAccel("Redo", "Ctrl-Y", func() {
|
||||
u.Canvas.RedoStroke()
|
||||
})
|
||||
editMenu.AddSeparator()
|
||||
editMenu.AddItem("Level options", func() {
|
||||
|
||||
////////
|
||||
// Level menu
|
||||
if u.Scene.DrawingType == enum.LevelDrawing {
|
||||
levelMenu := menu.AddMenu("Level")
|
||||
levelMenu.AddItem("Page settings", func() {
|
||||
log.Info("Opening the window")
|
||||
|
||||
// Open the New Level window in edit-settings mode.
|
||||
|
@ -557,16 +561,29 @@ func (u *EditorUI) SetupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
u.SetupPopups(u.d)
|
||||
u.levelSettingsWindow.Show()
|
||||
})
|
||||
|
||||
////////
|
||||
// Level menu
|
||||
if u.Scene.DrawingType == enum.LevelDrawing {
|
||||
levelMenu := menu.AddMenu("Level")
|
||||
levelMenu.AddItemAccel("Playtest", "P", func() {
|
||||
u.Scene.Playtest()
|
||||
})
|
||||
}
|
||||
|
||||
////////
|
||||
// View menu
|
||||
if balance.Feature.Zoom {
|
||||
viewMenu := menu.AddMenu("View")
|
||||
viewMenu.AddItemAccel("Zoom in", "+", func() {
|
||||
u.Canvas.Zoom++
|
||||
})
|
||||
viewMenu.AddItemAccel("Zoom out", "-", func() {
|
||||
u.Canvas.Zoom--
|
||||
})
|
||||
viewMenu.AddItemAccel("Reset zoom", "1", func() {
|
||||
u.Canvas.Zoom = 0
|
||||
})
|
||||
viewMenu.AddItemAccel("Scroll drawing to origin", "0", func() {
|
||||
u.Canvas.ScrollTo(render.Origin)
|
||||
})
|
||||
}
|
||||
|
||||
////////
|
||||
// Tools menu
|
||||
toolMenu := menu.AddMenu("Tools")
|
||||
|
|
|
@ -45,6 +45,26 @@ func Redo(ev *event.State) bool {
|
|||
return ev.Ctrl && ev.KeyDown("y")
|
||||
}
|
||||
|
||||
// ZoomIn (+)
|
||||
func ZoomIn(ev *event.State) bool {
|
||||
return ev.KeyDown("=") || ev.KeyDown("+")
|
||||
}
|
||||
|
||||
// ZoomOut (-)
|
||||
func ZoomOut(ev *event.State) bool {
|
||||
return ev.KeyDown("-")
|
||||
}
|
||||
|
||||
// ZoomReset (1)
|
||||
func ZoomReset(ev *event.State) bool {
|
||||
return ev.KeyDown("1")
|
||||
}
|
||||
|
||||
// Origin (0) -- scrolls the canvas back to 0,0 in Editor Mode.
|
||||
func Origin(ev *event.State) bool {
|
||||
return ev.KeyDown("0")
|
||||
}
|
||||
|
||||
// GotoPlay (P) play tests the current level in the editor.
|
||||
func GotoPlay(ev *event.State) bool {
|
||||
return ev.KeyDown("p")
|
||||
|
|
|
@ -29,6 +29,7 @@ type Canvas struct {
|
|||
// NewCanvas() with editable=true, they are both enabled.
|
||||
Editable bool // Clicking will edit pixels of this canvas.
|
||||
Scrollable bool // Cursor keys will scroll the viewport of this canvas.
|
||||
Zoom int // Zoom level on the canvas.
|
||||
|
||||
// Selected draw tool/mode, default Pencil, for editable canvases.
|
||||
Tool drawtool.Tool
|
||||
|
@ -289,10 +290,17 @@ func (w *Canvas) ViewportRelative() render.Rect {
|
|||
// the mouse cursor.
|
||||
func (w *Canvas) WorldIndexAt(screenPixel render.Point) render.Point {
|
||||
var P = ui.AbsolutePosition(w)
|
||||
return render.Point{
|
||||
world := render.Point{
|
||||
X: screenPixel.X - P.X - w.Scroll.X,
|
||||
Y: screenPixel.Y - P.Y - w.Scroll.Y,
|
||||
}
|
||||
|
||||
// Handle Zoomies
|
||||
if w.Zoom != 0 {
|
||||
world.X = w.ZoomMultiply(world.X)
|
||||
world.Y = w.ZoomMultiply(world.Y)
|
||||
}
|
||||
return world
|
||||
}
|
||||
|
||||
// Chunker returns the underlying Chunker object.
|
||||
|
|
|
@ -28,6 +28,10 @@ func (w *Canvas) commitStroke(tool drawtool.Tool, addHistory bool) {
|
|||
return
|
||||
}
|
||||
|
||||
// Zoom the stroke coordinates (this modifies the pointer)
|
||||
zStroke := w.ZoomStroke(w.currentStroke)
|
||||
_ = zStroke
|
||||
|
||||
// Mark the canvas as modified.
|
||||
w.modified = true
|
||||
|
||||
|
|
|
@ -42,9 +42,16 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
|
|||
} else {
|
||||
tex = chunk.Texture(e)
|
||||
}
|
||||
|
||||
// Zoom in the texture.
|
||||
texSize := tex.Size()
|
||||
if w.Zoom != 0 {
|
||||
texSize.W = w.ZoomMultiply(texSize.W)
|
||||
texSize.H = w.ZoomMultiply(texSize.H)
|
||||
}
|
||||
src := render.Rect{
|
||||
W: tex.Size().W,
|
||||
H: tex.Size().H,
|
||||
W: texSize.W,
|
||||
H: texSize.H,
|
||||
}
|
||||
|
||||
// If the source bitmap is already bigger than the Canvas widget
|
||||
|
@ -69,6 +76,16 @@ func (w *Canvas) Present(e render.Engine, p render.Point) {
|
|||
H: src.H,
|
||||
}
|
||||
|
||||
// Zoom the destination rect.
|
||||
if w.Zoom != 0 {
|
||||
// dst.X += int(w.GetZoomMultiplier())
|
||||
// dst.Y += int(w.GetZoomMultiplier())
|
||||
// dst.X = w.ZoomMultiply(dst.X)
|
||||
// dst.Y = w.ZoomMultiply(dst.Y)
|
||||
// dst.W = w.ZoomMultiply(dst.W)
|
||||
// dst.H = w.ZoomMultiply(dst.H)
|
||||
}
|
||||
|
||||
// TODO: all this shit is in TrimBox(), make it DRY
|
||||
|
||||
// If the destination width will cause it to overflow the widget
|
||||
|
|
|
@ -2,6 +2,7 @@ package uix
|
|||
|
||||
import (
|
||||
"git.kirsle.net/apps/doodle/pkg/level"
|
||||
"git.kirsle.net/apps/doodle/pkg/log"
|
||||
"git.kirsle.net/apps/doodle/pkg/wallpaper"
|
||||
"git.kirsle.net/go/render"
|
||||
)
|
||||
|
@ -70,45 +71,96 @@ func (w *Canvas) loopContainActorsInsideLevel(a *Actor) {
|
|||
}
|
||||
|
||||
// PresentWallpaper draws the wallpaper.
|
||||
// Point p is the one given to Canvas.Present(), i.e., the position of the
|
||||
// top-left corner of the Canvas widget relative to the application window.
|
||||
func (w *Canvas) PresentWallpaper(e render.Engine, p render.Point) error {
|
||||
var (
|
||||
wp = w.wallpaper
|
||||
S = w.Size()
|
||||
size = wp.corner.Size()
|
||||
|
||||
// Get the relative viewport of world coordinates looked at by the canvas.
|
||||
// The X,Y values are the negative Scroll value
|
||||
// The W,H values are the Canvas size same as var S above.
|
||||
Viewport = w.ViewportRelative()
|
||||
|
||||
// origin and limit seem to be the boundaries of where on screen
|
||||
// we are rendering inside.
|
||||
origin = render.Point{
|
||||
X: p.X + w.Scroll.X + w.BoxThickness(1),
|
||||
Y: p.Y + w.Scroll.Y + w.BoxThickness(1),
|
||||
}
|
||||
limit = render.Point{
|
||||
// NOTE: we add + the texture size so we would actually draw one
|
||||
// full extra texture out-of-bounds for the repeating backgrounds.
|
||||
// This is cuz for scrolling we offset the draw spot on a loop.
|
||||
X: origin.X + S.W - w.BoxThickness(1) + size.W,
|
||||
Y: origin.Y + S.H - w.BoxThickness(1) + size.H,
|
||||
X: p.X + w.Scroll.X, // + w.BoxThickness(1),
|
||||
Y: p.Y + w.Scroll.Y, // + w.BoxThickness(1),
|
||||
}
|
||||
limit render.Point // TBD later
|
||||
)
|
||||
|
||||
// Grow or shrink the render limit if we're zoomed.
|
||||
if w.Zoom != 0 {
|
||||
// I was surprised to discover that just zooming the texture
|
||||
// quadrant size handled most of the problem! For reference, the
|
||||
// Blueprint wallpaper has a size of 120x120 for the tiling pattern.
|
||||
size.H = w.ZoomMultiply(size.H)
|
||||
size.W = w.ZoomMultiply(size.W)
|
||||
}
|
||||
|
||||
// SCRATCH
|
||||
// at bootup, scroll position 0,0:
|
||||
// origin=44,20 p=44,20 p=relative to application window
|
||||
// scroll right and down to -60,-60:
|
||||
// origin=-16,-40 p=44,20 and looks good in that direction
|
||||
// scroll left and up to 60,60:
|
||||
// origin=104,80 p=44,20
|
||||
// becomes origin=44,20 p=44,20 d=-16,-40
|
||||
// the latter case is handled below. walking thru:
|
||||
// if o(104) > p(44):
|
||||
// while o(104) > p(44):
|
||||
// o -= size(120) of texture block
|
||||
// o is now -16,-40
|
||||
// while o(-16) > p(44): it's not; break
|
||||
// dx = o(-16)
|
||||
// origin.X = p.X
|
||||
// (becomes origin=44,20 p=44,20 d=-16,-40)
|
||||
//
|
||||
// The visual bug is: if you scroll left or up on an Unbounded level from
|
||||
// the origin (0, 0), the tiling of the wallpaper jumps to the right and
|
||||
// down by an offset of 44x20 pixels.
|
||||
//
|
||||
// what is meant to happen:
|
||||
// -
|
||||
|
||||
// For tiled textures, compute the offset amount. If we are scrolled away
|
||||
// from the Origin (0,0) we find out by how far (subtract full tile sizes)
|
||||
// and use the remainder as an offset for drawing the tiles.
|
||||
// p = position on screen of the Canvas widget
|
||||
// origin = p.X + Scroll.X, p.Y + scroll.Y
|
||||
// note: negative Scroll values means to the right and down
|
||||
var dx, dy int
|
||||
if origin.X > p.X {
|
||||
for origin.X > p.X && origin.X > size.W {
|
||||
origin.X -= size.W
|
||||
}
|
||||
// View is scrolled leftward (into negative world coordinates)
|
||||
dx = origin.X
|
||||
origin.X = p.X
|
||||
for dx > p.X {
|
||||
dx -= size.W
|
||||
}
|
||||
origin.X = 0 // note: origin 0,0 will be the corner of the app window
|
||||
}
|
||||
if origin.Y > p.Y {
|
||||
for origin.Y > p.Y && origin.Y > size.H {
|
||||
origin.Y -= size.H
|
||||
}
|
||||
// View is scrolled upward (into negative world coordinates)
|
||||
dy = origin.Y
|
||||
origin.Y = p.Y
|
||||
for dy > p.Y {
|
||||
dy -= size.H
|
||||
}
|
||||
origin.Y = 0
|
||||
}
|
||||
|
||||
// And capping the scroll delta in the other direction.
|
||||
limit = render.Point{
|
||||
// NOTE: we add + the texture size so we would actually draw one
|
||||
// full extra texture out-of-bounds for the repeating backgrounds.
|
||||
// This is cuz for scrolling we offset the draw spot on a loop.
|
||||
X: origin.X + S.W + size.W,
|
||||
Y: origin.Y + S.H + size.H,
|
||||
}
|
||||
|
||||
// And capping the scroll delta in the other direction. Always draw
|
||||
// pixels until the Canvas size is covered.
|
||||
if limit.X < S.W {
|
||||
limit.X = S.W
|
||||
}
|
||||
|
@ -117,10 +169,12 @@ func (w *Canvas) PresentWallpaper(e render.Engine, p render.Point) error {
|
|||
limit.Y = S.H
|
||||
}
|
||||
|
||||
// TODO: was still getting some slight flicker on the right and bottom
|
||||
// when scrolling.. add a bit extra margin.
|
||||
limit.X += size.W
|
||||
limit.Y += size.H
|
||||
|
||||
// Tile the repeat texture.
|
||||
// Tile the repeat texture. Start from 1 full wallpaper tile out of bounds
|
||||
for x := origin.X - size.W; x < limit.X; x += size.W {
|
||||
for y := origin.Y - size.H; y < limit.Y; y += size.H {
|
||||
src := render.Rect{
|
||||
|
@ -134,8 +188,20 @@ func (w *Canvas) PresentWallpaper(e render.Engine, p render.Point) error {
|
|||
H: src.H,
|
||||
}
|
||||
|
||||
// Zoom the output texture.
|
||||
if w.Zoom != 0 {
|
||||
// dst.X = w.ZoomMultiply(dst.X - p.X)
|
||||
// dst.Y = w.ZoomMultiply(dst.Y - p.Y)
|
||||
// dst.W = w.ZoomMultiply(dst.W)
|
||||
// dst.H = w.ZoomMultiply(dst.H)
|
||||
}
|
||||
|
||||
// Trim the edges of the destination box, like in canvas.go#Present
|
||||
odst := dst
|
||||
render.TrimBox(&src, &dst, p, S, w.BoxThickness(1))
|
||||
if dst.W == 0 {
|
||||
log.Error("TrimBoxed! %s => %s", odst, dst)
|
||||
}
|
||||
|
||||
e.Copy(wp.repeat, src, dst)
|
||||
}
|
||||
|
@ -154,6 +220,15 @@ func (w *Canvas) PresentWallpaper(e render.Engine, p render.Point) error {
|
|||
W: src.W,
|
||||
H: src.H,
|
||||
}
|
||||
|
||||
// Zoom the output texture.
|
||||
if w.Zoom != 0 {
|
||||
// dst.X = w.ZoomMultiply(dst.X - origin.X)
|
||||
// dst.Y = w.ZoomMultiply(dst.Y - origin.Y)
|
||||
// dst.W = w.ZoomMultiply(dst.W)
|
||||
// dst.H = w.ZoomMultiply(dst.H)
|
||||
}
|
||||
|
||||
render.TrimBox(&src, &dst, p, S, w.BoxThickness(1))
|
||||
e.Copy(wp.left, src, dst)
|
||||
}
|
||||
|
@ -170,6 +245,15 @@ func (w *Canvas) PresentWallpaper(e render.Engine, p render.Point) error {
|
|||
W: src.W,
|
||||
H: src.H,
|
||||
}
|
||||
|
||||
// Zoom the output texture.
|
||||
if w.Zoom != 0 {
|
||||
// dst.X = w.ZoomMultiply(dst.X - origin.X)
|
||||
// dst.Y = w.ZoomMultiply(dst.Y - origin.Y)
|
||||
// dst.W = w.ZoomMultiply(dst.W)
|
||||
// dst.H = w.ZoomMultiply(dst.H)
|
||||
}
|
||||
|
||||
render.TrimBox(&src, &dst, p, S, w.BoxThickness(1))
|
||||
e.Copy(wp.top, src, dst)
|
||||
}
|
||||
|
@ -186,6 +270,15 @@ func (w *Canvas) PresentWallpaper(e render.Engine, p render.Point) error {
|
|||
W: src.W,
|
||||
H: src.H,
|
||||
}
|
||||
|
||||
// Zoom the output texture.
|
||||
if w.Zoom != 0 {
|
||||
// dst.X = w.ZoomMultiply(dst.X - origin.X)
|
||||
// dst.Y = w.ZoomMultiply(dst.Y - origin.Y)
|
||||
// dst.W = w.ZoomMultiply(dst.W)
|
||||
// dst.H = w.ZoomMultiply(dst.H)
|
||||
}
|
||||
|
||||
render.TrimBox(&src, &dst, p, S, w.BoxThickness(1))
|
||||
e.Copy(wp.corner, src, dst)
|
||||
}
|
||||
|
|
97
pkg/uix/canvas_zoom.go
Normal file
97
pkg/uix/canvas_zoom.go
Normal file
|
@ -0,0 +1,97 @@
|
|||
package uix
|
||||
|
||||
import (
|
||||
"git.kirsle.net/apps/doodle/pkg/drawtool"
|
||||
"git.kirsle.net/go/render"
|
||||
)
|
||||
|
||||
// Functions related to the Zoom Tool to magnify the size of the canvas.
|
||||
|
||||
/*
|
||||
GetZoomMultiplier parses the .Zoom integer and returns a multiplier.
|
||||
|
||||
Examples:
|
||||
|
||||
Zoom = 0: neutral (100% scale, 1x)
|
||||
Zoom = 1: 2x zoom
|
||||
Zoom = 2: 4x zoom
|
||||
Zoom = 3: 8x zoom
|
||||
Zoom = -1: 0.5x zoom
|
||||
Zoom = -2: 0.25x zoom
|
||||
*/
|
||||
func (w *Canvas) GetZoomMultiplier() float64 {
|
||||
// Get and bounds cap the zoom setting.
|
||||
if w.Zoom < -2 {
|
||||
w.Zoom = -2
|
||||
} else if w.Zoom > 3 {
|
||||
w.Zoom = 3
|
||||
}
|
||||
|
||||
// Return the multipliers.
|
||||
switch w.Zoom {
|
||||
case -2:
|
||||
return 0.25
|
||||
case -1:
|
||||
return 0.5
|
||||
case 0:
|
||||
return 1
|
||||
case 1:
|
||||
return 1.5
|
||||
case 2:
|
||||
return 2
|
||||
case 3:
|
||||
return 2.5
|
||||
default:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ZoomMultiply multiplies a width or height value by the Zoom Multiplier and
|
||||
returns the modified integer.
|
||||
|
||||
Usage is like:
|
||||
|
||||
// when building a render.Rect destination box.
|
||||
dest.W *= ZoomMultiply(dest.W)
|
||||
dest.H *= ZoomMultiply(dest.H)
|
||||
*/
|
||||
func (w *Canvas) ZoomMultiply(value int) int {
|
||||
return int(float64(value) * w.GetZoomMultiplier())
|
||||
}
|
||||
|
||||
/*
|
||||
ZoomStroke adjusts a drawn stroke on the canvas to account for the zoom level.
|
||||
|
||||
Returns a copy Stroke value without changing the original.
|
||||
*/
|
||||
func (w *Canvas) ZoomStroke(stroke *drawtool.Stroke) drawtool.Stroke {
|
||||
copy := drawtool.Stroke{
|
||||
ID: stroke.ID,
|
||||
Shape: stroke.Shape,
|
||||
Color: stroke.Color,
|
||||
Thickness: stroke.Thickness,
|
||||
ExtraData: stroke.ExtraData,
|
||||
PointA: stroke.PointA,
|
||||
PointB: stroke.PointB,
|
||||
Points: stroke.Points,
|
||||
OriginalPoints: stroke.OriginalPoints,
|
||||
}
|
||||
return copy
|
||||
|
||||
// Multiply all coordinates in this stroke, which should be World
|
||||
// Coordinates in the level data, by the zoom multiplier.
|
||||
adjust := func(p render.Point) render.Point {
|
||||
p.X = w.ZoomMultiply(p.X)
|
||||
p.Y = w.ZoomMultiply(p.Y)
|
||||
return p
|
||||
}
|
||||
|
||||
copy.PointA = adjust(copy.PointA)
|
||||
copy.PointB = adjust(copy.PointB)
|
||||
for i := range copy.Points {
|
||||
copy.Points[i] = adjust(copy.Points[i])
|
||||
}
|
||||
|
||||
return copy
|
||||
}
|
Loading…
Reference in New Issue
Block a user