0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-15 17:21:17 -05:00

Refactor shape resize and displacement.

This commit is contained in:
Andrey Antukh 2016-12-20 17:05:42 +01:00
parent 23dbc77da9
commit af57d8b449
No known key found for this signature in database
GPG key ID: 4DFEBCB8316A8B95
14 changed files with 577 additions and 252 deletions

View file

@ -8,17 +8,18 @@
(:require [beicon.core :as rx]
[uxbox.util.uuid :as uuid]
[potok.core :as ptk]
[uxbox.util.router :as r]
[uxbox.store :as st]
[uxbox.util.forms :as sc]
[uxbox.util.geom.point :as gpt]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.router :as r]
[uxbox.util.rlocks :as rlocks]
[uxbox.util.workers :as uw]
[uxbox.main.constants :as c]
[uxbox.main.geom :as geom]
[uxbox.store :as st]
[uxbox.main.data.core :refer (worker)]
[uxbox.main.data.shapes-impl :as impl]
[uxbox.main.data.pages :as udp]
[uxbox.util.rlocks :as rlocks]
[uxbox.util.geom.point :as gpt]))
[uxbox.main.data.pages :as udp]))
;; --- Shapes CRUD
@ -70,18 +71,21 @@
(gpt/point c/canvas-start-x
c/canvas-start-y))
(declare apply-temporal-displacement)
(defn initial-align-shape
[id]
(reify
ptk/WatchEvent
(watch [_ state s]
(let [shape (get-in state [:shapes id])
shape (geom/outer-rect state shape)
point (gpt/point (:x shape) (:y shape))
point (gpt/add point canvas-coords)]
(->> (align-point point)
(rx/map #(gpt/subtract % point))
(rx/map #(move-shape id %)))))))
(let [{:keys [x1 y1] :as shape} (->> (get-in state [:shapes id])
(geom/shape->rect-shape state))
point1 (gpt/point x1 y1)
point2 (gpt/add point1 canvas-coords)]
(->> (align-point point2)
(rx/map #(gpt/subtract % canvas-coords))
(rx/map (fn [{:keys [x y] :as pt}]
(apply-temporal-displacement id (gpt/subtract pt point1)))))))))
(defn update-line-attrs
[sid {:keys [x1 y1 x2 y2] :as opts}]
@ -119,6 +123,92 @@
(update [_ state]
(update-in state [:shapes sid] geom/resize-dim opts))))
;; --- Apply Temporal Displacement
(deftype ApplyTemporalDisplacement [id delta]
ptk/UpdateEvent
(update [_ state]
(let [current-delta (get-in state [:shapes id :tmp-displacement] (gpt/point 0 0))
delta (gpt/add current-delta delta)]
(assoc-in state [:shapes id :tmp-displacement] delta))))
(defn apply-temporal-displacement
[id pt]
{:pre [(uuid? id) (gpt/point? pt)]}
(ApplyTemporalDisplacement. id pt))
;; --- Apply Displacement
(deftype ApplyDisplacement [id]
ptk/UpdateEvent
(update [_ state]
(let [{:keys [tmp-displacement type] :as shape} (get-in state [:shapes id])
xfmt (gmt/translate-matrix tmp-displacement)]
(if (= type :group)
(letfn [(update-item [state id]
(let [{:keys [type items] :as shape} (get-in state [:shapes id])]
(if (= type :group)
(reduce update-item state items)
(update-in state [:shapes id]
(fn [shape]
(as-> (dissoc shape :tmp-displacement) $
(geom/transform state $ xfmt)))))))]
(-> (reduce update-item state (:items shape))
(update-in [:shapes id] dissoc :tmp-displacement)))
(update-in state [:shapes id] (fn [shape]
(as-> (dissoc shape :tmp-displacement) $
(geom/transform state $ xfmt))))))))
(defn apply-displacement
[id]
{:pre [(uuid? id)]}
(ApplyDisplacement. id))
;; --- Apply Temporal Resize Matrix
(deftype ApplyTemporalResizeMatrix [id mx]
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:shapes id :tmp-resize-xform] mx)))
(defn apply-temporal-resize-matrix
"Attach temporal resize matrix transformation to the shape."
[id mx]
(ApplyTemporalResizeMatrix. id mx))
;; --- Apply Resize Matrix
(declare apply-resize-matrix)
(deftype ApplyResizeMatrix [id]
ptk/UpdateEvent
(update [_ state]
(let [{:keys [type tmp-resize-xform]
:or {tmp-resize-xform (gmt/matrix)}
:as shape} (get-in state [:shapes id])]
(if (= type :group)
(letfn [(update-item [state id]
(let [{:keys [type items] :as shape} (get-in state [:shapes id])]
(if (= type :group)
(reduce update-item state items)
(update-in state [:shapes id]
(fn [shape]
(as-> (dissoc shape :tmp-resize-xform) $
(geom/transform state $ tmp-resize-xform)))))))]
(-> (reduce update-item state (:items shape))
(update-in [:shapes id] dissoc :tmp-resize-xform)))
(update-in state [:shapes id] (fn [shape]
(as-> (dissoc shape :tmp-resize-xform) $
(geom/transform state $ tmp-resize-xform))))))))
(defn apply-resize-matrix
"Apply definitivelly the resize matrix transformation to the shape."
[id]
{:pre [(uuid? id)]}
(ApplyResizeMatrix. id))
(defn update-vertex-position
[id {:keys [vid delta]}]
(reify
@ -208,7 +298,6 @@
(when rx {:rx rx})
(when ry {:ry ry})))))
;; --- Shape Proportions
(defn lock-proportions
@ -378,17 +467,20 @@
id (first (get-in state [:pages page :shapes]))]
(assoc-in state [:workspace :selected] #{id})))))
(deftype SelectShape [id]
ptk/UpdateEvent
(update [_ state]
(let [selected (get-in state [:workspace :selected])
state (if (contains? selected id)
(update-in state [:workspace :selected] disj id)
(update-in state [:workspace :selected] conj id))]
(update-in state [:workspace :flags] conj :element-options))))
(defn select-shape
"Mark a shape selected for drawing in the canvas."
[id]
(reify
ptk/UpdateEvent
(update [_ state]
(let [selected (get-in state [:workspace :selected])
state (if (contains? selected id)
(update-in state [:workspace :selected] disj id)
(update-in state [:workspace :selected] conj id))]
(update-in state [:workspace :flags] conj :element-options)))))
(SelectShape. id))
;; --- Select Shapes

