changeset 139:173616375eb5

Refactoring. Moved viewport functions into separate namespace.
author Mikhail Kryshen <mikhail@kryshen.net>
date Mon, 07 Jan 2013 19:52:23 +0400
parents e3eeb1478df1
children 4dd98ea3b475
files src/net/kryshen/indyvon/demo.clj src/net/kryshen/indyvon/layers.clj src/net/kryshen/indyvon/viewport.clj
diffstat 3 files changed, 270 insertions(+), 222 deletions(-) [+]
line diff
     1.1 --- a/src/net/kryshen/indyvon/demo.clj	Mon Jan 07 19:51:21 2013 +0400
     1.2 +++ b/src/net/kryshen/indyvon/demo.clj	Mon Jan 07 19:52:23 2013 +0400
     1.3 @@ -1,5 +1,5 @@
     1.4  ;;
     1.5 -;; Copyright 2010, 2011 Mikhail Kryshen <mikhail@kryshen.net>
     1.6 +;; Copyright 2010, 2011, 2012 Mikhail Kryshen <mikhail@kryshen.net>
     1.7  ;;
     1.8  ;; This file is part of Indyvon.
     1.9  ;;
    1.10 @@ -21,7 +21,7 @@
    1.11    "Indyvon demo and experiments."
    1.12    (:gen-class)
    1.13    (:use
    1.14 -   (net.kryshen.indyvon core layers component))
    1.15 +   (net.kryshen.indyvon core layers viewport component))
    1.16    (:import
    1.17     (java.awt Color)
    1.18     (javax.swing JFrame)))
     2.1 --- a/src/net/kryshen/indyvon/layers.clj	Mon Jan 07 19:51:21 2013 +0400
     2.2 +++ b/src/net/kryshen/indyvon/layers.clj	Mon Jan 07 19:52:23 2013 +0400
     2.3 @@ -22,11 +22,10 @@
     2.4    (:use
     2.5     (net.kryshen.indyvon core async))
     2.6    (:import
     2.7 -   (java.lang.ref SoftReference)
     2.8 -   (java.awt Font Cursor Image Toolkit Point)
     2.9 +   (java.awt Font Image Toolkit)
    2.10     java.awt.image.ImageObserver
    2.11 -   (java.awt.geom AffineTransform Rectangle2D$Double Point2D$Double
    2.12 -                  Path2D Path2D$Double)
    2.13 +   (java.awt.geom Area AffineTransform Rectangle2D$Double Point2D
    2.14 +                  Point2D$Double)
    2.15     (java.awt.font FontRenderContext TextLayout)
    2.16     java.util.concurrent.TimeUnit
    2.17     (com.google.common.cache Cache CacheBuilder CacheLoader)))
    2.18 @@ -94,12 +93,10 @@
    2.19           (with-color :border-color
    2.20             (let [w (double *width*)
    2.21                   h (double *height*)
    2.22 -                 outer (Rectangle2D$Double. 0.0 0.0 w h)
    2.23 -                 inner (Rectangle2D$Double. t t (- w t t) (- h t t))]
    2.24 -             (.fill *graphics*
    2.25 -                    (doto (Path2D$Double. Path2D/WIND_EVEN_ODD)
    2.26 -                      (.append outer false)
    2.27 -                      (.append inner false)))))))))
    2.28 +                 outer (Area. (Rectangle2D$Double. 0.0 0.0 w h))
    2.29 +                 inner (Area. (Rectangle2D$Double. t t (- w t t) (- h t t)))]
    2.30 +             (.subtract outer inner)
    2.31 +             (.fill *graphics* outer)))))))
    2.32  
    2.33  ;; TODO: opacity and blur.
    2.34  (defn shadow
    2.35 @@ -261,196 +258,6 @@
    2.36  
    2.37  (def ^:dynamic *miniature-thread-priority* 2)
    2.38  
    2.39 -(defn- scaling
    2.40 -  [width height max-width max-height]
    2.41 -  (min (/ max-width width)
    2.42 -       (/ max-height height)))
    2.43 -
    2.44 -(defn miniature
    2.45 -  "Creates layer that asynchronously renders view of the content
    2.46 -  scaled to the specified size."
    2.47 -  [content mw mh]
    2.48 -  (async-layer
    2.49 -   (reify
    2.50 -    Layer
    2.51 -    (render! [this]
    2.52 -      (let [geom (geometry content)
    2.53 -            cw (width geom)
    2.54 -            ch (height geom)
    2.55 -            s (scaling cw ch mw mh)]
    2.56 -        (.scale *graphics* s s)
    2.57 -        (draw! content
    2.58 -               (align-x cw (/ mw s) :center)
    2.59 -               (align-y ch (/ mh s) :center)
    2.60 -               cw ch)))
    2.61 -    (geometry [_]
    2.62 -      (->Size mw mh)))
    2.63 -   mw mh *miniature-thread-priority*))
    2.64 -
    2.65 -;;(defn- translate [^AffineTransform transform ^double x ^double y]
    2.66 -;;  (doto ^AffineTransform (.clone transform)
    2.67 -;;        (.translate x y)))
    2.68 -
    2.69 -(defn- scale [^AffineTransform transform ^double sx ^double sy]
    2.70 -    (doto ^AffineTransform (.clone transform)
    2.71 -        (.scale sx sy)))
    2.72 -
    2.73 -(defn- pre-translate [^AffineTransform transform ^double x ^double y]
    2.74 -  (if (== 0.0 x y)
    2.75 -    transform
    2.76 -    (doto (AffineTransform/getTranslateInstance x y)
    2.77 -      (.concatenate transform))))
    2.78 -
    2.79 -(def ^:dynamic *viewport-scaling-step* (double 3/4))
    2.80 -(def ^:dynamic *viewport-min-scale* 1E-6)
    2.81 -(def ^:dynamic *viewport-max-scale* 1E6)
    2.82 -
    2.83 -(declare scale-viewport)
    2.84 -
    2.85 -(defrecord Viewport [content h-align v-align
    2.86 -                     ;; State (refs)
    2.87 -                     transform
    2.88 -                     fix-x fix-y
    2.89 -                     last-width last-height
    2.90 -                     last-anchor-x last-anchor-y]
    2.91 -  Layer
    2.92 -  (render! [layer]
    2.93 -    (repaint-on-update layer)
    2.94 -    (with-handlers layer
    2.95 -      (let [geom (geometry content)
    2.96 -            cw (width geom)
    2.97 -            ch (height geom)
    2.98 -            ax (anchor-x geom h-align cw)
    2.99 -            ay (anchor-y geom v-align ch)]
   2.100 -        (dosync
   2.101 -         (let [ax1 (align-x @last-width *width* h-align)
   2.102 -               ay1 (align-y @last-height *height* v-align)
   2.103 -               ax2 (- @last-anchor-x ax)
   2.104 -               ay2 (- @last-anchor-y ay)]
   2.105 -           (if-not (and (zero? ax1) (zero? ay1) (zero? ax2) (zero? ay2))
   2.106 -             (ref-set transform
   2.107 -                      (doto (AffineTransform/getTranslateInstance ax1 ay1)
   2.108 -                        (.concatenate @transform)
   2.109 -                        (.translate ax2 ay2)))))
   2.110 -         (ref-set last-width *width*)
   2.111 -         (ref-set last-height *height*)
   2.112 -         (ref-set last-anchor-x ax)
   2.113 -         (ref-set last-anchor-y ay))
   2.114 -        ;; TODO: notify observers when size changes.
   2.115 -        (with-transform @transform
   2.116 -          (draw! content 0 0 cw ch false)))
   2.117 -      (:mouse-pressed e
   2.118 -       (dosync
   2.119 -        (ref-set fix-x (:x-on-screen e))
   2.120 -        (ref-set fix-y (:y-on-screen e)))
   2.121 -       (set-cursor! (Cursor. Cursor/MOVE_CURSOR)))
   2.122 -      (:mouse-released e
   2.123 -       (set-cursor! (Cursor. Cursor/DEFAULT_CURSOR)))
   2.124 -      (:mouse-dragged e
   2.125 -       (dosync
   2.126 -        (alter transform pre-translate
   2.127 -               (- (:x-on-screen e) @fix-x)
   2.128 -               (- (:y-on-screen e) @fix-y))
   2.129 -        (ref-set fix-x (:x-on-screen e))
   2.130 -        (ref-set fix-y (:y-on-screen e)))
   2.131 -       (update layer))
   2.132 -      (:mouse-wheel e
   2.133 -       (scale-viewport
   2.134 -        layer
   2.135 -        (Math/pow *viewport-scaling-step* (:wheel-rotation e))
   2.136 -        true (:x e) (:y e)))))
   2.137 -  (geometry [_]
   2.138 -    (geometry content)))
   2.139 -
   2.140 -(defn viewport
   2.141 -  "Creates scrollable viewport layer."
   2.142 -  ([content]
   2.143 -     (viewport content :left :top))
   2.144 -  ([content h-align v-align]
   2.145 -     (Viewport. content h-align v-align
   2.146 -                (ref (AffineTransform.)) ; transform
   2.147 -                (ref 0) (ref 0)          ; fix-x fix-y
   2.148 -                (ref 0) (ref 0)          ; last-width last-height
   2.149 -                (ref 0) (ref 0))))       ; last-anchor-x last-anchor-y
   2.150 -
   2.151 -(defn scale-viewport
   2.152 -  ([viewport s]
   2.153 -     (scale-viewport viewport s true))
   2.154 -  ([viewport s relative?]
   2.155 -     (scale-viewport viewport s relative? nil nil))
   2.156 -  ([viewport s relative? x y]
   2.157 -     (dosync
   2.158 -      (let [^AffineTransform tr @(:transform viewport)
   2.159 -            sx (if relative? s (/ s (.getScaleX tr)))
   2.160 -            sy (if relative? s (/ s (.getScaleY tr)))
   2.161 -            x (or x (align-x 0 @(:last-width viewport) (:h-align viewport)))
   2.162 -            y (or y (align-y 0 @(:last-height viewport) (:v-align viewport)))
   2.163 -            x (- x (* x sx))
   2.164 -            y (- y (* y sy))
   2.165 -            scaled (doto (AffineTransform/getTranslateInstance x y)
   2.166 -                     (.scale sx sy)
   2.167 -                     (.concatenate tr))
   2.168 -            sx (.getScaleX scaled)
   2.169 -            sy (.getScaleY scaled)]
   2.170 -        (if (<= *viewport-min-scale*
   2.171 -                (min sx sy)
   2.172 -                (max sx sy)
   2.173 -                *viewport-max-scale*)
   2.174 -          (ref-set (:transform viewport) scaled))))
   2.175 -     (update viewport)))
   2.176 -
   2.177 -(defn reset-viewport [viewport]
   2.178 -  (dosync
   2.179 -   (ref-set (:last-width viewport) 0)
   2.180 -   (ref-set (:last-height viewport) 0)
   2.181 -   (ref-set (:last-anchor-x viewport) 0)
   2.182 -   (ref-set (:last-anchor-y viewport) 0)
   2.183 -   (ref-set (:transform viewport) (AffineTransform.)))
   2.184 -  (update viewport))
   2.185 -
   2.186 -(defn viewport-miniature
   2.187 -  "Creates miniature view of the viewport's contents."
   2.188 -  [viewport m-width m-height]
   2.189 -  (let [miniature (miniature (:content viewport) m-width m-height)]
   2.190 -    (decorate-layer miniature [l]
   2.191 -      (repaint-on-update viewport)
   2.192 -      (let [geom (geometry (:content viewport))
   2.193 -            s (scaling (width geom) (height geom) m-width m-height)
   2.194 -            [vp-tr w h] (dosync
   2.195 -                         [@(:transform viewport)
   2.196 -                          @(:last-width viewport)
   2.197 -                          @(:last-height viewport)])
   2.198 -            vp-inverse (.createInverse ^AffineTransform vp-tr)
   2.199 -            ox (align-x (width geom) (/ m-width s) :center)
   2.200 -            oy (align-y (height geom) (/ m-height s) :center)
   2.201 -            transform (doto (AffineTransform.)
   2.202 -                        (.scale s s)
   2.203 -                        (.translate ox oy)
   2.204 -                        (.concatenate vp-inverse))
   2.205 -            move-vp (fn [x y]
   2.206 -                      (dosync
   2.207 -                       (let [x (- (/ x s) ox)
   2.208 -                             y (- (/ y s) oy)
   2.209 -                             [x y] (transform-point @(:transform viewport)
   2.210 -                                                    x y)
   2.211 -                             x (- x (/ @(:last-width viewport) 2))
   2.212 -                             y (- y (/ @(:last-height viewport) 2))]
   2.213 -                         (alter (:transform viewport)
   2.214 -                                pre-translate (- x) (- y))))
   2.215 -                      (update viewport))]
   2.216 -        (with-color :alt-back-color
   2.217 -          (.fillRect *graphics* 0 0 *width* *height*))
   2.218 -        (with-transform transform
   2.219 -          (with-color :back-color
   2.220 -            (.fillRect *graphics* 0 0 w h)))
   2.221 -        (with-handlers l
   2.222 -          (draw! miniature)
   2.223 -          (:mouse-pressed e (move-vp (:x e) (:y e)))
   2.224 -          (:mouse-dragged e (move-vp (:x e) (:y e))))
   2.225 -        (with-transform transform
   2.226 -          (with-color :border-color
   2.227 -            (.drawRect *graphics* 0 0 w h)))))))
   2.228 -
   2.229  (defn ref-layer
   2.230    [layer-ref]
   2.231    (let [l (reify
   2.232 @@ -552,9 +359,9 @@
   2.233  (defn- overlay* [f & args]
   2.234    (var-set #'*above* (conj *above* (apply partial f args))))
   2.235  
   2.236 -(defn- ^Point to-graphics-coords
   2.237 +(defn- ^Point2D to-graphics-coords
   2.238    [^AffineTransform transform x y]
   2.239 -  (let [p (Point. x y)]
   2.240 +  (let [p (Point2D$Double. x y)]
   2.241      (.transform transform p p)
   2.242      (.transform (.createInverse (.getTransform *graphics*)) p p)
   2.243      p))
   2.244 @@ -562,10 +369,10 @@
   2.245  (defn- draw-relative!
   2.246    ([layer transform x y]
   2.247       (let [p (to-graphics-coords transform x y)]
   2.248 -       (draw! layer (.x p) (.y p))))
   2.249 +       (draw! layer (.getX p) (.getY p))))
   2.250    ([layer transform x y w h]
   2.251       (let [p (to-graphics-coords transform x y)]
   2.252 -       (draw! layer (.x p) (.y p) w h))))
   2.253 +       (draw! layer (.getX p) (.getY p) w h))))
   2.254  
   2.255  (defn- draw-relative-aligned!
   2.256    [layer transform h-align v-align x y]
   2.257 @@ -573,8 +380,8 @@
   2.258          w (width geom)
   2.259          h (height geom)
   2.260          p (to-graphics-coords transform x y)
   2.261 -        x (- (.x p) (anchor-x geom h-align w))
   2.262 -        y (- (.y p) (anchor-y geom v-align h))]
   2.263 +        x (- (.getX p) (anchor-x geom h-align w))
   2.264 +        y (- (.getY p) (anchor-y geom v-align h))]
   2.265      (draw! layer x y w h)))
   2.266  
   2.267  (defn overlay!
   2.268 @@ -591,22 +398,25 @@
   2.269              layer (.getTransform *graphics*)
   2.270              h-align v-align x y))
   2.271  
   2.272 -(defn overlayer
   2.273 -  [content]
   2.274 -  (decorate-layer content [_]
   2.275 -    (binding [*above* []]
   2.276 -      (render! content)
   2.277 -      (doseq [f *above*]
   2.278 -        (f)))))
   2.279 -
   2.280 -(defn overlayer*
   2.281 -  [content]
   2.282 -  (decorate-layer content [_]
   2.283 -    (binding [*above* []]
   2.284 -      (render! content)
   2.285 +(defn with-overlays* [rec? f & args]
   2.286 +  (binding [*above* []]
   2.287 +    (apply f args)
   2.288 +    (if rec?
   2.289        (loop [above *above*]
   2.290          (when (seq above)
   2.291            (var-set #'*above* [])
   2.292            (doseq [f above]
   2.293              (f))
   2.294 -          (recur *above*))))))
   2.295 +          (recur *above*)))
   2.296 +      (doseq [of *above*]
   2.297 +        (of)))))
   2.298 +
   2.299 +(defmacro with-overlays [rec? & body]
   2.300 +  `(with-overlays* ~rec? (fn [] ~@body)))
   2.301 +
   2.302 +(defn overlayer
   2.303 +  ([content]
   2.304 +     (overlayer content true))
   2.305 +  ([content rec?]
   2.306 +     (decorate-layer content [_]
   2.307 +       (with-overlays* rec? render! content))))
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/src/net/kryshen/indyvon/viewport.clj	Mon Jan 07 19:52:23 2013 +0400
     3.3 @@ -0,0 +1,238 @@
     3.4 +;;
     3.5 +;; Copyright 2010, 2011, 2012 Mikhail Kryshen <mikhail@kryshen.net>
     3.6 +;;
     3.7 +;; This file is part of Indyvon.
     3.8 +;;
     3.9 +;; Indyvon is free software: you can redistribute it and/or modify it
    3.10 +;; under the terms of the GNU Lesser General Public License version 3
    3.11 +;; only, as published by the Free Software Foundation.
    3.12 +;;
    3.13 +;; Indyvon is distributed in the hope that it will be useful, but
    3.14 +;; WITHOUT ANY WARRANTY; without even the implied warranty of
    3.15 +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    3.16 +;; Lesser General Public License for more details.
    3.17 +;;
    3.18 +;; You should have received a copy of the GNU Lesser General Public
    3.19 +;; License along with Indyvon.  If not, see
    3.20 +;; <http://www.gnu.org/licenses/>.
    3.21 +;;
    3.22 +
    3.23 +(ns net.kryshen.indyvon.viewport
    3.24 +  "Scrollable viewport and miniature."
    3.25 +  (:use
    3.26 +   (net.kryshen.indyvon core async layers))
    3.27 +  (:import
    3.28 +   java.awt.Cursor
    3.29 +   java.awt.geom.AffineTransform))
    3.30 +  
    3.31 +;;(defn- translate [^AffineTransform transform ^double x ^double y]
    3.32 +;;  (doto ^AffineTransform (.clone transform)
    3.33 +;;        (.translate x y)))
    3.34 +
    3.35 +(defn- scale [^AffineTransform transform ^double sx ^double sy]
    3.36 +    (doto ^AffineTransform (.clone transform)
    3.37 +        (.scale sx sy)))
    3.38 +
    3.39 +(defn- pre-translate [^AffineTransform transform ^double x ^double y]
    3.40 +  (if (== 0.0 x y)
    3.41 +    transform
    3.42 +    (doto (AffineTransform/getTranslateInstance x y)
    3.43 +      (.concatenate transform))))
    3.44 +
    3.45 +(def ^:dynamic *viewport-scaling-step* (double 3/4))
    3.46 +(def ^:dynamic *viewport-min-scale* 1E-6)
    3.47 +(def ^:dynamic *viewport-max-scale* 1E6)
    3.48 +
    3.49 +(def ^:dynamic *viewport* nil)
    3.50 +(def ^:dynamic ^AffineTransform *viewport-transform*)
    3.51 +
    3.52 +(declare scale-viewport!)
    3.53 +
    3.54 +(defrecord ViewportState [transform
    3.55 +                          fix-x fix-y
    3.56 +                          last-width last-height
    3.57 +                          last-anchor-x last-anchor-y])
    3.58 +
    3.59 +(defn- update-viewport [state content-geom h-align v-align]
    3.60 +  (let [w *width*
    3.61 +        h *height*
    3.62 +        cw (width content-geom)
    3.63 +        ch (height content-geom)
    3.64 +        ax (anchor-x content-geom h-align cw)
    3.65 +        ay (anchor-y content-geom v-align ch)
    3.66 +        ax1 (align-x (:last-width state) w h-align)
    3.67 +        ay1 (align-y (:last-height state) h v-align)
    3.68 +        ax2 (- (:last-anchor-x state) ax)
    3.69 +        ay2 (- (:last-anchor-y state) ay)
    3.70 +        transform (:transform state)
    3.71 +        transform (if (and (zero? ax1) (zero? ay1)
    3.72 +                           (zero? ax2) (zero? ay2))
    3.73 +                    transform
    3.74 +                    (doto
    3.75 +                        (AffineTransform/getTranslateInstance ax1 ay1)
    3.76 +                      (.concatenate transform)
    3.77 +                      (.translate ax2 ay2)))]
    3.78 +    (assoc state
    3.79 +      :last-width w
    3.80 +      :last-height h
    3.81 +      :last-anchor-x ax
    3.82 +      :last-anchor-y ay
    3.83 +      :transform transform)))
    3.84 +
    3.85 +(defrecord Viewport [content h-align v-align state]
    3.86 +  Layer
    3.87 +  (render! [layer]
    3.88 +    (repaint-on-update layer)
    3.89 +    (with-handlers layer
    3.90 +      (let [geom (geometry content)
    3.91 +            new-state (swap! state update-viewport geom h-align v-align)
    3.92 +            transform (:transform new-state)]
    3.93 +        ;; TODO: notify observers when size changes.
    3.94 +        (binding [*viewport* layer
    3.95 +                  *viewport-transform* transform]
    3.96 +          (with-transform transform
    3.97 +            (draw! content 0 0 (width geom) (height geom) false))))
    3.98 +      (:mouse-pressed e
    3.99 +       (swap! state assoc
   3.100 +              :fix-x (:x-on-screen e)
   3.101 +              :fix-y (:y-on-screen e))
   3.102 +       (set-cursor! (Cursor. Cursor/MOVE_CURSOR)))
   3.103 +      (:mouse-released e
   3.104 +       (set-cursor! (Cursor. Cursor/DEFAULT_CURSOR)))
   3.105 +      (:mouse-dragged e
   3.106 +       (swap! state
   3.107 +              (fn [s]
   3.108 +                (assoc s
   3.109 +                  :transform (pre-translate
   3.110 +                              (:transform s)
   3.111 +                              (- (:x-on-screen e) (:fix-x s))
   3.112 +                              (- (:y-on-screen e) (:fix-y s)))
   3.113 +                  :fix-x (:x-on-screen e)
   3.114 +                  :fix-y (:y-on-screen e))))
   3.115 +       (update layer))
   3.116 +      (:mouse-wheel e
   3.117 +       (scale-viewport!
   3.118 +        layer
   3.119 +        (Math/pow *viewport-scaling-step* (:wheel-rotation e))
   3.120 +        true (:x e) (:y e)))))
   3.121 +  (geometry [_]
   3.122 +    (geometry content)))
   3.123 +
   3.124 +(def ^:private viewport-initial-state
   3.125 +  (->ViewportState
   3.126 +   (AffineTransform.) ; transform
   3.127 +   0 0                ; fix-x fix-y
   3.128 +   0 0                ; last-width last-height
   3.129 +   0 0))
   3.130 +
   3.131 +(defn viewport
   3.132 +  "Creates scrollable viewport layer."
   3.133 +  ([content]
   3.134 +     (viewport content :left :top))
   3.135 +  ([content h-align v-align]
   3.136 +     (->Viewport content h-align v-align (atom viewport-initial-state))))
   3.137 +
   3.138 +(defn- scale-viewport [state vp s relative? x y]
   3.139 +  (let [^AffineTransform tr (:transform state)
   3.140 +        sx (if relative? s (/ s (.getScaleX tr)))
   3.141 +        sy (if relative? s (/ s (.getScaleY tr)))
   3.142 +        x (or x (align-x 0 (:last-width state) (:h-align vp)))
   3.143 +        y (or y (align-y 0 (:last-height state) (:v-align vp)))
   3.144 +        x (- x (* x sx))
   3.145 +        y (- y (* y sy))
   3.146 +        scaled (doto (AffineTransform/getTranslateInstance x y)
   3.147 +                 (.scale sx sy)
   3.148 +                 (.concatenate tr))
   3.149 +        sx (.getScaleX scaled)
   3.150 +        sy (.getScaleY scaled)]
   3.151 +    (if (<= *viewport-min-scale*
   3.152 +            (min sx sy)
   3.153 +            (max sx sy)
   3.154 +            *viewport-max-scale*)
   3.155 +      (assoc state
   3.156 +        :transform scaled)
   3.157 +      state)))
   3.158 +
   3.159 +(defn scale-viewport!
   3.160 +  ([viewport s]
   3.161 +     (scale-viewport! viewport s true))
   3.162 +  ([viewport s relative?]
   3.163 +     (scale-viewport! viewport s relative? nil nil))
   3.164 +  ([viewport s relative? x y]
   3.165 +     (swap! (:state viewport) scale-viewport viewport s relative? x y)
   3.166 +     (update viewport)))
   3.167 +
   3.168 +(defn reset-viewport! [viewport]
   3.169 +  (reset! (:state viewport) viewport-initial-state)
   3.170 +  (update viewport))
   3.171 +
   3.172 +(defn ^AffineTransform viewport-transform [viewport]
   3.173 +  (:transform @(:state viewport)))
   3.174 +
   3.175 +(defn- scaling
   3.176 +  [width height max-width max-height]
   3.177 +  (min (/ max-width width)
   3.178 +       (/ max-height height)))
   3.179 +
   3.180 +(defn miniature
   3.181 +  "Creates layer that asynchronously renders view of the content
   3.182 +  scaled to the specified size."
   3.183 +  [content mw mh]
   3.184 +  (async-layer
   3.185 +   (reify
   3.186 +    Layer
   3.187 +    (render! [this]
   3.188 +      (let [geom (geometry content)
   3.189 +            cw (width geom)
   3.190 +            ch (height geom)
   3.191 +            s (scaling cw ch mw mh)]
   3.192 +        (.scale *graphics* s s)
   3.193 +        (draw! content
   3.194 +               (align-x cw (/ mw s) :center)
   3.195 +               (align-y ch (/ mh s) :center)
   3.196 +               cw ch)))
   3.197 +    (geometry [_]
   3.198 +      (->Size mw mh)))
   3.199 +   mw mh *miniature-thread-priority*))
   3.200 +
   3.201 +(defn viewport-miniature
   3.202 +  "Creates miniature view of the viewport's contents."
   3.203 +  [viewport m-width m-height]
   3.204 +  (let [miniature (miniature (:content viewport) m-width m-height)]
   3.205 +    (decorate-layer miniature [l]
   3.206 +      (repaint-on-update viewport)
   3.207 +      (let [geom (geometry (:content viewport))
   3.208 +            s (scaling (width geom) (height geom) m-width m-height)
   3.209 +            vp-state @(:state viewport)
   3.210 +            {:keys [transform last-width last-height]} @(:state viewport)
   3.211 +            ox (align-x (width geom) (/ m-width s) :center)
   3.212 +            oy (align-y (height geom) (/ m-height s) :center)
   3.213 +            inverse (.createInverse ^AffineTransform transform)
   3.214 +            transform (doto (AffineTransform.)
   3.215 +                        (.scale s s)
   3.216 +                        (.translate ox oy)
   3.217 +                        (.concatenate inverse))
   3.218 +            move-vp (fn [state x y]
   3.219 +                      (let [x (- (/ x s) ox)
   3.220 +                            y (- (/ y s) oy)
   3.221 +                            tr (:transform state)
   3.222 +                            [x y] (transform-point tr x y)
   3.223 +                            x (- x (/ (:last-width state) 2))
   3.224 +                            y (- y (/ (:last-height state) 2))]
   3.225 +                        (assoc state
   3.226 +                          :transform (pre-translate tr (- x) (- y)))))
   3.227 +            move-vp! (fn [x y]
   3.228 +                       (swap! (:state viewport) move-vp x y)
   3.229 +                       (update viewport))]
   3.230 +        (with-color :alt-back-color
   3.231 +          (.fillRect *graphics* 0 0 *width* *height*))
   3.232 +        (with-transform transform
   3.233 +          (with-color :back-color
   3.234 +            (.fillRect *graphics* 0 0 last-width last-height)))
   3.235 +        (with-handlers l
   3.236 +          (draw! miniature)
   3.237 +          (:mouse-pressed e (move-vp! (:x e) (:y e)))
   3.238 +          (:mouse-dragged e (move-vp! (:x e) (:y e))))
   3.239 +        (with-transform transform
   3.240 +          (with-color :border-color
   3.241 +            (.drawRect *graphics* 0 0 last-width last-height)))))))