changeset 54:1d2dfe5026a8

Support transformations.
author Mikhail Kryshen <mikhail@kryshen.net>
date Thu, 19 Aug 2010 20:20:21 +0400
parents 042fae9cb24e
children 6adbc03a52cb
files src/net/kryshen/indyvon/component.clj src/net/kryshen/indyvon/core.clj src/net/kryshen/indyvon/demo.clj
diffstat 3 files changed, 61 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/src/net/kryshen/indyvon/component.clj	Sat Aug 14 19:03:10 2010 +0400
+++ b/src/net/kryshen/indyvon/component.clj	Thu Aug 19 20:20:21 2010 +0400
@@ -11,6 +11,7 @@
   (:import
    (net.kryshen.indyvon.core Size Bounds)
    (java.awt Graphics2D Component Dimension Color)
+   (java.awt.geom Rectangle2D$Double)
    (javax.swing JFrame JPanel)))
 
 (defn- font-context [^Component component]
@@ -25,11 +26,14 @@
     (let [bounds (Bounds. 0 0 width height)]
       (binding [*graphics* graphics
                 *font-context* (.getFontRenderContext graphics)
+                *initial-transform* (.getTransform graphics)
+                *inverse-initial-transform*
+                  (-> graphics .getTransform .createInverse)
                 *target* component
                 *event-dispatcher* event-dispatcher
                 *update* #(.repaint component)
                 *bounds* bounds
-                *clip* bounds]
+                *clip* (Rectangle2D$Double. 0 0 width height)]
         (render! layer)
         (commit event-dispatcher)))))
 
--- a/src/net/kryshen/indyvon/core.clj	Sat Aug 14 19:03:10 2010 +0400
+++ b/src/net/kryshen/indyvon/core.clj	Thu Aug 19 20:20:21 2010 +0400
@@ -6,8 +6,8 @@
 
 (ns net.kryshen.indyvon.core
   (:import
-   (java.awt Graphics2D Component Color Font AWTEvent)
-   (java.awt.geom AffineTransform)
+   (java.awt Graphics2D Component Color Font AWTEvent Shape)
+   (java.awt.geom AffineTransform Point2D$Double Rectangle2D$Double Area)
    (java.awt.event MouseListener MouseMotionListener)
    (java.awt.font FontRenderContext)))
 
@@ -15,10 +15,13 @@
 (def ^FontRenderContext *font-context*)
 (def ^Component *target*)
 (def *bounds*)
-(def *clip*)
+(def ^Shape *clip*)
 (def *update*)
 (def *event-dispatcher*)
 
+(def ^AffineTransform *initial-transform*)
+(def ^AffineTransform *inverse-initial-transform*)
+
 (defrecord Theme [fore-color back-color alt-back-color border-color font])
 
 (defn default-theme []
@@ -97,6 +100,35 @@
            y2 (min y12 y22)]
        (Bounds. x1 y1 (- x2 x1) (- y2 y1)))))
 