View file

@ -555,6 +555,7 @@
(case type
:rect (transform-rect shape xfmt)
:icon (transform-rect shape xfmt)
:text (transform-rect shape xfmt)
:image (transform-rect shape xfmt)
:path (transform-path shape xfmt)
:circle (transform-circle shape xfmt))))

View file

@ -5,50 +5,39 @@
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.shapes.circle
(:require [sablono.core :refer-macros [html]]
[rum.core :as rum]
[lentes.core :as l]
[uxbox.util.mixins :as mx :include-macros true]
(:require [lentes.core :as l]
[uxbox.main.ui.shapes.common :as common]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.geom :as geom]))
[uxbox.main.geom :as geom]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.util.mixins :as mx :include-macros true]))
;; --- Circle Component
(declare circle-shape)
(defn- circle-component-render
[own shape]
(mx/defc circle-component
{:mixins [mx/reactive mx/static]}
[shape]
(let [{:keys [id x y width height group]} shape
selected (mx/react common/selected-ref)
selected? (contains? selected id)
on-mouse-down #(common/on-mouse-down % shape selected)]
(html
[:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down}
(circle-shape shape identity)])))
(def circle-component
(mx/component
{:render circle-component-render
:name "circle-component"
:mixins [mx/static mx/reactive]}))
[:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down}
(circle-shape shape identity)]))
;; --- Circle Shape
(defn- circle-shape-render
[own {:keys [id] :as shape}]
(let [key (str "shape-" id)
rfm (geom/transformation-matrix shape)
props (select-keys shape [:cx :cy :rx :ry])
attrs (-> (attrs/extract-style-attrs shape)
(merge {:id key :key key :transform (str rfm)})
(merge props))]
(html
[:ellipse attrs])))
(mx/defc circle-shape
{:mixins [mx/static]}
[{:keys [id tmp-resize-xform tmp-displacement] :as shape}]
(let [xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
tmp-displacement (gmt/translate tmp-displacement))
(def circle-shape
(mx/component
{:render circle-shape-render
:name "circle-shape"
:mixins [mx/static]}))
props {:transform (str xfmt) :id (str id)}
attrs (merge props
(attrs/extract-style-attrs shape)
(select-keys shape [:cx :cy :rx :ry]))]
[:ellipse attrs]))

View file

