;; camera.fnl
;; A simple editor camera library
;; AlexJGriffith 2025

(local keybindings {:left [:left :a] :right [:right :d] :down [:down :s] :up [:up :w]})

(fn check-keybinding [direction]
  (if (love.keyboard.isDown (unpack (. keybindings direction))) 1 0))

(fn init [opts keybindings?]
  (local cam {:x 0 :y 0 :scale 3 :speed  7
              :v {:cx 0 :cy 0 :sx 0 :sy 0 :active false}})
  (each [key value (ipairs opts)] (tset cam key value))
  (when keybindings?
    (each [key value (ipairs keybindings?)] (tset keybindings key value)))
  cam)

(fn transform [cam]
  (love.graphics.scale cam.scale)
  (love.graphics.translate cam.x cam.y))

(fn transform-without-scale [cam]
  (love.graphics.translate (* cam.scale cam.x) (* cam.scale cam.y)))

(fn to-camera-space [cam x y]
  (values (- (/ x cam.scale) cam.x)
          (- (/ y cam.scale) cam.y)))

(fn to-global-space [cam x y]
  (values (* (+ x cam.x) cam.scale)
          (* (+ y cam.y) cam.scale)))

(local editor
       {
        :update
        (fn  [cam dt]
          (let [l (check-keybinding :left)
                r (check-keybinding :right)
                u (check-keybinding :up)
                d (check-keybinding :down)
                lr (- l r)
                ud (- u d)
                mag (if (and (= 0 lr) (= 0 ud)) 1 (math.sqrt (+ (^ lr 2) (^ ud 2))))
                lrn (/ lr mag)
                udn (/ ud mag)]
            (set cam.x (+ cam.x (-> lrn (* cam.speed) (/ cam.scale))))
            (set cam.y (+ cam.y (-> udn (* cam.speed) (/ cam.scale))))))
        :mousepressed
        (fn [cam x y button]
          (when (= button 3)
            (set cam.v.sx x)
            (set cam.v.sy y)
            (set cam.v.cx cam.x)
            (set cam.v.cy cam.y)
            (set cam.v.active true)))
        :mousemoved
        (fn [cam x y]
          (when cam.v.active
            (set cam.x (- cam.v.cx (-> (- cam.v.sx x) (/ cam.scale))))
            (set cam.y (- cam.v.cy (-> (- cam.v.sy y) (/ cam.scale))))))
        :mousereleased
        (fn [cam x y button] (set cam.v.active false))
        :wheelmoved
        (fn [cam x y]
          (when (not cam.v.active)
            (let [p cam.scale
                  n (-> cam.scale (* (+ 1 (* 0.1 y))) (math.max 1) (math.min 8))
                  (mx my) (love.mouse.getPosition)
                  ox  (* mx  (/ (- n p) (* p n)))
                  oy  (* my  (/ (- n p) (* p n)))]
              (set cam.scale n)
              (set cam.x (- cam.x ox))
              (set cam.y (- cam.y oy)))))
        })


{: editor : init : transform : transform-without-scale : to-camera-space : to-global-space}

Generated by alexjgriffith using scpaste at Tue Aug 26 17:59:37 2025. EDT. (original)