+(defn- relative-transform
+  "AffineTransform: layer -> absolute -> component."
+  []
+  (let [tr (.getTransform *graphics*)]
+    (.preConcatenate tr *inverse-initial-transform*)
+    tr))
+
+(defn- inverse-relative-transform
+  "AffineTransform: component (event) -> absolute -> layer."
+  []
+  (let [tr (.getTransform *graphics*)]
+    (.invert tr)                          ; absolute -> layer
+    (.concatenate tr *initial-transform*) ; component -> absolute
+    tr))
+
+(defn clip
+  "Intersect clipping area with the specified shape or bounds.
+   Returns new clip (Shape or nil if empty)."
+  ([x y w h]
+     (clip (Rectangle2D$Double. x y w h)))
+  ([shape]
+     (let [a1 (Area. shape)
+           a2 (if (instance? Area *clip*) *clip* (Area. *clip*))]
+       (.transform a1 (relative-transform))
+       (.intersect a1 a2)
+       (if (.isEmpty a1)
+         nil
+         a1))))
+
 (defn ^Graphics2D create-graphics
   ([]
      (create-graphics 0 0 (:width *bounds*) (:height *bounds*)))
@@ -105,10 +137,9 @@
 
 (defmacro with-bounds [x y w h & body]
   `(let [bounds# (Bounds. (+ ~x (:x *bounds*))
-                          (+ ~y (:y *bounds*))
-                          ~w ~h)
-         clip# (intersect bounds# *clip*)]
-     (when (and (pos? (:width clip#)) (pos? (:height clip#)))
+                          (+ ~y (:y *bounds*)) ~w ~h)
+         clip# (clip ~x ~y ~w ~h)]
+     (when clip#
        (let [graphics# (create-graphics ~x ~y ~w ~h)]
          (try
            (binding [*bounds* bounds#
@@ -215,7 +246,9 @@
       java.awt.event.MouseEvent/MOUSE_PRESSED  :mouse-pressed
       java.awt.event.MouseEvent/MOUSE_RELEASED :mouse-released})
 
-(defrecord DispatcherNode [handle handlers parent bounds bindings]
+(defrecord DispatcherNode [handle handlers parent
+                           ^Shape clip ^AffineTransform transform
+                           bindings]
   EventDispatcher
   (listen! [this component]
      (listen! parent component))
@@ -226,6 +259,7 @@
 
 (defn- make-node [handle handlers]
   (DispatcherNode. handle handlers *event-dispatcher* *clip*
+                   (inverse-relative-transform)
                    (get-thread-bindings)))
 
 (defn- assoc-cons [m key val]
@@ -234,31 +268,27 @@
 (defn- add-node [tree node]
   (assoc-cons tree (:parent node) node))
 
-(defn- inside?
-  ([x y bounds]
-     (inside? x y (:x bounds) (:y bounds)
-              (:width bounds) (:height bounds)))
-  ([px py x y w h]
-     (and (>= px x)
-          (>= py y)
-          (< px (+ x w))
-          (< py (+ y h)))))
-
 (defn- under-cursor
   "Returns a vector of child nodes under cursor."
   [x y tree node]
-  (some #(if (inside? x y (:bounds %))
+  (some #(if (.contains (:clip %) x y)
            (conj (vec (under-cursor x y tree %)) %))
         (get tree node)))
 
 (defn- remove-all [coll1 coll2 pred]
   (filter #(not (some (partial pred %) coll2)) coll1))
 
-(defn- translate-mouse-event [^java.awt.event.MouseEvent event x y id]
-  (MouseEvent. id (.getWhen event)
-               (- (.getX event) x) (- (.getY event) y)
-               (.getXOnScreen event) (.getYOnScreen event)
-               (.getButton event)))
+(defn- transform [^AffineTransform tr x y]
+  (let [p (Point2D$Double. x y)]
+    (.transform tr p p)
+    [(.x p) (.y p)]))
+
+(defn- translate-mouse-event [^java.awt.event.MouseEvent event
+                              ^AffineTransform tr id]
+  (let [[x y] (transform tr (.getX event) (.getY event))]
+    (MouseEvent. id (.getWhen event) x y
+                 (.getXOnScreen event) (.getYOnScreen event)
+                 (.getButton event))))
 
 (defn- translate-and-dispatch
   ([nodes first-only ^java.awt.event.MouseEvent event]
@@ -270,8 +300,7 @@
          (do
            (with-bindings* (:bindings node)
              handler
-             (translate-mouse-event event
-               (-> node :bounds :x) (-> node :bounds :y) id))
+             (translate-mouse-event event (:transform node) id))
            (if-not first-only
              (recur (rest nodes) false event id)))
          (recur (rest nodes) first-only event id)))))
--- a/src/net/kryshen/indyvon/demo.clj	Sat Aug 14 19:03:10 2010 +0400
+++ b/src/net/kryshen/indyvon/demo.clj	Thu Aug 19 20:20:21 2010 +0400
@@ -71,7 +71,7 @@
      (reify
       Layer
       (render! [layer]
-         (*update*)
+         ;;(*update*)
          (doto *graphics*
            (.setColor (rand-nth [Color/BLACK Color/BLUE Color/RED]))
            (.drawLine 0 0 (:width *bounds*) (:height *bounds*)))