@ -10,11 +10,12 @@
[beicon.core :as rx]
[potok.core :as ptk]
[uxbox.store :as st]
[uxbox.main.geom :as geom]
[uxbox.main.data.shapes :as uds]
[uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.workspace.base :as wb]
[uxbox.util.geom.point :as gpt]
[uxbox.util.rlocks :as rlocks]
[uxbox.main.geom :as geom]
[uxbox.util.dom :as dom]))
;; --- Refs
@ -36,13 +37,19 @@
(defn start-move
[]
(letfn [(on-start [shape]
(letfn [(on-move [shape delta]
(st/emit! (uds/apply-temporal-displacement shape delta)))
(on-stop [{:keys [id] :as shape}]
(rlocks/release! :shape/move)
(st/emit! (uds/apply-displacement shape)))
(on-start [shape]
(let [stoper (->> (rx/map first wb/events-s)
(rx/filter #(= % :mouse/up))
(rx/take 1))
stream (rx/take-until stoper wb/mouse-delta-s)
on-move #(st/emit! (uds/move-shape shape %))
on-stop #(rlocks/release! :shape/move)]
stream (->> wb/mouse-delta-s
(rx/take-until stoper))
on-move (partial on-move shape)
on-stop (partial on-stop shape)]
(when @wb/alignment-ref
(st/emit! (uds/initial-align-shape shape)))
(rx/subscribe stream on-move nil on-stop)))]

View file

@ -7,7 +7,7 @@
(ns uxbox.main.ui.shapes.group
(:require [lentes.core :as l]
[uxbox.store :as st]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.geom :as geom]
[uxbox.main.ui.shapes.common :as common]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.ui.shapes.icon :as icon]
@ -16,7 +16,9 @@
[uxbox.main.ui.shapes.text :as text]
[uxbox.main.ui.shapes.path :as path]
[uxbox.main.ui.shapes.image :as image]
[uxbox.main.geom :as geom]))
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.mixins :as mx :include-macros true]))
;; --- Helpers
@ -64,12 +66,14 @@
(mx/defc group-shape
{:mixins [mx/static mx/reactive]}
[{:keys [items id dx dy rotation] :as shape} factory]
(let [key (str "shape-" id)
rfm (geom/transformation-matrix shape)
attrs (merge {:id key :key key :transform (str rfm)}
(attrs/extract-style-attrs shape)
(attrs/make-debug-attrs shape))]
[{:keys [id items tmp-resize-xform tmp-displacement] :as shape} factory]
(let [xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
tmp-displacement (gmt/translate tmp-displacement))
props {:id (str id)
:transform (str xfmt)}
attrs (merge props (attrs/extract-style-attrs shape))]
[:g attrs
(for [item (reverse items)
:let [key (str item)]]

View file

@ -5,10 +5,11 @@
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.shapes.icon
(:require [uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.ui.shapes.common :as common]
(:require [uxbox.main.ui.shapes.common :as common]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.geom :as geom]))
[uxbox.main.geom :as geom]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.util.geom.matrix :as gmt]))
;; --- Icon Component
@ -28,19 +29,30 @@
(mx/defc icon-shape
{:mixins [mx/static]}
[{:keys [x1 y1 content id metadata] :as shape} factory]
(let [key (str "shape-" id)
;; rfm (geom/transformation-matrix shape)
[shape]
(let [{:keys [x1 y1 content id metadata
width height
tmp-resize-xform
tmp-displacement]} (geom/size shape)
[_ _ orw orh] (:view-box metadata)
scalex (/ width orw)
scaley (/ height orh)
view-box (apply str (interpose " " (:view-box metadata)))
size (geom/size shape)
attrs (merge {:id key :key key ;; :transform (str rfm)
:x x1 :y y1 :view-box view-box
:preserve-aspect-ratio "none"
:dangerouslySetInnerHTML {:__html content}}
size
(attrs/extract-style-attrs shape)
(attrs/make-debug-attrs shape))]
[:svg attrs]))
xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
tmp-displacement (gmt/translate tmp-displacement)
true (gmt/translate x1 y1)
true (gmt/scale scalex scaley))
props {:id (str id)
:preserve-aspect-ratio "none"
:dangerouslySetInnerHTML {:__html content}
:transform (str xfmt)}
attrs (merge props (attrs/extract-style-attrs shape))]
[:g attrs]))
;; --- Icon SVG
@ -48,7 +60,7 @@
{:mixins [mx/static]}
[{:keys [content id metadata] :as shape}]
(let [view-box (apply str (interpose " " (:view-box metadata)))
id (str "icon-svg-" id)
props {:view-box view-box :id id
props {:view-box view-box
:id (str id)
:dangerouslySetInnerHTML {:__html content}}]
[:svg props]))

View file

@ -7,14 +7,14 @@
(ns uxbox.main.ui.shapes.image
(:require [beicon.core :as rx]
[lentes.core :as l]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.util.http :as http]
[potok.core :as ptk]
[uxbox.store :as st]
[uxbox.main.ui.shapes.common :as common]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.data.images :as udi]
[uxbox.main.geom :as geom]))
[uxbox.main.geom :as geom]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.util.geom.matrix :as gmt]))
;; --- Refs
@ -51,13 +51,22 @@
(mx/defc image-shape
{:mixins [mx/static]}
[{:keys [id x1 y1 image] :as shape}]
(let [key (str "shape-" id)
;; rfm (geom/transformation-matrix shape)
size (geom/size shape)
props {:x x1 :y y1 :id key :key key
[shape]
(let [{:keys [id x1 y1 image
width height
tmp-resize-xform
tmp-displacement]} (geom/size shape)
xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
tmp-displacement (gmt/translate tmp-displacement))
props {:x x1 :y y1
:id (str id)
:preserve-aspect-ratio "none"
:xlink-href (:url image)}
attrs (-> (attrs/extract-style-attrs shape)
(merge props size))]
:xlink-href (:url image)
:transform (str xfmt)
:width width
:height height}
attrs (merge props (attrs/extract-style-attrs shape))]
[:image attrs]))

View file

@ -5,13 +5,14 @@
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.shapes.path
(:require [uxbox.util.mixins :as mx :include-macros true]
[potok.core :as ptk]
(:require [potok.core :as ptk]
[uxbox.store :as st]
[uxbox.main.ui.shapes.common :as common]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.data.shapes :as uds]
[uxbox.main.geom :as geom]))
[uxbox.main.geom :as geom]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.mixins :as mx :include-macros true]))
;; --- Path Component
@ -45,11 +46,11 @@
(mx/defc path-shape
{:mixins [mx/static]}
[{:keys [id drawing?] :as shape}]
(let [key (str "shape-" id)
rfm (geom/transformation-matrix shape)
attrs (-> (attrs/extract-style-attrs shape)
(merge {:id key :key key :d (render-path shape)})
(merge (when-not drawing?
#_{:transform (str rfm)})))]
[{:keys [id tmp-resize-xform tmp-displacement] :as shape}]
(let [xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
tmp-displacement (gmt/translate tmp-displacement))
props {:transform (str xfmt)
:id (str id)
:d (render-path shape)}
attrs (merge props (attrs/extract-style-attrs shape))]
[:path attrs]))

View file

@ -7,8 +7,10 @@
(ns uxbox.main.ui.shapes.rect
(:require [uxbox.main.ui.shapes.common :as common]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.geom :as geom]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.util.dom :as dom]))
;; --- Rect Component
@ -23,18 +25,26 @@
selected? (contains? selected id)
on-mouse-down #(common/on-mouse-down % shape selected)]
[:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down}
:on-mouse-down on-mouse-down
}
(rect-shape shape identity)]))
;; --- Rect Shape
(mx/defc rect-shape
{:mixins [mx/static]}
[{:keys [id x1 y1] :as shape}]
(let [key (str "shape-" id)
rfm (geom/transformation-matrix shape)
size (geom/size shape)
props {:x x1 :y y1 :id key :key key :transform (str rfm)}
attrs (-> (attrs/extract-style-attrs shape)
(merge props size))]
[shape]
(let [{:keys [id x1 y1 width height
tmp-resize-xform
tmp-displacement]} (geom/size shape)
xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
tmp-displacement (gmt/translate tmp-displacement))
props {:x x1 :y y1 :id id
:width width
:height height
:transform (str xfmt)}
attrs (merge (attrs/extract-style-attrs shape) props)]
[:rect attrs]))

View file

@ -9,14 +9,16 @@
"Multiple selection handlers component."
(:require [lentes.core :as l]
[beicon.core :as rx]
[uxbox.store :as st]
[uxbox.util.mixins :as mx :include-macros true]
[potok.core :as ptk]
[uxbox.store :as st]
[uxbox.main.constants :as c]
[uxbox.main.data.shapes :as uds]
[uxbox.main.ui.workspace.base :as wb]
[uxbox.util.rlocks :as rlocks]
[uxbox.main.ui.shapes.common :as scommon]
[uxbox.main.geom :as geom]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.util.rlocks :as rlocks]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.geom.point :as gpt]
[uxbox.util.dom :as dom]))
@ -41,115 +43,257 @@
(def edition-ref scommon/edition-ref)
(defn transform-rect
[{:keys [x1 y1 x2 y2] :as shape} xfmt]
(if xfmt
(let [tl (gpt/transform [x1 y1] xfmt)
tr (gpt/transform [x2 y1] xfmt)
bl (gpt/transform [x1 y2] xfmt)
br (gpt/transform [x2 y2] xfmt)
minx (apply min (map :x [tl tr bl br]))
maxx (apply max (map :x [tl tr bl br]))
miny (apply min (map :y [tl tr bl br]))
maxy (apply max (map :y [tl tr bl br]))]
(assoc shape
:x1 minx
:y1 miny
:x2 maxx
:y2 maxy))
shape))
;; --- Resize Implementation
(defn- start-resize
[vid sid]
(letfn [(on-resize [[delta ctrl?]]
(let [params {:vid vid :delta (assoc delta :lock ctrl?)}]
(st/emit! (uds/update-vertex-position sid params))))
[vid ids shape]
(letfn [(gen-matrix [shape {scalex :x scaley :y}]
(case vid
:top-left
(-> (gmt/matrix)
(gmt/translate (+ (:x2 shape))
(+ (:y2 shape)))
(gmt/scale scalex scaley)
(gmt/translate (- (:x2 shape))
(- (:y2 shape))))
:top-right
(-> (gmt/matrix)
(gmt/translate (+ (:x1 shape))
(+ (:y2 shape)))
(gmt/scale scalex scaley)
(gmt/translate (- (:x1 shape))
(- (:y2 shape))))
:top
(-> (gmt/matrix)
(gmt/translate (+ (:x1 shape))
(+ (:y2 shape)))
(gmt/scale scalex scaley)
(gmt/translate (- (:x1 shape))
(- (:y2 shape))))
:bottom-left
(-> (gmt/matrix)
(gmt/translate (+ (:x2 shape))
(+ (:y1 shape)))
(gmt/scale scalex scaley)
(gmt/translate (- (:x2 shape))
(- (:y1 shape))))
:bottom-right
(-> (gmt/matrix)
(gmt/translate (+ (:x1 shape))
(+ (:y1 shape)))
(gmt/scale scalex scaley)
(gmt/translate (- (:x1 shape))
(- (:y1 shape))))
:bottom
(-> (gmt/matrix)
(gmt/translate (+ (:x1 shape))
(+ (:y1 shape)))
(gmt/scale scalex scaley)
(gmt/translate (- (:x1 shape))
(- (:y1 shape))))
:right
(-> (gmt/matrix)
(gmt/translate (+ (:x1 shape))
(+ (:y1 shape)))
(gmt/scale scalex scaley)
(gmt/translate (- (:x1 shape))
(- (:y1 shape))))
:left
(-> (gmt/matrix)
(gmt/translate (+ (:x2 shape))
(+ (:y1 shape)))
(gmt/scale scalex scaley)
(gmt/translate (- (:x2 shape))
(- (:y1 shape))))
))
(on-resize [shape scale]
(let [mt (gen-matrix shape scale)
xf (map #(uds/apply-temporal-resize-matrix % mt))]
(apply st/emit! (sequence xf ids))))
(on-end []
(rlocks/release! :shape/resize))]
(apply st/emit! (map uds/apply-resize-matrix ids))
(rlocks/release! :shape/resize))
(calculate-ratio [orig-shape {:keys [width height] :as shape}]
{:x (/ width (:width orig-shape))
:y (/ height (:height orig-shape))})
(apply-delta [shape [{:keys [x y] :as point} ctrl?]]
(case vid
:top-left
(let [width (- (:x2 shape) x)
height (- (:y2 shape) y)
proportion (:proportion shape)]
(assoc shape
:width width
:height (if ctrl? (/ width proportion) height)))
:top-right
(let [width (- x (:x1 shape))
height (- (:y2 shape) y)
proportion (:proportion shape)]
(assoc shape
:width width
:height (if ctrl? (/ width proportion) height)))
:top
(let [width (- (:x2 shape) (:x1 shape))
height (- (:y2 shape) y)
proportion (:proportion shape)]
(assoc shape
:width width
:height (if ctrl? (/ width proportion) height)))
:bottom-left
(let [width (- (:x2 shape) x)
height (- y (:y1 shape))
proportion (:proportion shape)]
(assoc shape
:width width
:height (if ctrl? (/ width proportion) height)))
:bottom-right
(let [width (- x (:x1 shape))
height (- y (:y1 shape))
proportion (:proportion shape)]
(assoc shape
:width width
:height (if ctrl? (/ width proportion) height)))
:bottom
(let [width (- (:x2 shape) (:x1 shape))
height (- y (:y1 shape))
proportion (:proportion shape)]
(assoc shape
:width width
:height (if ctrl? (/ width proportion) height)))
:left
(let [width (- (:x2 shape) x)
height (- (:y2 shape) (:y1 shape))
proportion (:proportion shape)]
(assoc shape
:width width
:height (if ctrl? (/ width proportion) height)))
:right
(let [width (- x (:x1 shape))
height (- (:y2 shape) (:y1 shape))
proportion (:proportion shape)]
(assoc shape
:width width
:height (if ctrl? (/ width proportion) height)))
))]
(let [stoper (->> wb/events-s
(rx/map first)
(rx/filter #(= % :mouse/up))
(rx/take 1))
stream (->> wb/mouse-delta-s
stream (->> wb/mouse-canvas-s
(rx/map #(gpt/divide % @wb/zoom-ref))
(rx/mapcat (fn [point]
(if @wb/alignment-ref
(uds/align-point point)
(rx/of point))))
(rx/take-until stoper)
(rx/with-latest-from vector wb/mouse-ctrl-s))]
(rx/with-latest-from vector wb/mouse-ctrl-s)
(rx/scan apply-delta shape)
(rx/map (partial calculate-ratio shape)))]
(rlocks/acquire! :shape/resize)
(when @wb/alignment-ref
(st/emit! (uds/initial-vertext-align sid vid)))
(rx/subscribe stream on-resize nil on-end))))
(rx/subscribe stream
(partial on-resize shape)
nil
on-end))))
;; --- Controls (Component)
(mx/defc controls
{:mixins [mx/static]}
[{:keys [width height x1 y1]} zoom on-mouse-down]
[:g.controls
[:rect.main {:x x1 :y y1
:width width
:height height
:stroke-dasharray (str (/ 5.0 zoom) "," (/ 5 zoom))
:style {:stroke "#333" :fill "transparent"
:stroke-opacity "1"}}]
[:circle.top
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :top %)
:r (/ 6.0 zoom)
:cx (+ x1 (/ width 2))
:cy (- y1 2)})]
[:circle.right
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :right %)
:r (/ 6.0 zoom)
:cy (+ y1 (/ height 2))
:cx (+ x1 width 1)})]
[:circle.bottom
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :bottom %)
:r (/ 6.0 zoom)
:cx (+ x1 (/ width 2))
:cy (+ y1 height 2)})]
[:circle.left
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :left %)
:r (/ 6.0 zoom)
:cy (+ y1 (/ height 2))
:cx (- x1 3)})]
[:circle.top-left
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :top-left %)
:r (/ 6.0 zoom)
:cx x1
:cy y1})]
[:circle.top-right
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :top-right %)
:r (/ 6.0 zoom)
:cx (+ x1 width)
:cy y1})]
[:circle.bottom-left
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :bottom-left %)
:r (/ 6.0 zoom)
:cx x1
:cy (+ y1 height)})]
[:circle.bottom-right
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :bottom-right %)
:r (/ 6.0 zoom)
:cx (+ x1 width)
:cy (+ y1 height)})]])
;; --- Selection Handlers (Component)
(mx/defc multiple-selection-handlers
[shapes]
(let [{:keys [width height x y]} (geom/outer-rect-coll shapes)]
[:g.controls
[:rect.main {:x x :y y
:width width
:height height
:stroke-dasharray "5,5"
:style {:stroke "#333" :fill "transparent"
:stroke-opacity "1"}}]]))
(mx/defc single-not-editable-selection-handlers
[{:keys [id] :as shape} zoom]
(let [{:keys [width height x y]} (geom/outer-rect shape)]
[:g.controls
[:rect.main {:x x :y y
:width width
:height height
:stroke-dasharray (str (/ 5.0 zoom) "," (/ 5.0 zoom))
:style {:stroke "#333"
:fill "transparent"
:stroke-opacity "1"}}]]))
(mx/defc single-selection-handlers
[{:keys [id] :as shape} zoom]
(letfn [(on-mouse-down [vid event]
(dom/stop-propagation event)
(start-resize vid id))]
(let [{:keys [x y width height]} (geom/outer-rect shape)]
[:g.controls
[:rect.main {:x x :y y
:width width
:height height
:stroke-dasharray (str (/ 5.0 zoom) "," (/ 5.0 zoom))
:style {:stroke "#333"
:fill "transparent"
:stroke-opacity "1"}}]
[:circle.top
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :top %)
:r (/ 6.0 zoom)
:cx (+ x (/ width 2))
:cy (- y 2)})]
[:circle.right
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :right %)
:r (/ 6.0 zoom)
:cy (+ y (/ height 2))
:cx (+ x width 1)})]
[:circle.bottom
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :bottom %)
:r (/ 6.0 zoom)
:cx (+ x (/ width 2))
:cy (+ y height 2)})]
[:circle.left
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :left %)
:r (/ 6.0 zoom)
:cy (+ y (/ height 2))
:cx (- x 3)})]
[:circle.top-left
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :top-left %)
:r (/ 6.0 zoom)
:cx x
:cy y})]
[:circle.top-right
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :top-right %)
:r (/ 6.0 zoom)
:cx (+ x width)
:cy y})]
[:circle.bottom-left
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :bottom-left %)
:r (/ 6.0 zoom)
:cx x
:cy (+ y height)})]
[:circle.bottom-right
(merge +circle-props+
{:on-mouse-down #(on-mouse-down :bottom-right %)
:r (/ 6.0 zoom)
:cx (+ x width)
:cy (+ y height)})]])))
(defn start-path-edition
[shape-id index]
(letfn [(on-move [delta]
@ -181,6 +325,43 @@
:stroke "#28c4d4"
:style {:cursor "pointer"}}])]))
(mx/defc multiple-selection-handlers
{:mixins [mx/static]}
[[shape & rest :as shapes] zoom]
(let [resize-xf (:tmp-resize-xform shape (gmt/matrix))
displc-xf (-> (:tmp-displacement shape (gpt/point 0 0))
(gmt/translate-matrix))
selection (-> (geom/shapes->rect-shape shapes)
(assoc :type :rect)
(geom/transform resize-xf)
(geom/transform displc-xf)
(geom/size))
on-click #(do (dom/stop-propagation %2)
(start-resize %1 (map :id shapes) selection))]
;; (println "single-selection-handlers" displc-xf)
;; (println "single-selection-handlers" (select-keys selection [:x1 :y1 :x2 :y2]))
;; (println "single-selection-handlers" (select-keys selection [:x1 :y1 :width :height]))
(controls selection zoom on-click)))
(mx/defc single-selection-handlers
{:mixins [mx/static]}
[{:keys [id] :as shape} zoom]
(let [resize-xf (:tmp-resize-xform shape (gmt/matrix))
displc-xf (-> (:tmp-displacement shape (gpt/point 0 0))
(gmt/translate-matrix))
selection (-> (geom/shape->rect-shape shape)
;; (transform-rect resize-xf)
(assoc :type :rect)
(geom/transform resize-xf)
(geom/transform displc-xf)
(geom/size))
on-click #(do (dom/stop-propagation %2)
(start-resize %1 #{id} selection))]
;; (println "single-selection-handlers" displc-xf)
;; (println "single-selection-handlers" (select-keys selection [:x1 :y1 :x2 :y2]))
;; (println "single-selection-handlers" (select-keys selection [:x1 :y1 :width :height]))
(controls selection zoom on-click)))
(mx/defc selection-handlers
{:mixins [mx/reactive mx/static]}
[]
@ -196,20 +377,10 @@
(> shapes-num 1)
(multiple-selection-handlers shapes zoom)
(= :path (:type shape))
(if (= @edition-ref (:id shape))
(path-edition-selection-handlers shape zoom)
(single-selection-handlers shape zoom))
:else
(cond
(= :path (:type shape))
(if (= @edition-ref (:id shape))
(path-edition-selection-handlers shape zoom)
(single-not-editable-selection-handlers shape zoom))
(= :text (:type shape))
(if (= @edition-ref (:id shape))
(single-not-editable-selection-handlers shape zoom)
(single-selection-handlers (first shapes) zoom))
(= :group (:type shape))
(single-not-editable-selection-handlers shape zoom)
:else
(single-selection-handlers (first shapes) zoom)))))
(single-selection-handlers shape zoom))))

View file

@ -9,15 +9,16 @@
[lentes.core :as l]
[goog.events :as events]
[potok.core :as ptk]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.util.color :as color]
[uxbox.util.dom :as dom]
[uxbox.store :as st]
[uxbox.main.geom :as geom]
[uxbox.main.data.shapes :as uds]
[uxbox.main.ui.shapes.common :as common]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.util.color :as color]
[uxbox.util.dom :as dom]
[uxbox.util.geom.matrix :as gmt]
[uxbox.util.rlocks :as rlocks]
[uxbox.main.geom :as geom])
[uxbox.util.mixins :as mx :include-macros true])
(:import goog.events.EventType))
;; --- Events
@ -106,8 +107,8 @@
[{:keys [id x1 y1 content] :as shape}]
(let [size (geom/size shape)
style (make-style shape)
rfm (geom/transformation-matrix shape)
props {:x x1 :y y1 :transform (str rfm)}
;; rfm (geom/transformation-matrix shape)
props {:x x1 :y y1} ;; :transform (str rfm)}
props (merge props size)]
(letfn [#_(on-blur [ev]
(rlocks/release! :ui/text-edit)
@ -126,15 +127,38 @@
;; --- Text Shape
;; NOTE: this is a hack for the browser rendering.
;;
;; Without forcing rerender, when the shape is displaced
;; and only x and y attrs are updated in dom, the whole content
;; of the foreignObject becomes sometimes partially or
;; completelly invisible. The complete dom rerender fixes that
;; problem.
(defn- text-shape-did-update
[own]
(let [pref (mx/ref-node own "fo")
html (.-innerHTML pref)]
(set! (.-innerHTML pref) html)
own))
(mx/defc text-shape
{:mixins [mx/static]}
[{:keys [id x1 y1 content] :as shape}]
(let [key (str "shape-" id)
rfm (geom/transformation-matrix shape)
size (geom/size shape)
props {:x x1 :y y1
:transform (str rfm)}
attrs (merge props size)
style (make-style shape)]
[:foreignObject attrs
[:p {:style style} content]]))
{:mixins [mx/static]
:did-update text-shape-did-update}
[{:keys [tmp-resize-xform] :as shape}]
(let [shape (cond-> (geom/size shape)
tmp-resize-xform (geom/transform shape tmp-resize-xform))
{:keys [id x1 y1 content
width height
tmp-displacement]} (geom/size shape)
xfmt (cond-> (gmt/matrix)
tmp-displacement (gmt/translate tmp-displacement))
style (make-style shape)
props {:x x1 :y y1 :id (str id) :ref "fo"
:width width :height height
:transform (str xfmt)}]
[:foreignObject props
[:p {:ref "p" :style style} content]]))

View file

@ -65,5 +65,10 @@
(defn encode
[data]
(let [w (t/writer :json {:handlers +write-handlers+})]
(t/write w data)))
(try
(let [w (t/writer :json {:handlers +write-handlers+})]
(t/write w data))
(catch :default e
(println "data:" data)
(throw e))))

View file

@ -116,7 +116,7 @@
[{:keys [x1 y1 rotation] :as shape}
{:keys [resize-width resize-height easing
element delay duration direction] :as opts}]
(if (= direction :reverse)
#_(if (= direction :reverse)
(let [end (geom/transformation-matrix shape)]
(animate :targets [(str "#shape-" element)]
:transform (str end)
@ -165,7 +165,7 @@
(defn- run-rotate-interaction
[{:keys [element rotation direction easing delay duration] :as opts}]
(let [shape (get-in @st/state [:shapes element])
#_(let [shape (get-in @st/state [:shapes element])
dom (dom/get-element (str "shape-" element))
mtx1 (geom/transformation-matrix (update shape :rotation + rotation))
mtx2 (geom/transformation-matrix shape)]

View file

@ -52,15 +52,15 @@
{:pre [(map? shape)]}
(let [show-itx? (and (mx/react itx-flag-ref)
(not (empty? (:interactions shape))))
rect (geom/inner-rect shape)]
rect (geom/shape->rect-shape shape)]
[:g {:id (str "itx-" (:id shape))
:style (when show-itx?
{:cursor "pointer"})}
(factory shape)
(when show-itx?
[:circle {:fill "#78dbbe"
:cx (:x rect)
:cy (:y rect)
:cx (:x1 rect)
:cy (:y1 rect)
:r 5}])]))
;; --- Shapes