doodle/pkg/uix/canvas_zoom.go
Noah Petherbridge 37f6177a17 Zoom In/Out Feature: WorldIndexAt Fixed
WorldIndexAt() translates the pixel below the mouse cursor in screen
space (0,0 at top-left corner of the application window) into a world
coordinate in the level shown inside the canvas, taking into account the
canvas's position on the window and the scroll position.

It now translates correctly when zoom In or Out, so the "Abs:" mouse
position level in the status bar shows correctly.

Zoom features that are still jank:

- Scrolling while zoomed in, the chunks to the top/left start unloading
  too rapidly and outpacing the scroll, eventually level is invisible
- Drawing and committing pixels to the image while zoomed in/out is
  unpredictable where the pixels actually land.
- Actors in the level don't move or zoom at all.
2021-07-13 18:04:25 -07:00

138 lines
2.7 KiB
Go

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())
}
/*
ZoomDivide divides an integer by the zoom inversely.
The algo is: int(float64(value) * divider)
Where the divider is a map of:
w.Zoom => divider
-2 => 4
-1 => 2
0 => 1
1 => 0.675*
2 => 0.5
3 => 0.404*
The 0.675 and 0.404 numbers I don't understand but were
discovered the hard way when the 1.5x and 2.5x zoom levels
were coming out jank. Expected to be 0.25 and 0.75.
*/
func (w *Canvas) ZoomDivide(value int) int {
var divider float64
switch w.Zoom {
case -2:
divider = 4
case -1:
divider = 2
case 0:
divider = 1
case 1:
divider = 0.675 // JANK
case 2:
divider = 0.5 // GOOD, 2x (200%) zoom in
case 3:
divider = 0.404 // JANK
default:
divider = 1
}
return int(float64(value) * divider)
}
/*
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.ZoomDivide(p.X)
p.Y = w.ZoomDivide(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
}