Zooming and panning using matrices
This commit is contained in:
parent
638950be49
commit
043c09da50
128
src/zua.nim
128
src/zua.nim
|
@ -1,16 +1,16 @@
|
|||
# SPDX-FileCopyrightText: ☭ 2021 Emery Hemingway
|
||||
# SPDX-License-Identifier: Unlicense
|
||||
|
||||
import std/[asyncdispatch, hashes, os, strutils, tables, xmlparser, xmltree]
|
||||
import preserves, preserves/parse
|
||||
import std/[asyncdispatch, hashes, os, tables]
|
||||
import preserves
|
||||
import syndicate,
|
||||
syndicate/[actors, capabilities, dataspaces, patterns, relay],
|
||||
syndicate/protocols/[simpleChatProtocol]
|
||||
|
||||
import nimsvg
|
||||
import pixie, pixie/fileformats/svg
|
||||
import bumpy, pixie, pixie/fileformats/svg
|
||||
|
||||
import math, sdl2, sdl2/gfx
|
||||
import sdl2
|
||||
|
||||
import svui
|
||||
|
||||
|
@ -39,23 +39,20 @@ type
|
|||
Pane = ref object
|
||||
svg: string
|
||||
texture: TexturePtr
|
||||
rect: bumpy.Rect
|
||||
|
||||
type
|
||||
App = ref object
|
||||
screen: Image
|
||||
# ctx: Context
|
||||
window: WindowPtr
|
||||
renderer: RendererPtr
|
||||
# surface: SurfacePtr
|
||||
# texture: TexturePtr
|
||||
panes: Table[Hash, Pane]
|
||||
viewPort: sdl2.Rect
|
||||
viewPoint: Vec2
|
||||
zoomFactor: float
|
||||
|
||||
proc newPane(app: App; svg: string): Pane =
|
||||
result = Pane(svg: svg)
|
||||
let (w, h) = app.window.getSize
|
||||
result = Pane(svg: svg, rect: rect(0, 0, float w, float h))
|
||||
try:
|
||||
let (w, h) = app.window.getSize
|
||||
var
|
||||
image = decodeSvg(svg, w, h)
|
||||
dataPtr = image.data[0].addr
|
||||
|
@ -76,61 +73,70 @@ proc newPane(app: App; svg: string): Pane =
|
|||
destroyTexture(result.texture)
|
||||
stderr.writeLine "Failed to render SVG, ", e.msg
|
||||
|
||||
proc newApp(width, height: int): App =
|
||||
proc newApp(length: cint): App =
|
||||
## Create a new square plane of `length` pixels.
|
||||
var wh = vec2(float length, float length)
|
||||
result = App(zoomFactor: 1.0)
|
||||
discard createWindowAndRenderer(
|
||||
cint width, cint height,
|
||||
SDL_WINDOW_RESIZABLE,
|
||||
length, length,
|
||||
SDL_WINDOW_SHOWN,
|
||||
# SDL_WINDOW_RESIZABLE,
|
||||
result.window, result.renderer)
|
||||
var info: RendererInfo
|
||||
check getRendererInfo(result.renderer, addr info)
|
||||
echo "SDL Renderer: ", info.name
|
||||
|
||||
func toSdl(rect: bumpy.Rect): sdl2.Rect =
|
||||
(result.x, result.y, result.w, result.h) =
|
||||
(cint rect.x, cint rect.y, cint rect.w, cint rect.h)
|
||||
|
||||
proc viewPort(app: App; wh: Vec2): bumpy.Rect =
|
||||
result.wh = wh / app.zoomFactor
|
||||
result.xy = app.viewPoint - (result.wh * 0.5)
|
||||
|
||||
proc redraw(app: App) =
|
||||
var srcRect, dstRect: sdl2.Rect
|
||||
(srcRect.w, srcRect.h) = app.window.getSize
|
||||
dstRect.w = cint(srcRect.w.float * app.zoomFactor)
|
||||
dstRect.h = cint(srcRect.h.float * app.zoomFactor)
|
||||
app.renderer.setDrawColor(128, 128, 128)
|
||||
assert app.zoomFactor != 0.0
|
||||
var
|
||||
(w, h) = app.window.getSize
|
||||
sdlViewPort = rect(-float(w shr 1), -float(h shr 1), float w, float h)
|
||||
viewPort = app.viewPort(sdlViewPort.wh)
|
||||
app.renderer.setDrawColor(0xff, 0xff, 0xff)
|
||||
app.renderer.clear()
|
||||
app.renderer.setViewport(addr app.viewPort)
|
||||
for pane in app.panes.values:
|
||||
app.renderer.copy(pane.texture, nil, addr dstRect)
|
||||
if overlaps(viewPort, pane.rect):
|
||||
var
|
||||
overlap = viewPort and pane.rect
|
||||
src = rect(overlap.xy - pane.rect.xy, overlap.wh)
|
||||
dst: bumpy.Rect
|
||||
dst.xy = (overlap.xy - viewPort.xy) * (sdlViewPort.w / viewPort.w)
|
||||
dst.wh =
|
||||
if app.zoomFactor == 1.0:
|
||||
overlap.wh # correct
|
||||
elif app.zoomFactor > 1.0:
|
||||
sdlViewPort.wh - dst.xy # correct?
|
||||
else:
|
||||
overlap.wh * app.zoomFactor
|
||||
var (sdlSrc, sdlDst) = (src.toSdl, dst.toSdl)
|
||||
app.renderer.copy(pane.texture, addr sdlSrc, addr sdlDst)
|
||||
app.renderer.present()
|
||||
|
||||
proc update(app: App) =
|
||||
let (w, h) = app.window.getSize
|
||||
if app.screen.isNil or app.screen.width != w or app.screen.height != h:
|
||||
# app.screen = newImage(w, h)
|
||||
check app.renderer.setLogicalSize(w, h)
|
||||
app.viewPort = app.renderer.getViewport
|
||||
|
||||
#[
|
||||
app.screen.fill(rgba(255, 255, 255, 255))
|
||||
|
||||
var dataPtr = app.screen.data[0].addr
|
||||
var mainSurface = createRGBSurfaceFrom(
|
||||
dataPtr, cint app.screen.width, cint app.screen.height, cint 32, cint 4*w,
|
||||
rmask, gmask, bmask, amask)
|
||||
destroy app.texture
|
||||
app.texture = app.renderer.createTextureFromSurface(mainSurface)
|
||||
]#
|
||||
app.redraw()
|
||||
|
||||
proc zoom(app: App; change: float) =
|
||||
# TODO: how does renderer scaling work?
|
||||
app.zoomFactor = app.zoomFactor * (1.0 - (0.1 * change))
|
||||
app.zoomFactor = app.zoomFactor * (1.0 + ((1 / 8) * change))
|
||||
app.redraw()
|
||||
|
||||
proc pan(app: App; x, y: int) =
|
||||
app.viewPort.x.inc x
|
||||
app.viewPort.y.inc y
|
||||
proc pan(app: App; xy: Vec2) =
|
||||
app.viewPoint.xy = app.viewPoint.xy + (xy / app.zoomFactor)
|
||||
app.redraw()
|
||||
|
||||
proc recenter(app: App) =
|
||||
reset app.viewPoint
|
||||
app.zoomFactor = 1.0
|
||||
app.redraw()
|
||||
|
||||
proc main() =
|
||||
discard sdl2.init(INIT_TIMER or INIT_VIDEO or INIT_EVENTS)
|
||||
let app = newApp(1024, 768)
|
||||
app.update()
|
||||
let app = newApp(512)
|
||||
app.redraw()
|
||||
|
||||
let cap = mint()
|
||||
|
||||
|
@ -142,7 +148,7 @@ proc main() =
|
|||
onPublish(turn, ds, Svui ? {0: drop(), 1: grab()}) do (svg: string):
|
||||
onRetract:
|
||||
app.panes.del(hash svg)
|
||||
app.update()
|
||||
app.redraw()
|
||||
# TODO: keep dead panes around until they are cleaned up.
|
||||
# If something crashes then the last state should be visable.
|
||||
var pane = app.panes.getOrDefault(hash svg)
|
||||
|
@ -151,11 +157,7 @@ proc main() =
|
|||
app.panes[hash svg] = pane
|
||||
else:
|
||||
pane.svg = svg
|
||||
app.update() # TODO: wait til end of turn
|
||||
|
||||
var
|
||||
svgHandle: Handle
|
||||
groups: Table[Hash, Nodes]
|
||||
app.redraw() # TODO: wait til end of turn
|
||||
|
||||
const
|
||||
sdlTimeout = 500
|
||||
|
@ -172,15 +174,11 @@ proc main() =
|
|||
app.zoom(evt.wheel.y.float)
|
||||
of WindowEvent:
|
||||
if evt.window.event == WindowEvent_Resized:
|
||||
app.update()
|
||||
app.redraw()
|
||||
of MouseMotion:
|
||||
if mousePanning:
|
||||
if (getModState() and (KMOD_LCTRL or KMOD_RCTRL)) == KMOD_NONE:
|
||||
app.pan(-2 * evt.motion.xrel, -2 * evt.motion.yrel)
|
||||
# TODO: panning accelation curve
|
||||
# ../../../genode/repos/os/src/server/event_filter/accelerate_source.h
|
||||
else:
|
||||
app.pan(evt.motion.xrel, evt.motion.yrel)
|
||||
var xy = vec2(evt.motion.xrel.float, evt.motion.yrel.float)
|
||||
app.pan(xy * 2.0)
|
||||
of MouseButtonDown:
|
||||
case evt.button.button
|
||||
of BUTTON_MIDDLE:
|
||||
|
@ -191,7 +189,15 @@ proc main() =
|
|||
of BUTTON_MIDDLE:
|
||||
mousePanning = false
|
||||
else: discard
|
||||
of KeyUp: discard
|
||||
of KeyUp:
|
||||
try:
|
||||
let code = evt.key.keysym.scancode.Scancode
|
||||
echo "code: ", code
|
||||
if code in {SDL_SCANCODE_SPACE, SDL_SCANCODE_ESCAPE}:
|
||||
app.recenter()
|
||||
except:
|
||||
# invalid event.key.keysym.sym sometimes arrive
|
||||
discard
|
||||
of KeyDown: discard
|
||||
of QuitEvent: quit(0)
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue